3

Postgres9.1データベースで実行している2つの異なる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秒(ORで区切られた3620句)

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秒(ORで区切られた3620句)

最初のものでは、国の条件はすべての条項で同じです。2つ目では、最後の句で国を「異なる」に切り替えました。

最初のselectステートメントの実行には12秒かかり、2番目のselectステートメントの実行には0.6秒かかります。

最初のクエリでは、ディスクの読み取りが行われずに、CPUが12秒間のほぼすべてで100%に固定されています。これは、非常に時間がかかるパーサーである可能性が高いことを示しています。これは、2番目のクエリでは発生しません。

EXPLAIN ANALYZEを実行したところ、両方のクエリがどのように分類されたかについて、まったく同じ結果が得られました。

ここで何が起こっているのですか?各WHERE句ステートメントで2番目の条件が同じであると、クエリ時間が大幅に長くなるのはなぜですか。

編集:

最初のクエリからのEXPLAINANALYZEの最初の数行:

デバイスでのビットマップヒープスキャン(コスト=18807.49..52584.74行=3564幅=39)(実際の時間=73.994..78.994行=3620ループ=1)

Condを再確認します:(((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番目のクエリからのEXPLAINANALYZEの最初の数行:

デバイスでのビットマップヒープスキャン(コスト=18806.59..85317.68行=3563幅=39)(実際の時間=74.737..79.769行=3619ループ=1)

Condを再確認します:(((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:

EXPLAINANALYZEの2つの結果は次のとおりです。

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

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

4

1 に答える 1

6

パフォーマンスの違いの説明ではありませんが、この問題の最善の解決策は、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 またはing することをお勧めします。本当に巨大なデータ セットの場合は、.COPYVALUESdevice_id,country

于 2012-11-18T02:41:46.057 に答える