行を削除するときに外部キー制約に違反するものがあるかどうかを調べて、削除できるかどうかを確認しています(ON DELETE CASCADE
は使用できません)。
そのために、私はその行のIDを参照するすべての(テーブル、列)のリストを提供するコードを用意しています。次に、知りたいのは、それらのいずれか(テーブル、列)に行のIDがあるかどうかだけです。これにより、削除できなくなります。
私はそれを行う3つの方法を見つけ、非常に大規模なデータベースのEXPLAIN ANALYZEですべて同じような結果を生成しました。ここにいくつかの実際の例を貼り付けます:
1つ目は、それぞれ(テーブル、列)のUNIONを作成し、IDがあるかどうかを確認することです(最後にnullの場合、参照はありません)。
(SELECT client_category_price.sellable_id
FROM client_category_price
WHERE client_category_price.sellable_id = '9bc202ca-f7c1-11e2-a751-062b1fc90460' LIMIT 1)
UNION
(SELECT commission_source.sellable_id
FROM commission_source
WHERE commission_source.sellable_id = '9bc202ca-f7c1-11e2-a751-062b1fc90460' LIMIT 1)
UNION
(SELECT loan_item.sellable_id
FROM loan_item
WHERE loan_item.sellable_id = '9bc202ca-f7c1-11e2-a751-062b1fc90460' LIMIT 1)
UNION
(SELECT product.sellable_id
FROM product
WHERE product.sellable_id = '9bc202ca-f7c1-11e2-a751-062b1fc90460' LIMIT 1)
UNION
(SELECT product_history.sellable_id
FROM product_history
WHERE product_history.sellable_id = '9bc202ca-f7c1-11e2-a751-062b1fc90460' LIMIT 1)
UNION
(SELECT purchase_item.sellable_id
FROM purchase_item
WHERE purchase_item.sellable_id = '9bc202ca-f7c1-11e2-a751-062b1fc90460' LIMIT 1)
UNION
(SELECT receiving_order_item.sellable_id
FROM receiving_order_item
WHERE receiving_order_item.sellable_id = '9bc202ca-f7c1-11e2-a751-062b1fc90460' LIMIT 1)
UNION
(SELECT returned_sale_item.sellable_id
FROM returned_sale_item
WHERE returned_sale_item.sellable_id = '9bc202ca-f7c1-11e2-a751-062b1fc90460' LIMIT 1)
UNION
(SELECT sale_item.sellable_id
FROM sale_item
WHERE sale_item.sellable_id = '9bc202ca-f7c1-11e2-a751-062b1fc90460' LIMIT 1)
UNION
(SELECT service.sellable_id
FROM service
WHERE service.sellable_id = '9bc202ca-f7c1-11e2-a751-062b1fc90460' LIMIT 1)
UNION
(SELECT stock_decrease_item.sellable_id
FROM stock_decrease_item
WHERE stock_decrease_item.sellable_id = '9bc202ca-f7c1-11e2-a751-062b1fc90460' LIMIT 1)
UNION
(SELECT transfer_order_item.sellable_id
FROM transfer_order_item
WHERE transfer_order_item.sellable_id = '9bc202ca-f7c1-11e2-a751-062b1fc90460' LIMIT 1)
UNION
(SELECT work_order.sellable_id
FROM work_order
WHERE work_order.sellable_id = '9bc202ca-f7c1-11e2-a751-062b1fc90460' LIMIT 1)
UNION
(SELECT work_order_item.sellable_id
FROM work_order_item
WHERE work_order_item.sellable_id = '9bc202ca-f7c1-11e2-a751-062b1fc90460' LIMIT 1)
2つ目は、クロス結合を作成し、同じIDを含む「大きなタプル」を返し、それらがすべてnullかどうかをテストすることです。
SELECT client_category_price.sellable_id, commission_source.sellable_id, loan_item.sellable_id, product.sellable_id, product_history.sellable_id, purchase_item.sellable_id,
receiving_order_item.sellable_id, returned_sale_item.sellable_id, sale_item.sellable_id, service.sellable_id, stock_decrease_item.sellable_id, transfer_order_item.sellable_id,
work_order.sellable_id, work_order_item.sellable_id
FROM sellable
LEFT JOIN client_category_price ON client_category_price.sellable_id = sellable.id
LEFT JOIN commission_source ON commission_source.sellable_id = sellable.id
LEFT JOIN loan_item ON loan_item.sellable_id = sellable.id
LEFT JOIN product ON product.sellable_id = sellable.id
LEFT JOIN product_history ON product_history.sellable_id = sellable.id
LEFT JOIN purchase_item ON purchase_item.sellable_id = sellable.id
LEFT JOIN receiving_order_item ON receiving_order_item.sellable_id = sellable.id
LEFT JOIN returned_sale_item ON returned_sale_item.sellable_id = sellable.id
LEFT JOIN sale_item ON sale_item.sellable_id = sellable.id
LEFT JOIN service ON service.sellable_id = sellable.id
LEFT JOIN stock_decrease_item ON stock_decrease_item.sellable_id = sellable.id
LEFT JOIN transfer_order_item ON transfer_order_item.sellable_id = sellable.id
LEFT JOIN work_order ON work_order.sellable_id = sellable.id
LEFT JOIN work_order_item ON work_order_item.sellable_id = sellable.id
WHERE sellable.id = '9bc202ca-f7c1-11e2-a751-062b1fc90460' LIMIT 1
データベースが非常に大きいため、LIMIT 1
を最後に配置しないと、クエリの実行に非常に長い時間がかかりますが、制限があるため、上記のUNIONと同じ時間がかかります。
そして3番目のオプションがあります。私は実際にこれをpythonから実行しています。そのため、これらのUNIONクエリのそれぞれでCOUNTを実行でき、そのうちの1つが0を超えるものを返した場合、行の参照が存在することはすでにわかっています。最良の場合は1つのCOUNTを実行し、最悪の場合はすべてのCOUNTSを実行します。
これらすべてのオプションは非常に重要ではない違いを生成するため(どちらも+/- 0.300ミリ秒かかり、関連するテーブル間で約100万行が分割されます)、どちらを使用するのが適切ですか?つまり、どちらの方がより適切にスケーリングできますか?
最初のオプション、2つの改善点といくつかの簡略化をお勧めします。
(
SELECT 1 -- irrelevant what you select here
FROM client_category_price
WHERE sellable_id = '9bc202ca-f7c1-11e2-a751-062b1fc90460'
LIMIT 1 -- may be redundant
)
UNION ALL -- not just UNION
...
UNION ALL
(
SELECT 1
FROM work_order_item
WHERE sellable_id = '9bc202ca-f7c1-11e2-a751-062b1fc90460'
LIMIT 1
)
LIMIT 1; -- this one is crucial
あなたが知りたいのは
それらのいずれか(テーブル、列)に行のIDが含まれていると、削除できなくなります。
違反している行の完全なリストは必要ありません。最初のもので検索を停止します。必要なのは、クエリの別のLIMIT 1
at the endを追加することだけです。このように、Postgresは最初の行が見つかるとすぐに残りのクエリをスキップします。おそらく、SELECT
ごとにLIMIT 1
は必要ありません。最後の1つだけです。テストせずに、異なるクエリプランを生成する場合があります。
UNION
の代わりにUNION ALL
を使用します。もっと早く。
他のいくつかの簡略化。
SOの関連回答: