FROM子句中的SQL子查询

时间:2012-10-03 16:27:39

标签: sql-server subquery dynamic-sql

我理解,如果使用不正确,子查询对性能的影响是非常糟糕的。我有一个非常具体的场景,用户需要从表中检索过滤的记录集。将提供各种各样的过滤器,它们必须支持组合。此外,一组开发人员将定期创建新的过滤器。

我不喜欢一个具有大量参数的增长的单一SQL查询的想法。我不喜欢一堆具有相同SELECT语句和不同WHERE子句的自治SQL查询。我喜欢动态SQL查询的想法,但我不确定我应该使用什么样的结构。我可以想到4个基本选项:(如果还有更多我缺失,那么请不要犹豫,建议他们)

  1. " INNER JOIN":通过INNER JOINS连接过滤器以过滤结果。
  2. " FROM子查询":通过FROM语句中的子查询进行堆栈过滤。
  3. " WHERE子查询":通过WHERE子句中的子查询来连接过滤器。
  4. " INNER JOIN子查询":一个奇怪的混合。
  5. 我已经创建了一个SQL小提示器来演示(和分析)它们:

      

    http://sqlfiddle.com/#!3/4e17b/9

    以下是小提琴的摘录,以提供我所谈论的内容:

    ------------------------------------------------------------------------
    --THIS IS AN EXCERPT FROM THE SQL FIDDLE -- IT IS NOT MEANT TO COMPILE--
    ------------------------------------------------------------------------
    
    --
    --"INNER JOIN" test
          SELECT COUNT(*) 
          FROM 
                @TestTable Test0
                INNER JOIN @TestTable Test1 ON Test1.ID=Test0.ID AND Test1.ID % @i = 0
                INNER JOIN @TestTable Test2 ON Test2.ID=Test0.ID AND Test2.ID % @j = 0
                INNER JOIN @TestTable Test3 ON Test3.ID=Test0.ID AND Test3.ID % @k = 0
    
    --
    --"FROM subqueries" test   
          SELECT COUNT(*) FROM (
                SELECT * FROM (
                      SELECT * FROM (
                            SELECT * FROM @TestTable Test3 WHERE Test3.ID % @k = 0
                      ) Test2 WHERE Test2.ID % @j = 0
                ) Test1 WHERE Test1.ID % @i = 0
          ) Test0
    
    --
    --"WHERE subqueries" test
          SELECT COUNT(*) 
          FROM @TestTable Test0
          WHERE 
                Test0.ID IN (SELECT ID FROM @TestTable Test1 WHERE Test1.ID % @i = 0)
                AND Test0.ID IN (SELECT ID FROM @TestTable Test2 WHERE Test2.ID % @j = 0)
                AND Test0.ID IN (SELECT ID FROM @TestTable Test3 WHERE Test3.ID % @k = 0)
    
    --
    --"INNER JOIN subqueries" test
        SELECT COUNT(*) 
        FROM 
            TestTable Test0
            INNER JOIN (SELECT ID FROM TestTable WHERE ID % @i = 0) Test1 ON Test1.ID=Test0.ID
            INNER JOIN (SELECT ID FROM TestTable WHERE ID % @j = 0) Test2 ON Test2.ID=Test0.ID
            INNER JOIN (SELECT ID FROM TestTable WHERE ID % @k = 0) Test3 ON Test3.ID=Test0.ID
    
    --
    --"EXISTS subqueries" test
        SELECT COUNT(*) 
        FROM TestTable Test0
        WHERE 
            EXISTS (SELECT 1 FROM TestTable Test1 WHERE Test1.ID = Test0.ID AND Test1.ID % @i = 0)
            AND EXISTS (SELECT 1 FROM TestTable Test2 WHERE Test2.ID = Test0.ID AND Test2.ID % @j = 0)
            AND EXISTS (SELECT 1 FROM TestTable Test3 WHERE Test3.ID = Test0.ID AND Test3.ID % @k = 0)
    

    排名(执行测试的时间)

    SQL小提琴:

    |INNER JOIN|FROM SUBQUERIES|WHERE SUBQUERIES|INNER JOIN SUBQUERIES|EXISTS SUBQUERIES|
    -------------------------------------------------------------------------------------
    |     5174 |           777 |           7240 |                5478 |            7359 |
    

    本地环境:(没有缓存:每次测试前清除缓冲区)

    |INNER JOIN|FROM SUBQUERIES|WHERE SUBQUERIES|INNER JOIN SUBQUERIES|EXISTS SUBQUERIES|
    -------------------------------------------------------------------------------------
    |     3281 |          2851 |           2964 |                3148 |            3071 |
    

    本地环境:(使用缓存:连续两次运行查询并记录第二次运行的时间)

    |INNER JOIN|FROM SUBQUERIES|WHERE SUBQUERIES|INNER JOIN SUBQUERIES|EXISTS SUBQUERIES|
    -------------------------------------------------------------------------------------
    |      284 |            50 |           3334 |                 278 |             408 |
    

    每种解决方案都有优点/缺点。 WHERE子句中的子查询具有非常糟糕的性能。 FROM子句中的子查询具有相当好的性能(实际上它们通常表现最佳)(注意:我相信这种方法会否定索引的好处?)。 INNER JOIN具有相当好的性能,但它引入了一些有趣的范围问题,因为与子查询不同,INNER JOIN将在相同的上下文中运行(必须有一个中间系统来避免表别名的冲突)。

    总的来说,我认为最干净的解决方案是FROM子句中的子查询。过滤器很容易编写和测试(因为与INNER JOIN不同,它们不需要提供上下文/基本查询)。

    思考?这是子查询的有效用法还是等待发生的灾难?

    更新(2012/10/04):

    • 更新了SQL小提琴以包含对" EXISTS"的测试。方法
    • 从SQL Fiddle和本地环境添加了性能测试

1 个答案:

答案 0 :(得分:0)

如果你总是要应用“和”逻辑,那么内连接可能是一个很好的方法(我推广,但它会因很多因素而变化,包括你的表大小和索引等)。如果您希望能够应用“和”或“或”过滤,则需要使用其他解决方案之一。

此外,您应该使用exists子句测试性能:

SELECT COUNT(*) 
      FROM @TestTable Test0
      WHERE 
            EXISTS (SELECT 1 FROM @TestTable Test1 WHERE Test0.ID = Test1.ID AND Test1.ID % @i = 0)
            EXISTS (SELECT 1 FROM @TestTable Test2 WHERE Test0.ID = Test2.ID AND Test2.ID % @j = 0)
            EXISTS (SELECT 1 FROM @TestTable Test3 WHERE Test0.ID = Test3.ID AND Test3.ID % @k = 0)