ウィンドウ関数lead()
を使用できます:
SELECT dt_lead7 AS dt
FROM (
SELECT *, lead(dt, 7) OVER (ORDER BY dt) AS dt_lead7
FROM foo
) d
WHERE dt <= now()::date
ORDER BY dt DESC
LIMIT 11;
多少短いですがUNION ALL
、適切なインデックスを使用するとバージョンが高速になります。
これにより、「今日に最も近い日付」が最初の 7 行内にあるというコーナー ケースが残ります。これを処理するために、元のデータに の 7 行を埋め込むことができます-infinity
。
SELECT d.dt_lead7 AS dt
FROM (
SELECT *, lead(dt, 7) OVER (ORDER BY dt) AS dt_lead7
FROM (
SELECT '-infinity'::date AS dt FROM generate_series(1,7)
UNION ALL
SELECT dt FROM foo
) x
) d
WHERE d.dt <= now()::date -- same as: WHERE dt <= now()::date1
ORDER BY d.dt_lead7 DESC -- same as: ORDER BY dt DESC 1
LIMIT 11;
何が起こるかを明確にするために、2 番目のクエリの列をテーブル修飾しました。下記参照。「今日に最も近い日付」がベース テーブルの最後の 7 行以内にある場合、
結果には値が含まれます。NULL
必要に応じて、サブセレクトを追加してそれらをフィルタリングできます。
1コメント内の出力名と列名に関する疑問に対処するには、マニュアルからの次の引用を検討してください。
出力列の名前を使用する場所:
出力列の名前は、
ORDER BY
andGROUP BY
句で列の値を参照するために使用できますが、 or句では使用できません。WHERE
HAVING
代わりに式を書き出す必要があります。
大胆強調鉱山。同じ名前の出力列ではなく、WHERE dt <= now()::date
列を参照するため、意図したとおりに機能します。d.dt
競合の解決:
ORDER BY
式が出力列名と入力列名の両方に一致する単純な名前である場合、ORDER BY
はそれを出力列名として解釈します。GROUP BY
これは、同じ状況で行われる選択の反対です。この矛盾は、SQL 標準との互換性を保つために行われています。
再び大胆な強調鉱山。ORDER BY dt DESC
この例では、意図したとおりに出力列の名前を参照しています。とにかく、どちらの列も同じようにソートされます。唯一の違いは、コーナー ケースの値にある可能性があります。NULL
しかし、それも横ばいです。
デフォルトの動作はNULLS LAST
、ASC
が指定または暗示された場合、およびが指定されNULLS FIRST
た場合です。DESC
値は最大値の後にNULL
来るため、順序はどちらの方法でも同じです。
または、なしでLIMIT
(コメントのリクエストに従って):
WITH x AS (
SELECT *
, row_number() OVER (ORDER BY dt) AS rn
, first_value(dt) OVER (ORDER BY (dt > '2011-11-02')
, dt DESC) AS dt_nearest
FROM foo
)
, y AS (
SELECT rn AS rn_nearest
FROM x
WHERE dt = dt_nearest
)
SELECT dt
FROM x, y
WHERE rn BETWEEN rn_nearest - 3 AND rn_nearest + 7
ORDER BY dt;
パフォーマンスが重要な場合でも、@Clodoaldo のUNION ALL
バリアントを使用します。最速になります。データベースに依存しない SQL は、これまでのところしか役に立ちません。他の RDBMS にはまだウィンドウ関数がまったくない (MySQL) か、別の関数名 (のfirst_val
代わりになどfirst_value
) があります。(MS SQL)またはローカルの方言LIMIT
に置き換えることもできます。TOP n