查询具有 where (a = a) 或 (b = b) - 导致索引扫描的第二部分。包括完整示例

Pet*_*ock 3 index sql-server-2005

当两个值(原始表及其查找的表)都是整数时,我一直试图弄清楚为什么这个查询(对整数)使用索引扫描。

第一部分用于用值填充表格。

第二部分是我正在处理的搜索查询。如果在查询上运行执行计划,您将看到即使intcol上有一个索引,并且 #temp 表的类型为int,它仍然使用索引扫描并扫描所有 298 000 行。

问题是我正在使用的查询,它具有 (a = a) 或 (b = b)

当我取出“或(b = b)”时,它不会进行索引扫描。但是我需要“or (b == b)”来完成这个复杂的查询工作。

导致索引扫描的隐式转换

    CREATE TABLE dbo.TestImplicitConverts
(
  RowID int NOT NULL IDENTITY (1, 1),
  BigIntCol bigint NOT NULL,
  BitCol bit NOT NULL,
  CharCol char(10) NOT NULL,
  DateTimeCol datetime NOT NULL,
   DecimalCol decimal(10, 2) NOT  NULL , 
  FloatCol float(53) NOT  NULL  ,
  IntCol int NOT NULL,
  MoneyCol money NOT NULL,
  NCharCol nchar(10) NOT NULL,
  NumericCol numeric(10, 2) NOT NULL,
  NVarchrCol nvarchar(50) NOT NULL,
  RealCol real NOT NULL,
  SmallDateTimeCol smalldatetime NOT NULL,
  SmallIntCol smallint NOT NULL,
  SmallMoneyCol smallmoney NOT NULL,
  TinyIntCol tinyint NOT NULL,
  GUIDCol uniqueidentifier NOT NULL,
  VarcharCol varchar(50) NOT NULL,
  CONSTRAINT PK_TestImplicitConverts PRIMARY KEY CLUSTERED (RowID)

 )
GO

-- Create nonclustered indexes on all columns to test implicit conversion affects

CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_BigIntCol ON dbo.TestImplicitConverts (BigIntCol);
GO

CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_BitCol ON dbo.TestImplicitConverts (BitCol);
GO
CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_CharCol ON dbo.TestImplicitConverts (CharCol);
GO


CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_DateTimeCol ON dbo.TestImplicitConverts (DateTimeCol);
GO

CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_DecimalCol ON dbo.TestImplicitConverts (DecimalCol);
GO

CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_FloatCol ON dbo.TestImplicitConverts (FloatCol);
GO

CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_GUIDCol ON dbo.TestImplicitConverts (GUIDCol);
GO

CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_NVarcharCol ON dbo.TestImplicitConverts (NVarchrCol);
GO

CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_RealCol ON dbo.TestImplicitConverts (RealCol);
GO

CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_SmallDateTimeCol ON dbo.TestImplicitConverts (SmallDateTimeCol);
GO

CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_SmallIntCol ON dbo.TestImplicitConverts (SmallIntCol);
GO

CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_IntCol ON dbo.TestImplicitConverts (IntCol);
GO

CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_MoneyCol ON dbo.TestImplicitConverts (MoneyCol);
GO

CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_NCharCol ON dbo.TestImplicitConverts (NCharCol);
GO

CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_NumericCol ON dbo.TestImplicitConverts (NumericCol);
GO

CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_SmallMoneyCol ON dbo.TestImplicitConverts (SmallMoneyCol);
GO


CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_TinyIntCol ON dbo.TestImplicitConverts (TinyIntCol);
GO

CREATE NONCLUSTERED INDEX IX_TestImplicitConverts_VarcharCol ON dbo.TestImplicitConverts (VarcharCol);
GO



INSERT INTO dbo.TestImplicitConverts
(   BigIntCol, BitCol, CharCol, DateTimeCol, 
    DecimalCol, FloatCol, IntCol, MoneyCol, NCharCol, NumericCol, NVarchrCol, RealCol,
    SmallDateTimeCol, SmallIntCol, SmallMoneyCol,  TinyIntCol, GUIDCol, VarcharCol)
SELECT a.number, a.number%1, CAST(b.name AS CHAR(10)),  DATEADD(ms, -1*a.number, GETDATE()), 
   a.number, a.number, a.number, a.number, CAST(b.name AS NCHAR(10)), a.number, b.name, a.number,
    DATEADD(ms, -1*a.number, GETDATE()), a.number, a.number, a.number%255, NEWID(), b.name

FROM master.dbo.spt_values AS a
CROSS JOIN master.dbo.spt_values AS b
WHERE a.type = N'P'
  AND a.number < 1000
  AND b.name IS NOT NULL;
GO
ALTER INDEX ALL ON TestImplicitConverts REBUILD;
GO
Run Code Online (Sandbox Code Playgroud)
 create table #temp(myint int, my2ndint int)
 insert into #temp(myint, my2ndint)
 select 800,140



while @@rowcount>0
begin
    insert into #temp(myint,my2ndint)
    SELECT intcol, intcol
    FROM    TestImplicitConverts
    WHERE   
       (
       intcol in (select myint from #temp  )
         and 
       intcol not in (select my2ndint from #temp)
       )
    or (
       intcol in (select my2ndint from #temp  )
          and 
       intcol not in (select myint from #temp  )
       )

 end
 drop table #temp
Run Code Online (Sandbox Code Playgroud)

Mar*_*ith 5

你的谓词是这样的形式

WHERE  (A AND NOT B) OR (B AND NOT A)
Run Code Online (Sandbox Code Playgroud)

在 SQL Server 2008 上进行测试并FORCESEEK提示它无法生成计划。

如果您将它们重新排列为

WHERE (A OR B) AND NOT (A AND B)
Run Code Online (Sandbox Code Playgroud)

然后您可以通过搜索获得一个计划(不需要提示)。

SELECT intcol,
       intcol
FROM   TestImplicitConverts
WHERE  ( ( intcol IN (SELECT myint
                      FROM   #temp) )
          OR ( intcol IN (SELECT my2ndint
                          FROM   #temp) ) )
       AND NOT ( intcol IN (SELECT myint
                            FROM   #temp)
                 AND intcol IN (SELECT my2ndint
                                FROM   #temp) )
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

或者另一种可能性是

WITH T
     AS (SELECT DISTINCT ISNULL(t1.myint, t2.my2ndint) AS intcol
         FROM   #temp t1
                FULL OUTER JOIN #temp t2
                  ON t1.myint = t2.my2ndint
         WHERE  t1.myint IS NULL
                 OR t2.my2ndint IS NULL)
SELECT T.intcol,
       T.intcol
FROM   T
       JOIN TestImplicitConverts TIC
         ON TIC.intcol = T.intcol 
Run Code Online (Sandbox Code Playgroud)