【问题标题】:Why does a multiple WHERE clause SQL SELECT statement with two conditions in each clause where one condition has the same value take a long time?为什么在一个条件具有相同值的每个子句中有两个条件的多个 WHERE 子句 SQL SELECT 语句需要很长时间?
【发布时间】:2012-11-17 14:29:42
【问题描述】:

我在 Postgres 9.1 数据库上运行了两个不同的 SQL 查询:

SELECT device_id, country FROM devices WHERE
(device_id = '97c179bd' AND country = 'US') OR
(device_id = 'bf5f50c6' AND country = 'US') OR
....
(device_id = '0e66c04d' AND country = 'US')

12 秒运行(3620 个子句用 OR 分隔)

SELECT device_id, country FROM devices WHERE
(device_id = '97c179bd' AND country = 'US') OR
(device_id = 'bf5f50c6' AND country = 'US') OR
....
(device_id = '0e66c04d' AND country = 'US') OR
(device_id = '0e66c04d' AND country = 'different')

0.6 秒运行(3620 子句用 OR 分隔)

在第一个中,国家/地区条件在每个子句中都是相同的。在第二个中,我在最后一个子句中将国家/地区切换为“不同”。

第一个 select 语句需要 12 秒运行,第二个 select 语句需要 0.6 秒运行。

在第一个查询中,CPU 在几乎所有 12 秒内都固定在 100%,而没有任何磁盘读取,这表明它很可能是解析器花费了这么长时间。第二个查询不会发生这种情况。

我已经运行了 EXPLAIN ANALYZE 并获得了完全相同的结果来了解两个查询的分解方式。

这里发生了什么?为什么每个 WHERE 子句语句中的第二个条件相同会导致查询时间更长?

编辑:

EXPLAIN ANALYZE 来自第一个查询的前几行:

设备上的位图堆扫描(成本=18807.49..52584.74 行=3564 宽度=39)(实际时间=73.994..78.994 行=3620 循环=1)

重新检查条件:(((device_id = '97c179bd'::text) AND (country = 'US'::text)) OR ((device_id = 'bf5f50c6'::text) AND (country = 'US': :text)) OR ((device_id = '3b05d35a'::text) AND (country = 'US'::text)) OR ((device_id = 'c6684bc0'::text) AND (country = 'US'::text )) OR ((device_id = '0e66c04d'::text) AND (country = 'US'::text))

第二个查询中 EXPLAIN ANALYZE 的前几行:

设备上的位图堆扫描(成本=18806.59..85317.68 行=3563 宽度=39)(实际时间=74.737..79.769 行=3619 循环=1)

重新检查条件:(((device_id = '97c179bd'::text) AND (country = 'US'::text)) OR ((device_id = 'bf5f50c6'::text) AND (country = 'US': :text)) OR ((device_id = '3b05d35a'::text) AND (country = 'US'::text)) OR ((device_id = 'c6684bc0'::text) AND (country = 'US'::text )) OR ((device_id = '0e66c04d'::text) AND (country = 'US'::text))

编辑 2:

以下是两个 EXPLAIN ANALYZE 结果:

https://dl.dropbox.com/u/4747107/explain/query1slow.htm

https://dl.dropbox.com/u/4747107/explain/query2fast.htm

【问题讨论】:

  • 很高兴在这里看到您的EXPLAIN ANALYZE 输出。哪个 PostgreSQL 版本?..
  • PostgreSQL9.1.我刚刚添加了 EXPLAIN ANALYZE 的结果
  • 请将执行计划上传到explain.depesz.com 或至少正确格式化(使用<pre> 标签)以便保留缩进。
  • 前几行还不够,需要完整输出。
  • 解释分析表明两个查询花费的时间大致相同,不到 80 毫秒。你在哪里测量 12 秒?

标签: postgresql select


【解决方案1】:

虽然不能解释性能差异,但此问题的最佳解决方案是重构您的查询,不要使用超过 3000 个 OR 子句。这太可怕了。

代替:

SELECT device_id, country FROM devices WHERE
(device_id = '97c179bd' AND country = 'US') OR
(device_id = 'bf5f50c6' AND country = 'US') OR
....
(device_id = '0e66c04d' AND country = 'US')

写:

SELECT d.device_id, d.country
FROM devices d 
INNER JOIN (VALUES 
            ('97c179bd','US'),
            ('bf5f50c6','US'),
            ('0e66c04d','US')
) v(device_id,country) USING (device_id,country);

演示设置:

create table devices (device_id text, country text, primary key (device_id,country));

insert into devices values 
        ('97c179bd','US'),
        ('bf5f50c6','US'),
        ('0e66c04d','US'),('0e66c04d','different');

演示输出:

regress=>     SELECT d.device_id, d.country
    FROM devices d 
    INNER JOIN (VALUES 
                ('97c179bd','US'),
                ('bf5f50c6','US'),
                ('0e66c04d','US')
    ) v(device_id,country) USING (device_id,country);
 device_id | country 
-----------+---------
 97c179bd  | US
 bf5f50c6  | US
 0e66c04d  | US
(3 rows)

对于较大的值列表,可能值得创建一个临时表并将INSERTing 或COPYing 放入其中,而不是使用内联VALUES 列表。对于真正庞大的数据集,您可能会受益于在 device_id,country 上创建唯一索引。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-13
    • 2019-11-16
    • 1970-01-01
    相关资源
    最近更新 更多