如何使用数千个 WHERE 子句优化 SQL 查询

Posted

技术标签:

【中文标题】如何使用数千个 WHERE 子句优化 SQL 查询【英文标题】:How to optimize an SQL query with many thousands of WHERE clauses 【发布时间】:2010-04-19 15:29:35 【问题描述】:

我对一个非常大的数据库进行了一系列查询,并且在 WHERE 子句中有数十万个 OR。优化此类 SQL 查询的最佳和最简单的方法是什么?我发现了一些关于创建临时表和使用连接的文章,但我不确定。我是严肃 SQL 的新手,一直在将结果从一个剪切和粘贴到另一个。

SELECT doc_id, language, author, title FROM doc_text WHERE language='fr' OR language='es'
SELECT doc_id, ref_id FROM doc_ref WHERE doc_id=1234567 OR doc_id=1234570 OR doc_id=1234572 OR doc_id=1234596 OR OR OR ...
SELECT ref_id, location_id FROM ref_master WHERE ref_id=098765 OR ref_id=987654 OR ref_id=876543 OR OR OR ...
SELECT location_id, location_display_name FROM location
SELECT doc_id, index_code, FROM doc_index WHERE doc_id=1234567 OR doc_id=1234570 OR doc_id=1234572 OR doc_id=1234596 OR OR OR x100,000

这些未优化的查询每个可能需要超过 24 小时。干杯。

【问题讨论】:

【参考方案1】:

我想我刚刚回答了我自己的问题...嵌套表!

SELECT doc_text.doc_id, doc_text.language, doc_text.author, doc_text.title, doc_ref.ref_id, ref_master.location_id, location.location_display_name, doc_index.doc_id, doc_index.display_heading
FROM DOC_TEXT, DOC_REF, REF_MASTER, LOCATION, DOC_INDEX
WHERE
    doc_text.language='fr' OR doc_text.language='es'
AND
    doc_text.doc_id=doc_ref.doc_id
AND
    doc_ref.doc_id=ref_master.ref_id
AND
    ref_master.location_id=location.location_id
AND
    doc_text.doc_id=doc_index.doc_id

【讨论】:

那不是嵌套表。这些是常见的内部连接。欢迎来到关系数据库的世界。 ;) 谢谢。我设法从一整天的等待时间缩短到“.20145 SECONDS”。呜呜呜! 是的……生活中的小事是最令人愉快的。 ;)(哦,+1 供您自行查找) 将您的答案标记为正确,如果您确实是从前一个选择中生成 where 子句的代码。您是正确的,INNER JOIN 是正确的解决方案,否则尝试模拟它们会造成巨大的性能损失。 @Matthew:48 小时后可以接受自己的答案,而不是马上。【参考方案2】:

最简单的方法是:

在被过滤的列(languageref_iddoc_id 等)上创建索引,至少仔细检查它们的存在。如果它们是表的主索引,则将它们聚集在一起。 创建包含条件的帮助表(通过 INSERT/DELETE 语句添加/删除条件),并为它们编制索引。 而不是 1000 个“OR”组件,而是进行 INNER JOIN:

所以...

SELECT doc_id, language, author, title 
  FROM doc_text
 WHERE language='fr' OR language='es'

变成

INSERT language_search (language) VALUES ('fr')
INSERT language_search (language) VALUES ('es')
/* and 50 more */

SELECT dt.doc_id, dt.language, dt.author, dt.title 
  FROM doc_text dt
       INNER JOIN language_search ls ON dt.language = ls.language

【讨论】:

看起来,问题作者正在寻找不那么复杂的东西——跨 5 个表的连接是实际尝试做的。嗯嗯。【参考方案3】:

您可以使用in关键字,而不是在同一个字段上有很多条件:

SELECT doc_id, ref_id FROM doc_ref WHERE doc_id in (1234567, 1234570, 1234572, 1234596, ...)

这将使查询更短,但不确定性能是否会有很大差异。您应该确保在相关字段上有索引,这通常会对性能产生巨大影响。

编辑

但是,您有很多值要比较的原因似乎是您正在使用一个查询的结果来创建下一个查询。这当然应该通过连接而不是动态查询来解决:

select
  doc_text.doc_id, doc_text.language, doc_text.author, doc_text.title,
  doc_ref.ref_id, ref_master.location_id, location.location_display_name,
  doc_index.doc_id, doc_index.display_heading
from DOC_TEXT
inner join DOC_REF on doc_text.doc_id = doc_ref.doc_id
inner join REF_MASTER on doc_ref.doc_id = ref_master.ref_id
inner join LOCATION on ref_master.location_id = location.location_id
inner join DOC_INDEX on doc_text.doc_id = doc_index.doc_id
where
  doc_text.language in ('fr', 'es')

【讨论】:

“使用一个查询的结果来创建下一个”我也应该想到。我的心理调试能力在这方面让我失望了。 ;) +1【参考方案4】:

我认为你真正的问题是你没有加入表。

这是一个猜测,但我敢打赌,您运行一个查询,然后获取应用程序中的所有 ID,然后运行另一个查询,其中所有行都与前一个查询匹配。通过编写带有连接的查询,您将大大提高性能:

SELECT
    *
    FROM YourTableA            a
        INNER JOIN YourTableB  b ON a.ID=b.ID
    WHERE a. .....

然后在您的应用程序中处理单个结果集。

【讨论】:

以上是关于如何使用数千个 WHERE 子句优化 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Joomla 的组件菜单项中获取 MySQL 查询结果并使用 foreach where 子句进行优化?

如何优化SQL语句

WHERE 子句中的 SQL 查询子选择优化 (SQL Server)

查询优化(MySql/Sql):将函数移出 where 子句

如何使用 sql server exists 子句进行优化?

使用具有一个条件的 WHERE 子句运行 100 个 SQL 查询,还是使用具有 100 个条件的 WHERE 子句的一个查询更好?