現在受け入れられている回答は質問に答えません。そして、それは原則として間違っています。 a BETWEEN x AND y
次のように変換されます:
a >= x AND a <= y
含む 上限ですが、人々は通常除外する必要があります それ:
a >= x AND a < y
日付 簡単に調整できます。 2009年には、上限として「2009-12-31」を使用します。
しかし、タイムスタンプではそれほど単純ではありません。 これは小数桁を許可します。最新のPostgresバージョンは、内部で8バイト整数を使用して、最大6小数秒(µsの解像度)を格納します。これを知っていると、できた それでも機能しますが、それは直感的ではなく、実装の詳細に依存します。悪い考えです。
さらに、a BETWEEN x AND y
重複する範囲は見つかりません。必要なもの:
b >= x AND a < y
そして、決して離れなかった まだ考慮されていません。
適切な回答
年を2009
と仮定します 、意味を変えずに質問を言い換えます:
「2010年より前に参加し、2009年より前に退場しなかった特定のチームのすべてのプレーヤーを検索します。」
基本的なクエリ:
SELECT p.*
FROM team t
JOIN contract c USING (name_team)
JOIN player p USING (name_player)
WHERE t.name_team = ?
AND c.date_join < date '2010-01-01'
AND c.date_leave >= date '2009-01-01';
しかし、それだけではありません:
参照整合性がFK制約で適用される場合、テーブルteam
それ自体はクエリの単なるノイズであり、削除できます。
同じプレーヤーが同じチームを離れて再参加することはできますが、たとえばDISTINCT
を使用して、重複する可能性のあるものを折りたたむ必要もあります。 。
そして、私たちはかもしれません 特別な場合を提供する必要があります:決して去ったことのないプレーヤー。それらのプレーヤーがdate_leave
でNULLを持っていると仮定します 。
「去ったことが知られていないプレーヤーは、今日までチームのためにプレーしていると想定されます。」
洗練されたクエリ:
SELECT DISTINCT p.*
FROM contract c
JOIN player p USING (name_player)
WHERE c.name_team = ?
AND c.date_join < date '2010-01-01'
AND (c.date_leave >= date '2009-01-01' OR c.date_leave IS NULL);
演算子の優先順位は、AND
に対して機能します OR
の前にバインドします 。かっこが必要です。
最適化されたDISTINCT
に関連する回答 (重複が一般的である場合):
- 多対多のテーブル-パフォーマンスが悪い
通常、名前 自然人の数は一意ではなく、代理の主キーが使用されます。しかし、明らかに、name_player
player
の主キーです 。必要なのがプレーヤー名だけの場合、テーブルplayer
は必要ありません。 クエリでは、次のいずれかです。
SELECT DISTINCT name_player
FROM contract
WHERE name_team = ?
AND date_join < date '2010-01-01'
AND (date_leave >= date '2009-01-01' OR date_leave IS NULL);
SQL OVERLAPS
オペレーター
マニュアル:
OVERLAPS
ペアの以前の値を開始として自動的に取得します。各期間は、半分開いた間隔のstart <= time < end
を表すと見なされます 、start
でない限り およびend
等しい場合、それはその単一の瞬間を表します。
潜在的なNULL
を処理する 値、COALESCE
最も簡単なようです:
SELECT DISTINCT name_player
FROM contract
WHERE name_team = ?
AND (date_join, COALESCE(date_leave, CURRENT_DATE)) OVERLAPS
(date '2009-01-01', date '2010-01-01'); -- upper bound excluded
インデックスをサポートする範囲タイプ
Postgresの場合9.2以降 実際の範囲タイプで操作することもできます :
SELECT DISTINCT name_player
FROM contract
WHERE name_team = ?
AND daterange(date_join, date_leave) &&
daterange '[2009-01-01,2010-01-01)'; -- upper bound excluded
範囲タイプはオーバーヘッドを追加し、より多くのスペースを占有します。 2xdate
=8バイト; 1 x daterange
=ディスクで14バイト、またはRAMで17バイト。ただし、重複演算子&&
と組み合わせて クエリはGiSTインデックスでサポートできます。
また、NULL値を特殊な場合にする必要はありません。 NULLは、範囲タイプの「オープンレンジ」を意味します。まさに必要なものです。テーブル定義を変更する必要はありません。範囲タイプをその場で作成でき、一致する式インデックスを使用してクエリをサポートできます:
CREATE INDEX mv_stock_dr_idx ON mv_stock USING gist (daterange(date_join, date_leave));
関連:
- 平均在庫履歴テーブル