SQLSERVER語句 in和exists哪一個效率高本人測試證實

SQLSERVR語句 in和exists哪一個效率高本人測試證實

最近不少人討論in和exists哪一個效率高,今天就本身測試一下html

我使用的是客戶的數據庫GPOSDB(已經有數據)web

環境:SQLSERVER2005   Windows7數據庫

個人測試條件:兩個表做鏈接根據VC_IC_CardNO字段,查出CT_InhouseCard表中的VC_IC_CardNO(卡號)在CT_FuelingData表中存在的記錄函數

前提:某些人可能在SQL語句中有多個in,或者多個exists,這些狀況很難測試效率的,由於你們的條件都不相同post

例以下面兩個SQL語句測試

1 SELECT  OrderNo, SiteCode, AreaCode
2 FROM    SchedulingProgram
3 WHERE   AreaCode IN ( 'P', 'M' ) AND SiteCode IN ( SELECT   SiteCode
4                                                    FROM     EnvBasicInfo
5                                                    WHERE    cityiD = 31 ) AND OrderNo NOT IN (
6         SELECT  OrderNo
7         FROM    KK_DeliveryinfoTmp )

上面SQL語句IN裏面有IN和NOT IN優化

1 SELECT  OrderNo, SiteCode, AreaCode
2 FROM    SchedulingProgram
3 WHERE   ( AreaCode IN ( 'P', 'M' ) AND SiteCode IN ( SELECT SiteCode
4                                                      FROM   EnvBasicInfo
5                                                      WHERE  cityiD = 31 )
6         ) AND NOT EXISTS ( SELECT   OrderNo
7                            FROM     KK_DeliveryinfoTmp
8                            WHERE    KK_DeliveryinfoTmp.OrderNo = SchedulingProgram.OrderNo )

上面的SQL語句IN裏面又有NOT EXISTSspa

這樣的狀況很難測試同等條件下IN語句和EXISTS語句的效率3d

還有一個非SARG運算符code

在《SQLSERVER企業級平臺管理實踐》的第424頁裏提到:

SQLSERVER對篩選條件(search argument/SARG)的寫法有必定的建議

對於不使用SARG運算符的表達式,索引是沒有用的,SQLSERVER對它們很難使用比較優化的作法。非SARG運算符包括

NOT、<>、NOT EXISTS、NOT IN、NOT LIKE和內部函數,例如:Convert、Upper等

因此當您的表中有索引而且SQL語句包含非SARG運算符,那麼當測試SQL語句的執行時間的時候確定相差很大,

由於有些SQL語句走索引,有些SQL語句不走索引


建表腳本

注意:兩個表中都有索引!!

CT_FuelingData表

 1 USE [GPOSDB]
 2 GO
 3 /****** 對象:  Table [dbo].[CT_FuelingData]    腳本日期: 08/24/2013 11:00:34 ******/
 4 SET ANSI_NULLS ON
 5 GO
 6 SET QUOTED_IDENTIFIER ON
 7 GO
 8 SET ANSI_PADDING ON
 9 GO
10 CREATE TABLE [dbo].[CT_FuelingData](
11     [RecordNO] [int] IDENTITY(1,1) NOT NULL,
12     [I_FD_StationNo] [int] NOT NULL,
13     [VC_FD_No] [varchar](50) NOT NULL,
14     [VC_FD_Cardno] [varchar](50) NOT NULL,
15     [I_FD_CardStatus] [int] NULL,
16     [LI_FD_CTC] [bigint] NOT NULL,
17     [I_FD_TypeCode] [int] NULL,
18     [I_FD_PumpID] [int] NOT NULL,
19     [VC_FD_OilType] [varchar](50) NULL,
20     [DE_FD_Volume] [decimal](18, 2) NULL,
21     [DE_FD_Price] [decimal](18, 2) NULL,
22     [DE_FD_Amount] [decimal](18, 2) NULL,
23     [I_FD_Point] [decimal](10, 2) NULL,
24     [D_FD_DateTime] [datetime] NOT NULL,
25     [VC_FD_GroupNo] [varchar](50) NULL,
26     [D_FD_GroupDate] [datetime] NULL,
27     [DE_FD_CardAmount] [decimal](18, 2) NULL,
28     [DE_FD_VolumeTotals] [decimal](18, 2) NULL,
29     [DE_FD_AmountTotals] [decimal](18, 2) NULL,
30     [I_FD_ISSend] [int] NULL,
31     [VC_FD_CardMoneyauthFile] [varchar](50) NULL,
32     [D_Month] [datetime] NULL,
33  CONSTRAINT [PK_CT_FuelingData_1] PRIMARY KEY CLUSTERED 
34 (
35     [VC_FD_No] ASC
36 )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
37 ) ON [PRIMARY]
38 
39 GO
40 SET ANSI_PADDING OFF

CT_InhouseCard表

 1 USE [GPOSDB]
 2 GO
 3 /****** 對象:  Table [dbo].[CT_InhouseCard]    腳本日期: 08/24/2013 10:59:58 ******/
 4 SET ANSI_NULLS ON
 5 GO
 6 SET QUOTED_IDENTIFIER ON
 7 GO
 8 SET ANSI_PADDING ON
 9 GO
10 CREATE TABLE [dbo].[CT_InhouseCard](
11     [RecordNO] [int] IDENTITY(1,1) NOT NULL,
12     [VC_IC_CardNO] [varchar](50) NOT NULL,
13     [VC_IC_PhysicalNO] [varchar](50) NULL,
14     [I_IC_CardType] [int] NULL,
15     [VC_IC_UserName] [varchar](50) NULL,
16     [VC_IC_JobNO] [varchar](50) NULL,
17     [VC_IC_UserID] [varchar](50) NULL,
18     [VC_IC_Password] [varchar](50) NULL,
19     [DE_IC_CardAmount] [decimal](18, 2) NULL,
20     [DE_IC_AppendAmount] [decimal](18, 2) NULL,
21     [DE_IC_ConsumerAmount] [decimal](18, 2) NULL,
22     [I_IC_ISLost] [int] NULL,
23     [D_IC_UsedDateTime] [datetime] NULL,
24     [D_IC_UselifeDateTime] [datetime] NULL,
25     [I_IC_IssueStationNO] [int] NULL,
26     [VC_IC_IssuerNO] [varchar](50) NULL,
27     [D_IC_IssueDateTime] [datetime] NULL,
28     [D_IC_LastUpdateDateTime] [datetime] NULL,
29     [I_IC_CardStatus] [int] NULL,
30     [VC_IC_Remark] [varchar](256) NULL,
31  CONSTRAINT [PK_CT_InhouseCard] PRIMARY KEY CLUSTERED 
32 (
33     [VC_IC_CardNO] ASC
34 )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
35 ) ON [PRIMARY]
36 
37 GO
38 SET ANSI_PADDING OFF

 


測試腳本

由於這個是客戶的數據庫,原本里面已經有數據了,因此在測試以前先更新兩個表的統計信息,以作到公正

1 USE [GPOSDB]
2 GO
3 UPDATE STATISTICS CT_FuelingData
4 UPDATE STATISTICS CT_InhouseCard
5 GO

 

IN語句

 1 USE [GPOSDB]
 2 GO
 3 DBCC DROPCLEANBUFFERS
 4 GO
 5 DBCC FREEPROCCACHE
 6 GO
 7 SET STATISTICS IO ON
 8 GO
 9 SET STATISTICS TIME ON
10 GO
11 SET STATISTICS PROFILE ON
12 GO
13 SELECT * FROM [dbo].[CT_FuelingData] WHERE [VC_FD_Cardno] IN (SELECT [VC_IC_CardNO] FROM [dbo].[CT_InhouseCard])

EXISTS語句

 1 USE [GPOSDB]
 2 GO
 3 DBCC DROPCLEANBUFFERS
 4 GO
 5 DBCC FREEPROCCACHE
 6 GO
 7 SET STATISTICS IO ON
 8 GO
 9 SET STATISTICS TIME ON
10 GO
11 SET STATISTICS PROFILE ON
12 GO
13 SELECT  *
14 FROM    [dbo].[CT_FuelingData]
15 WHERE   EXISTS ( SELECT [VC_IC_CardNO]
16                  FROM   [dbo].[CT_InhouseCard]
17                  WHERE  [dbo].[CT_FuelingData].[VC_FD_Cardno] = [dbo].[CT_InhouseCard].[VC_IC_CardNO] )

 


測試結果

IN語句

 1 SQL Server 執行時間:
 2    CPU 時間 = 0 毫秒,佔用時間 = 2 毫秒。
 3 SQL Server 分析和編譯時間: 
 4    CPU 時間 = 0 毫秒,佔用時間 = 0 毫秒。
 5 
 6 SQL Server 執行時間:
 7    CPU 時間 = 0 毫秒,佔用時間 = 0 毫秒。
 8 SQL Server 分析和編譯時間: 
 9    CPU 時間 = 0 毫秒,佔用時間 = 0 毫秒。
10 
11 SQL Server 執行時間:
12    CPU 時間 = 0 毫秒,佔用時間 = 0 毫秒。
13 SQL Server 分析和編譯時間: 
14    CPU 時間 = 0 毫秒,佔用時間 = 0 毫秒。
15 
16 SQL Server 執行時間:
17    CPU 時間 = 0 毫秒,佔用時間 = 0 毫秒。
18 SQL Server 分析和編譯時間: 
19    CPU 時間 = 31 毫秒,佔用時間 = 67 毫秒。
20 
21 (167 行受影響)
22'Worktable'。掃描計數 0,邏輯讀取 0 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
23'CT_FuelingData'。掃描計數 1,邏輯讀取 31 次,物理讀取 1 次,預讀 64 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
24'CT_InhouseCard'。掃描計數 1,邏輯讀取 2 次,物理讀取 1 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
25 
26 (4 行受影響)
27 
28 SQL Server 執行時間:
29    CPU 時間 = 16 毫秒,佔用時間 = 192 毫秒。

 

EXISTS語句

 1 SQL Server 分析和編譯時間: 
 2    CPU 時間 = 0 毫秒,佔用時間 = 0 毫秒。
 3 
 4 SQL Server 執行時間:
 5    CPU 時間 = 0 毫秒,佔用時間 = 0 毫秒。
 6 SQL Server 分析和編譯時間: 
 7    CPU 時間 = 0 毫秒,佔用時間 = 34 毫秒。
 8 
 9 (167 行受影響)
10'Worktable'。掃描計數 0,邏輯讀取 0 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
11'CT_FuelingData'。掃描計數 1,邏輯讀取 31 次,物理讀取 1 次,預讀 64 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
12'CT_InhouseCard'。掃描計數 1,邏輯讀取 2 次,物理讀取 1 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
13 
14 (4 行受影響)
15 
16 SQL Server 執行時間:
17    CPU 時間 = 0 毫秒,佔用時間 = 163 毫秒。

你們能夠看到除了執行時間有一點差異,IO是同樣的

由於數據量比較大,因此兩個查詢都用到了Worktable(中間表)來存儲中間結果

IN語句的執行計劃

EXISTS語句的執行計劃

 

從執行計劃能夠看到兩個SQL語句的開銷都是同樣的,並且你們都使用了右半鏈接(Right Semi Join)

至於什麼是半鏈接(Semi-join)你們能夠看一下這篇文章:SQL Join的一些總結

 

總結

從上面實際的執行來比較,,IN語句和EXISTS語句基本上都是同樣的效率

 

若有不對的地方,歡迎你們來拍磚o(∩_∩)o

相關文章
相關標籤/搜索