DISTINCT
多くの場合、内部から腐敗しているクエリを修復するために適用されます。これは、多くの場合、低速であるか、正しくありません。最初に行を乗算しないでください。そうすれば、最後に不要な重複を分類する必要がなくなります。
複数のnテーブル(「多数ある」)に一度に結合すると、結果セットの行が乗算されます。これはCROSS JOIN
のようなものです またはデカルト積プロキシによる :
- 2つのSQLLEFTJOINSが誤った結果を生成する
この間違いを回避するにはさまざまな方法があります。
最初に集約し、後で参加する
技術的には、 one に参加している限り、クエリは機能します 集計する前に一度に複数の行を持つテーブル:
SELECT e.id, e.name, e.age, e.streets, arrag_agg(wd.day) AS days
FROM (
SELECT e.id, e.name, e.age, array_agg(ad.street) AS streets
FROM employees e
JOIN address ad ON ad.employeeid = e.id
GROUP BY e.id -- id enough if it is defined PK
) e
JOIN workingdays wd ON wd.employeeid = e.id
GROUP BY e.id, e.name, e.age;
主キーid
を含めることもお勧めします およびGROUP BY
name
だからです およびage
必ずしも一意ではありません。誤って2人の従業員をマージする可能性があります。
ただし、 beforeのサブクエリで集計できます。 あなたが参加する、あなたが選択的なWHERE
を持っていない限り、それは優れています employees
の条件 :
SELECT e.id, e.name, e.age, ad.streets, arrag_agg(wd.day) AS days
FROM employees e
JOIN (
SELECT employeeid, array_agg(ad.street) AS streets
FROM address
GROUP BY 1
) ad ON ad.employeeid = e.id
JOIN workingdays wd ON e.id = wd.employeeid
GROUP BY e.id, e.name, e.age, ad.streets;
または両方を集約します:
SELECT name, age, ad.streets, wd.days
FROM employees e
JOIN (
SELECT employeeid, array_agg(ad.street) AS streets
FROM address
GROUP BY 1
) ad ON ad.employeeid = e.id
JOIN (
SELECT employeeid, arrag_agg(wd.day) AS days
FROM workingdays
GROUP BY 1
) wd ON wd.employeeid = e.id;
最後の1つは、通常、すべてまたはほとんどを取得した場合に高速になります ベーステーブルの行の数。
JOIN
を使用することに注意してください LEFT JOIN
ではありません 住所がない従業員を結果から削除しますまたは 営業日はありません。それは意図されているかもしれないし、意図されていないかもしれない。 LEFT JOIN
に切り替えます すべてを保持する 結果の従業員。
関連するサブクエリ/LATERAL結合
小さな選択の場合 、代わりに相関サブクエリを検討します:
SELECT name, age
, (SELECT array_agg(street) FROM address WHERE employeeid = e.id) AS streets
, (SELECT arrag_agg(day) FROM workingdays WHERE employeeid = e.id) AS days
FROM employees e
WHERE e.namer = 'peter'; -- very selective
または、Postgres 9.3以降では、LATERAL
を使用できます。 そのために参加します:
SELECT e.name, e.age, a.streets, w.days
FROM employees e
LEFT JOIN LATERAL (
SELECT array_agg(street) AS streets
FROM address
WHERE employeeid = e.id
GROUP BY 1
) a ON true
LEFT JOIN LATERAL (
SELECT array_agg(day) AS days
FROM workingdays
WHERE employeeid = e.id
GROUP BY 1
) w ON true
WHERE e.name = 'peter'; -- very selective
- LATERALとPostgreSQLのサブクエリの違いは何ですか?
どちらのクエリもすべてを保持します 結果の従業員。