範囲内のすべての日付に対して複雑なクエリを実行する


9

注文表があります

   Column   |            Type             |                      Modifiers                      
------------+-----------------------------+-----------------------------------------------------
 id         | integer                     | not null default nextval('orders_id_seq'::regclass)
 client_id  | integer                     | not null
 start_date | date                        | not null
 end_date   | date                        | 
 order_type | character varying           | not null

データにはclient_idの重複しない永続的な注文があり、時にはclient_idが一致する場合、start_dateの永続的な注文を上書きする一時的な注文があります。同じタイプの注文が重複しないようにするアプリケーションレベルの制約があります。

 id | client_id | start_date |  end_date  | order_type 
----+-----------+------------+------------+------------
 17 |        11 | 2014-02-05 |            | standing
 18 |        15 | 2014-07-16 | 2015-07-19 | standing
 19 |        16 | 2015-04-01 |            | standing
 20 |        16 | 2015-07-18 | 2015-07-18 | temporary

たとえば、2015-07-18クライアント16では、注文#20がアクティブな注文であるため、注文#19を上書きします。いくつかの大騒ぎで、日付のアクティブな注文IDを照会する効率的な方法を見つけました。

    SELECT id from (
      SELECT
        id,
        first_value(id) OVER (PARTITION BY client_id ORDER BY order_type DESC) active_order_id
      FROM orders
      WHERE start_date <= ? and (end_date is null OR end_date >= ?)
    ) active_orders
    WHERE id = active_order_id

これを2015-07-18プレースホルダーとしてクエリすると、次のようになります。

 id 
----
 17
 18
 20

このクエリのクエリプランは、他のいくつかのアイデア(ある日付のクライアントの一時注文の数をカウントするサブクエリなど)と比較すると非常に小さく、かなり満足しています。(テーブルのデザイン、私は興奮していません)

今、私はそれらがアクティブな日付と結合された日付範囲のすべてのアクティブな注文を見つける必要があります。たとえば、日付範囲が2015-07-18toの2015-07-19場合、次の結果が必要です。

active_date | id 
------------+----
 2015-07-18 | 17
 2015-07-18 | 18
 2015-07-18 | 20
 2015-07-19 | 17
 2015-07-19 | 18
 2015-07-19 | 19

注文20は注文19をオーバーライドします2015-07-18が、オンではありません2015-07-19

generate_series()日付の範囲を生成できることがわかりましたが、日付と注文IDのテーブルを取得するためにこれをどのように結合するのかわかりません。私の直感はクロスジョインですが、この状況でそれを機能させる方法を理解できません。

ありがとう

UPDATE SQLフィドルが 追加されました。


2
データの例をいくつか示していただけますか?このアクティブ/非アクティブおよび一時的なものは、最初の読み取り後はあまり明確ではありません。
dezso 2015

はい、明確ではありません。クエリはクライアントごとに1つの注文を見つけ、確定的ではないようです。同じタイプのクライアントに2つ以上の注文がある場合、2つのうちどちらが返されるかは任意であり、実行ごとに異なります。したがって、テーブルにいくつかの制約を設定していないか、クエリが正しくありません。
ypercubeᵀᴹ

質問をより多くの詳細で更新しましたが、データに制約があります。
2015

回答:


5

私はselect distinct onウィンドウ関数の代わりに使用し、それから日々に参加します。

select 
    distinct on (date, client_id) date, 
    id 
from orders
inner join generate_series('2015-07-18'::date, '2015-07-19'::date, '1 day') date
  on start_date <= date and (end_date is null or date <= end_date)
order by date, client_id, order_type desc

http://sqlfiddle.com/#!15/5a420/16/0

不明な点がある場合は、さらに詳しく説明します。


これは一時的な注文/常設注文はカバーしていませんが、結合後に行うことができます=)
reconbot 2015

これは、ウィンドウクエリと同じ順序を指定します。したがって、(date、client_id)の場合、最初のorder_typeがアルファベットの逆順に選択されます。
Simon Perepelitsa

内部結合は完全であり、個別選択はウィンドウよりもはるかに理解しやすく(そしてほぼ同様に機能します)します。ウィンドウ関数を使用してはならないその他の理由は何ですか?
2015

1
それだけです。distinct onウィンドウクエリよりもさらに最適化されていると思います。:ところで、私は、これは一般的なSQLの「トップ・イン・グループ」の問題であることを言及する必要がありstackoverflow.com/questions/3800551/...
サイモンPerepelitsaは、

それは素晴らしい読み物です、私はやるべきことを勉強しています。時間があれば、ここで学んだことを使ったこの質問の拡張版を持っています。dba.stackexchange.com/questions/108767/…そのリンクから学んだことで更新するために戻ってくると確信しています。そしてありがとう
再コンボ2015

0

パラメータとして単一の日付を取り、日付と日付のリストを返す関数を記述します。

次に、提案どおりにgenerate_seriesを使用し、日付範囲に対して関数を呼び出します。

これは、SQLで複雑な条件を処理する場合の一般的な戦略です。

以下にいくつかのコードを含めましたが、上記のSQLの答えははるかに簡単です。

これが関数です:

create or replace function o( date) returns setof INT AS '
SELECT id from (
 SELECT
  id,
  first_value(id) OVER (PARTITION BY client_id ORDER BY order_type DESC) active_order_id
 FROM orders
 WHERE start_date <= $1 and (end_date is null OR end_date >= $1)
) active_orders
WHERE id = active_order_id;
' LANGUAGE sql ;

そしてそれを呼び出す方法:

select distinct d, o(d::date) 
from generate_series('2015-07-18'::date, '2015-07-19'::date, '1 day') as d;

SQLFiddle


2
いくつかの詳細、サンプルコードなどでその回答をフラッシュしたい場合があります。この回答は、かなり曖昧であるため削除される可能性があります。
Max Vernon

私のフィドルを例で更新できますか?sqlfiddle.com/#!15/5a420/3/0
リコンボット2015

いくつかのコードを含めるように回答を更新しましたが、上記の回答の方が簡単です。
Don Drake
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.