3

假设我有下表

在此处输入图像描述

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 之间相交的UserID是(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

所以结果将是

在此处输入图像描述

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

4

4 回答 4

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

于 2013-04-23T01:51:48.123 回答
0

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

编辑:这里的一篇文章说对于INvs EXISTS,优化器也会对它们一视同仁(截至 2008 年)。所以几乎我的猜测以及我刚刚阅读的内容归结为:它们将执行相同的操作,因为优化器知道。

于 2013-04-22T23:28:32.273 回答
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%,因为查询的最高成本是对数据进行排序。

于 2013-04-22T23:44:05.330 回答
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

或者

;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上的演示

于 2013-04-23T09:18:08.633 回答