我知道子查询在使用不当时性能出色。我有一个非常具体的场景,用户需要从表中检索一组经过筛选的记录。各种各样的过滤器将可用,他们必须支持组合。而且,新的过滤器将由一组开发者定期创建。FROM子句中的SQL子查询
我不喜欢一个不断增长的单一SQL查询与大量参数的想法。我不喜欢使用相同的SELECT语句和不同的WHERE子句的一堆自治SQL查询的想法。我喜欢动态SQL查询的想法,但我不确定我应该使用什么样的结构。我能想到的4个基本选项:(如果有更多的,我很想念,那么请不要犹豫,建议他们)
- “INNER JOIN”:通过INNER串联过滤器连接来筛选结果。
- “FROM子查询”:通过FROM语句中的子查询堆栈筛选器。
- “WHERE子查询”:通过WHERE子句中的子查询来污染过滤器。
- “INNER JOIN子查询”:一个奇怪的混合。
我创建了一个SQL小提琴证明(和配置文件),其中:
下面是从小提琴的摘录提供什么我的想法米谈论:
------------------------------------------------------------------------
--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小提琴
- 增加的性能测试和当地环境
增加测试的“存在”的方法。尽管我很惊讶地看到“SELECT *”和“SELECT 1”(当然不是最受控制的测试,但存在一些明确的变化)之间的某些区别,但它排在最后。 一般方法将应用于各种表格,因此大小和索引难以预先预测。我基本上在寻找“最友好的沙箱”。 – makerplays