考えられるすべてのインデックスがあるにもかかわらず、クエリに時間がかかりすぎていました。
最終的に、私はJOIN .. ON
クエリ内で列のコンテンツを別のデータ型にキャストしていました。テーブルA
の列はvarchar
型であり、テーブルB
の一致する列はinteger
型であるためです。
コードを変更して、テーブルA
から必要な行を挿入し、列をvarchar
からinteger
にキャストする一時テーブルを使用するように変更しました。
クエリ速度が約1000倍に向上しました。
表zone_site
:
CREATE TABLE traitements.zones_sites (
pkid serial PRIMARY KEY,
pkid_site integer NOT NULL,
origine varchar(50) NOT NULL,
origine_id varchar(255) NOT NULL, -- can't be converted to int
catégorie varchar(255) NOT NULL,
horodatage timestamp NOT NULL,
geom geometry(MultiPolygon,2154),
précision_contour varchar(100),
statut varchar(100),
détails_jsonb text,
CONSTRAINT enforce_dims_geomloc CHECK (ST_NDims(geom) = 2)
);
CREATE INDEX zones_sites_idx_pkid_site ON traitements.zones_sites (pkid_site);
CREATE INDEX zones_sites_géométrie ON traitements.zones_sites USING Gist (geom);
CREATE INDEX zones_sites_précision_contour ON traitements.zones_sites (précision_contour);
CREATE INDEX zones_sites_idx_catégorie ON traitements.zones_sites (catégorie);
CREATE INDEX zones_sites_idx_origine ON traitements.zones_sites (origine);
CREATE INDEX zones_sites_idx_statut ON traitements.zones_sites (statut);
ALTER TABLE traitements.zones_sites
ADD CONSTRAINT zones_sites_references_sites_candidats FOREIGN KEY (pkid_site)
REFERENCES traitements.sites_candidats(pkid) ON DELETE CASCADE;
Dba.SEに関する関連質問:
残念ながら、列traitements.zones_sites.origine_id
は、integer
型にすることはできません。これは、いくつかの発信元からの識別子を保持しているためです。
元のクエリ:
SELECT emprises.pkid, emprises.pkid_site, emprises.origine, parcelles.idpar
FROM traitements.zones_sites AS emprises
JOIN parcelles ON parcelles.idpk::varchar = emprises.origine_id
WHERE emprises.catégorie = 'emprise_site'
AND emprises.précision_contour = 'contour_inconnu'
AND emprises.origine = 'xxxxxxxxx';
変更されたコード:
CREATE TEMPORARY TABLE temp_emprises (
pkid serial PRIMARY KEY,
pkid_site integer NOT NULL,
origine varchar(50) NOT NULL,
idpk_parcelle integer NOT NULL
);
INSERT INTO temp_emprises (pkid, pkid_site, origine, idpk_parcelle)
SELECT pkid, pkid_site, origine, origine_id::integer
FROM traitements.zones_sites AS emprises
WHERE emprises.catégorie = 'emprise_site'
AND emprises.précision_contour = 'contour_inconnu'
AND emprises.origine = 'xxxxxxxxx';
CREATE INDEX ON temp_emprises(idpk_parcelle);
SELECT emprises.pkid, emprises.pkid_site, emprises.origine, parcelles.idpar
FROM temp_emprises AS emprises
JOIN parcelles ON parcelles.idpk = emprises.idpk_parcelle;
より良い解決策はありますか?
あなたのコメント(あなたの質問に追加しました)は、問題の根本を開示しています:
残念ながら、列_
traitements.zones_sites.origine_id
_はinteger
型にすることはできません。これは、いくつかの起点からの識別子を保持しているため、一部が整数ではないためです。
おおよそ正規化されたDB設計は、同じ列に異なる種類のデータを混在させることを避けます。次に、真の整数値をinteger
タイプの列に入れ、残りを(a)text
列に入れることができます。そもそも問題はありません。
あなたが主張したように、列_origine_id
_は、クエリ内の行の選択のために整数にキャストできます、私はこれより速い代替案を提案します:partial expression index:
_CREATE INDEX zones_sites_idx_origine_part_int ON traitements.zones_sites ((origine_id::int))
WHERE catégorie = 'emprise_site'
AND précision_contour = 'contour_inconnu'
AND origine = 'xxxxxxxxx';
_
INSERT
のtempテーブルへのキャストが機能することが保証されている場合、このインデックスも機能します。
少し変更されたクエリ:
_SELECT z.pkid, z.pkid_site, z.origine, p.idpar
FROM traitements.zones_sites z
JOIN parcelles p ON p.idpk = z.origine_id::int
WHERE z.catégorie = 'emprise_site'
AND z.précision_contour = 'contour_inconnu'
AND z.origine = 'xxxxxxxxx';
_
部分インデックスもクエリを完全にサポートするため、現在のソリューションは比較してスローモーションのように見えるはずです。また、シンプルで信頼性も高くなっています。
他に必要な唯一のインデックスは、parcelles(idpk)
のインデックスです。parcelles
から必要な他の唯一の列はidpar
であるため、そのテーブルが大きく、書き込み負荷が低いため、 index-only scans それ以外の場合、parcelles(idpk, idpar)
の(追加の)複数列インデックスにより、さらに高速になります。
さらに高速ですが、次の INCLUDE
列を持つ真のカバーするインデックスを持つPostgres 11 :
コード例と関連する回答:
一時テーブルを作成しなかった場合でも、それがどれほど速くなるか想像してみてください。
SELECT emprises.pkid, emprises.pkid_site , emprises.origine, parcelles.idpar
FROM traitements.zones_sites AS emprises
JOIN parcelles
ON parcelles.idpk = emprises.origine_id::integer
WHERE emprises.catégorie = 'emprise_site'
AND emprises.précision_contour = 'contour_inconnu'
AND emprises.origine = 'xxxxxxxxx'