2012-10-03 105 views
5

我知道子查询在使用不当时性能出色。我有一个非常具体的场景,用户需要从表中检索一组经过筛选的记录。各种各样的过滤器将可用,他们必须支持组合。而且,新的过滤器将由一组开发者定期创建。FROM子句中的SQL子查询

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

  1. “INNER JOIN”:通过INNER串联过滤器连接来筛选结果。
  2. “FROM子查询”:通过FROM语句中的子查询堆栈筛选器。
  3. “WHERE子查询”:通过WHERE子句中的子查询来污染过滤器。
  4. “INNER JOIN子查询”:一个奇怪的混合。

我创建了一个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 JOINs将在相同的上下文中操作(必须是中介系统以避免表别名的冲突)。

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

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

更新(2012年10月4日):

  • 更新SQL小提琴包括测试 “存在” 的方法从SQL小提琴
  • 增加的性能测试和当地环境

回答

0

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

此外,你应该使用测试出的性能存在条款:

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) 
+0

增加测试的“存在”的方法。尽管我很惊讶地看到“SELECT *”和“SELECT 1”(当然不是最受控制的测试,但存在一些明确的变化)之间的某些区别,但它排在最后。 一般方法将应用于各种表格,因此大小和索引难以预先预测。我基本上在寻找“最友好的沙箱”。 – makerplays