从一个表中获取相交数据的最佳方法是什么?

时间:2013-04-22 23:24:28

标签: sql sql-server database performance

假设我有下表

enter image description here

CREATE TABLE [dbo].[TestData](
    [ID] [bigint] NOT NULL,
    [InstanceID] [int] NOT NULL,
    [Field] [int] NULL,
    [UserID] [bigint] NOT NULL
) ON [PRIMARY]

GO
INSERT [dbo].[TestData] ([ID], [InstanceID], [Field], [UserID]) 
VALUES (1, 1, NULL, 1000),(2, 1, NULL, 1002),(3, 1, NULL, 1000),
    (4, 1, NULL, 1003),(5, 2, NULL, 1002), (6, 2, NULL, 1005),
    (7, 2, NULL, 1006),(8, 2, NULL, 1007),(9, 3, NULL, 1002),
    (10, 3, NULL, 1006),(11, 3, NULL, 1009),(12, 3, NULL, 1010),
    (13, 1, NULL, 1006),(14, 2, NULL, 1002),(15, 3, NULL, 1003)
GO

我搜索编写查询的最佳做法,使用 UserID

在两个实例之间获取完整的交叉数据行

例如,InstanceID 1和2之间的相交的 UserIDs 是(1002,1006),以获得结果我用两种不同的方式来验证查询:

Select * From TestData
Where UserID in 
( 
    Select T1.UserID From TestData T1 Where InstanceID = 1
        Intersect
    Select T2.UserID From TestData T2 Where InstanceID = 2
)
and InstanceID in (1,2) Order By 1

第二

Select * From TestData
Where UserID in 
( 
    Select Distinct T1.UserID 
    From TestData T1 join TestData T2 on T1.UserID = T2.UserID
    Where T1.InstanceID = 1 and T2.InstanceID = 2
)
and InstanceID in (1,2) Order By 1

结果将是

enter image description here

以上查询之一是获得结果的最佳方法吗?

4 个答案:

答案 0 :(得分:0)

使用EXISTS比使用IN更好。使用IN子查询时,将处理整个结果集。使用EXISTS时,只需搜索它们即可匹配。至于你的问题,我认为INTERSECT实现只是简单地进行连接,所以不应该有区别。

编辑:帖子Here表示,对于IN vs EXISTS,优化程序也会对它们进行同样的处理(截至2008年)。所以我的猜测以及我刚刚读到的内容归结为:它们将执行相同的操作,因为优化器知道。

答案 1 :(得分:0)

如果你要使用EXISTS语句,这是一个查询示例:

SELECT * 
FROM TestData td
WHERE td.InstanceID IN (1, 2)
AND EXISTS
    (SELECT 1
    FROM TestData sub
    WHERE td.UserID = sub.UserID
    AND sub.InstanceID = 2)
AND EXISTS
    (SELECT 1
    FROM TestData sub
    WHERE td.UserID = sub.UserID
    AND sub.InstanceID = 1)
ORDER BY 1;

对于提供的样本数据,三种解决方案中的任何一种都没有明显的性能差异。但是,我同意Scotch的观点,即在特定情况下使用EXISTS语句将有助于提高IN语句的性能。

为提高性能,您可以做的最好的事情是使用PRIMARY KEY创建表。将ID字段设置为PRIMARY KEY将使性能提高50%,因为查询的最高成本是对数据进行排序。

答案 2 :(得分:0)

您也可以使用聚合和加入来执行此操作:

select td.*
from TestData td join
     (select td.userid
      from TestData
      group by td.userId
      having sum(case when InstanceId = 1 then 1 else 0 end) > 0 and
             sum(case when InstanceId = 2 then 1 else 0 end) > 0
    ) td2
    on td.userid = td2.userid

聚合的优点是having子句使其在您可以表示的条件方面非常灵活。如果您有userId, InstanceId的索引,那么效果最佳。

答案 3 :(得分:0)

该脚本由两个Index seek操作和一个Distinct排序操作使用。

SELECT ID, InstanceID, Field, UserID
FROM [dbo].[TestData] t
WHERE InstanceID IN(1, 2) 
  AND EXISTS (
              SELECT 1
              FROM [dbo].[TestData] t2
              WHERE InstanceID IN(1, 2) AND t.UserID = t2.UserID
              HAVING COUNT(DISTINCT t2.InstanceID) = 2
              )
ORDER BY t.ID

OR

;WITH cte AS
 (
  SELECT ID, InstanceID, Field, UserId
         ,COUNT(*) OVER(PARTITION BY InstanceID, UserID) AS cntInstanceUser
  FROM [dbo].[TestData] t
  WHERE InstanceID IN(1, 2)
  )
  SELECT c.ID, c.InstanceID, c.Field, c.UserID
  FROM cte c
  WHERE EXISTS (
                SELECT 1
                FROM cte c2 
                WHERE c2.UserId = c.UserID
                HAVING COUNT(*) != c.cntInstanceUser
                )
  ORDER BY c.ID

为了提高性能,请使用此索引:

CREATE INDEX x ON [dbo].[TestData](InstanceID, UserID) INCLUDE(Id, Field)

SQLFiddle上的演示