复杂的WHERE子句与使用UNION合并结果,哪个更好?

时间:2014-07-02 07:15:47

标签: sql-server union where-clause

我有一些重要的问题要做。我可以用两种方式之一来执行它:

  1. 综合而复杂的WHERE条款:

    SELECT *  
    FROM Talks d  
    WHERE (d.UserId IN (SELECT SomeGuyUserId FROM Network WHERE MainUserId = @ViewerUserId AND IsX = 0 AND IsY = 0)  
    AND DATEDIFF(DAY,d.Started,GETDATE()) < 3  
    AND d.Status <= 100)  
    OR   
    (d.UserId IN (SELECT SomeGuyUserId FROM Network WHERE MainUserId = @ViewerUserId AND IsX = 1 AND IsY = 0)   
    AND DATEDIFF(DAY,d.Started,GETDATE()) < 3   
    AND d.Status <= 110)   
    ORDER BY d.UserId;   
    
  2. 两个不同的SELECTSUNION

    SELECT *   
    FROM Talks d  
    WHERE (d.UserId IN (SELECT SomeGuyUserId FROM Network WHERE MainUserId = @ViewerUserId AND IsX = 0 AND IsY = 0)  
    AND DATEDIFF(DAY,d.Started,GETDATE()) < 3  
    AND d.Status <= 100)  
    UNION  
    SELECT *   
    FROM Talks d  
    (d.UserId IN (SELECT SomeGuyUserId FROM Network WHERE MainUserId = @ViewerUserId AND IsX = 1 AND IsY = 0)   
    AND DATEDIFF(DAY,d.Started,GETDATE()) < 3   
    AND d.Status <= 110)  
    
  3. 我有三个问题:

    1. 哪种方式更好用? (如果你能详细说明原因)
    2. 有没有更好的方法来执行此操作
    3. 在第二种情况下,我如何对统一结果进行排序(ORDER BY)(如第一个选项中所示)。
    4. PS。所有表格都很大(每个表格大于10M),这个查询应该被某些网站广泛使用。

3 个答案:

答案 0 :(得分:1)

在许多情况下,SQL Server在优化OR方面非常糟糕,因此您最好使用UNION。但唯一可以确定的方法是针对代表性的大型数据库测试两个查询。使用SQL事件探查器来比较度量标准。

另一种方法是重构方法1的WHERE子句;尝试将OR更深地移动到表达式树中,或者如果可能的话,完全消除它。例如:

SELECT *
FROM Talks d
INNER JOIN Network n ON n.SomeGuyUserId = d.UserId
WHERE n.MainUserId = @ViewerUserId
AND n.IsX IN (0, 1)
AND n.IsY = 0
AND DATEDIFF(DAY, d.Started, GETDATE()) < 3
AND d.Status <= CASE n.IsX WHEN 0 THEN 100 WHEN 1 THEN 110 END
ORDER BY d.UserId;

请注意,此查询可能会导致重复记录,因为我已使用联接替换了IN <subquery>。如果表MainUserId中存在SomeGuyUserIdNetwork的重复组合,则会发生这种情况。如果可能,请使用唯一约束来避免这种情况。

答案 1 :(得分:0)

您必须查看两个查询的执行计划才能确定。可能会发生两件不同的事情:

  • 第一个查询会在Talks上产生一次全表扫描,而。{ 第二个查询导致两个全表扫描(一个用于联合的每个部分)。在那种情况下,第一个 查询可能更快。

  • 第一个查询会在Talks上产生一次全表扫描,而。{ 第二个查询导致两个索引范围扫描。在那种情况下, 第二个查询可能更快。

答案 2 :(得分:0)

基本上你的第一个sql查询不是很复杂。与我多年来看到的所有查询相比,它实际上是一个简单的...我只是提取DATEDIFF并继续... ...

SELECT *  
FROM Talks d  
WHERE DATEDIFF(DAY,d.Started,GETDATE()) < 3  
and (    (d.UserId IN (SELECT SomeGuyUserId FROM Network WHERE MainUserId = @ViewerUserId AND IsX = 0 AND IsY = 0) AND d.Status <= 100)  
     OR   
         (d.UserId IN (SELECT SomeGuyUserId FROM Network WHERE MainUserId = @ViewerUserId AND IsX = 1 AND IsY = 0) AND d.Status <= 110)   
    )
ORDER BY d.UserId;