web-dev-qa-db-ja.com

PostgreSQLで指定されたIDを参照する行を検索する最良の方法

行を削除するときに外部キー制約に違反するものがあるかどうかを調べて、削除できるかどうかを確認しています(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万行が分割されます)、どちらを使用するのが適切ですか?つまり、どちらの方がより適切にスケーリングできますか?

3
bellini666

最初のオプション、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の関連回答:

2