在SQL中执行多个“IN”查询的最快方法

时间:2012-08-22 09:45:19

标签: sql sql-server tsql

我使用需要在我们的数据库上运行的2“IN”语句进行相当可怕的查询。首先是模式(本例简化):

CREATE TABLE [dbo].[SystemUser]
(
    [SystemUserID] [int] IDENTITY(1,1) NOT NULL,
    [FirstName] [nvarchar](50) NULL,
    [Surname] [nvarchar](50) NULL
    CONSTRAINT [PK_ApplicationUser] PRIMARY KEY CLUSTERED 
    (
        [SystemUserID] ASC
    )
)
GO

CREATE TABLE [dbo].[Group]
(
    [GroupID] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NULL
    CONSTRAINT [PK_Group] PRIMARY KEY CLUSTERED 
    (
        [GroupID] ASC
    )
)
GO

CREATE TABLE [dbo].[GroupMembership]
(
    [SystemUserID] [int] NOT NULL,
    [GroupID] [int] NOT NULL
    CONSTRAINT [PK_GroupMembership] PRIMARY KEY CLUSTERED 
    (
        [SystemUserID] ASC,
        [GroupID] ASC
    )
)
GO

我想要做的是找到所有“SystemUser”记录,这些记录与没有GroupID列表中“Group”成员身份的SystemUserID列表相匹配。

因此,在一个查询中比较了两个独立的ID列表。我现在想到的最快的方法是:

SELECT SU.SystemUserID
FROM [dbo].[SystemUser] SU 
LEFT JOIN
(
    SELECT GM.SystemUserID
    FROM [dbo].[GroupMembership] GM
    WHERE GM.GroupID IN
    (
        1, 7, 8, 10, 32
    )
) GM ON GM.SystemUserID = SU.SystemUserID
WHERE SU.SystemUserID IN
(
    10, 61, 80, 93, 98
)
AND GM.SystemUserID IS NULL /* Not matched */

我有什么遗漏; “哪里不存在”检查会更有效率?或者你能想到两个列表更好的处理和过滤方式吗?

4 个答案:

答案 0 :(得分:4)

假设SQL Server 2005或更高版本,

SELECT SU.SystemUserID
FROM [dbo].[SystemUser] SU 
WHERE SU.SystemUserID IN
(
    10, 61, 80, 93, 98
)
EXCEPT
SELECT GM.SystemUserID
FROM [dbo].[GroupMembership] GM
WHERE GM.GroupID IN
(
    1, 7, 8, 10, 32
)

答案 1 :(得分:2)

使用IN来封闭范围,而不是使用BETWEEN(发出单独的查询):

SELECT SU.SystemUserID 
FROM [dbo].[SystemUser] SU  
LEFT JOIN 
( 
    SELECT GM.SystemUserID 
    FROM [dbo].[GroupMembership] GM 
    WHERE GM.GroupID BETWEEN 1 AND 5

) GM ON GM.SystemUserID = SU.SystemUserID 
WHERE SU.SystemUserID BETWEEN 10 AND 14
AND GM.SystemUserID IS NULL /* Not matched */

如果您的范围根本不是连续的,请创建临时表(或CTE),填充值,然后使用内部联接。

答案 2 :(得分:2)

通过重写查询文本可以解决很少的问题查询,您的查询也不例外。性能问题的罪魁祸首几乎总是缺少索引,你的同样也不例外。

SELECT SU.SystemUserID
FROM [dbo].[SystemUser] SU 
LEFT JOIN
(
    SELECT GM.SystemUserID
    FROM [dbo].[GroupMembership] GM
    WHERE GM.GroupID IN
    (
        1, 7, 8, 10, 32
    )
) GM ON GM.SystemUserID = SU.SystemUserID
WHERE SU.SystemUserID IN
(
    10, 61, 80, 93, 98
)
AND GM.SystemUserID IS NULL /* Not matched */

所以你需要:

    {li> GroupID GroupMembership 上的索引 {li> SystemUserID SystemUser 上的索引
  1. SystemUserID GroupMembership上的索引(用于加入)
  2. 你DDL(用于添加它!)显示你已经解决了2)和3),但没有解决1)。所以添加缺少的索引:

    CREATE NONCLUSTERED INDEX idx_GroupMembership_GroupID ON GroupMembership(GroupID, SystemUserID)
    

    根据经验法则:表格的多对多表格(leftId,rightId)总是需要(leftId, rightId)上的索引和(rightId, leftId)上的索引。

答案 3 :(得分:0)

根据我的经验,我更多地通过加入具有所需值的临时表而不是在值列表增加时使用IN来获得更好的结果。有100个值,我首先习惯这样,但可能会写两个并比较计划和统计数据。