长的IN子句是代码味吗?

时间:2011-06-02 19:40:13

标签: mysql sql database performance

简单的问题。想知道长的IN子句是否是代码气味?我真的不知道如何证明这一点。我不能把手指放在为什么它闻起来不像我认为的那样。

select
  name,
  code,
  capital,
  population,
  flower,
  bird
from us_states
where
  code in
    ('NJ', 'NY', 'PA', 'CA', 'AL', 'AK', 'AZ',
    'IL', 'IN', 'KY', 'KS', 'DC', 'MD', 'MA')

数据库通常如何实现这样的查找?是一个临时表制作并加入?或者它只是扩展为一系列逻辑OR?

感觉它应该是一个加入......

我不是说所有的IN条款都不好。有时你无法帮助它。但是有些情况(特别是它们得到的时间越长),你所匹配的元素集合实际上来自某个地方。不应该加入吗?

是否值得创建(通过应用程序级别)一个临时表,其中包含您要搜索的所有元素,然后对其进行真正的连接?

select u.*
from us_states u

join #chosen_states t
on u.code = t.code

5 个答案:

答案 0 :(得分:8)

我认为这是代码味道。首先,数据库对IN子句中允许的元素数量有限制,如果动态生成SQL,最终可能会遇到这些限制。

当列表开始变长时,我会转换为使用带临时表的存储过程,以避免出现错误。

我怀疑性能是一个主要问题,IN条款非常快,因为它们可以短路,与NOT IN条款不同。

答案 1 :(得分:4)

  

是否值得(通过应用程序级别)创建临时表。

IN的问题在于它不使用索引,并且对源表中的每一行重复比较(最差情况:此处为x14)

如果在连接字段上添加索引,则创建临时表是个好主意 这样查询可以使用BTree索引直接查找值,该索引应该只进行3或4次比较最坏情况log2(14)= 3.something
哪个更快。

如果你很聪明,你甚至可以使用hash-index,在这种情况下,数据库只需要进行1次比较,与btree索引相比,你的查询速度提高了3倍。

使用临时表的提示
确保使用内存表
使用hash index作为主键 尝试在一个语句中执行插入操作。

由于使用哈希索引进行O(1)查找时间,因此加速创建temp-table的半常数时间会相形见绌。

答案 2 :(得分:0)

我不知道这是代码味道,确切地说。有时你只有很长的事情in列出你可能存在的事情。

至于制作一个带有元素的临时表(甚至是一个查找表)并加入反对(甚至做一个where [column] in (select [lookup] from [lookuptable])是我喜欢的方法之一IFF * a)有大量的值b)如果有的话,很少会改变。

*:“If and Only If”

答案 3 :(得分:0)

您也可以使用带有IN的子查询,如here in the manual所述。

SELECT * FROM us_states WHERE code IN (SELECT code FROM state_codes);

答案 4 :(得分:0)

我也认为它是“嗅觉”。对于不经意的观察者来说,IN条款可能类似于集合,列表,包,表等,但不是。

根据SQL标准,您的IN子句仅仅是

的语法糖
(
 code = 'NJ' OR code = 'NY' OR code = 'PA' OR code = 'CA' 
    OR code = 'AL' OR code = 'AK' OR code = 'AZ' 
    OR code = 'IL' OR code = 'IN' OR code = 'KY' 
    OR code = 'KS' OR code = 'DC' OR code = 'MD' 
    OR code = 'MA'
)

我希望典型的解析器能够以这种方式扩展IN子句;我知道SQL Server的作用,因为当我在INFORMATION_SCHEMA中检查约束的定义时,我用来创建某些IN约束的漂亮,整洁的CHECK子句变成了一组丑陋的OR子句。 YMMV:如果你担心性能,请测试。

有一个设计经验法则规定,如果值集很小且稳定,则使用IN子句,否则使用表。 52个中的14个是“小”是主观的。小表是否最佳索引可能取决于它与其他表的连接方式:this SO question可能是一个有用的参考。