最近不少人討論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