1

私のテーブルには次のような値があります(RowCount以下のクエリによって生成されます):

ID       Date_trans   Time_trans  Price  RowCount
-------  -----------  ----------  -----  --------
1699093  22-Feb-2011  09:30:00    58.07  1
1699094  22-Feb-2011  09:30:00    58.08  1
1699095  22-Feb-2011  09:30:00    58.08  2
1699096  22-Feb-2011  09:30:00    58.08  3
1699097  22-Feb-2011  09:30:00    58.13  1
1699098  22-Feb-2011  09:30:00    58.13  2
1699099  22-Feb-2011  09:30:00    58.12  1
1699100  22-Feb-2011  09:30:08    58.13  3
1699101  22-Feb-2011  09:30:09    57.96  1
1699102  22-Feb-2011  09:30:09    57.95  1
1699103  22-Feb-2011  09:30:09    57.93  1
1699104  22-Feb-2011  09:30:09    57.96  2
1699105  22-Feb-2011  09:30:09    57.93  2
1699106  22-Feb-2011  09:30:09    57.93  3
1699107  22-Feb-2011  09:30:37    58     1
1699108  22-Feb-2011  09:30:37    58.08  4
1699109  22-Feb-2011  09:30:38    58.08  5
1699110  22-Feb-2011  09:30:41    58.02  1
1699111  22-Feb-2011  09:30:41    58.02  2
1699112  22-Feb-2011  09:30:41    58.01  1
1699113  22-Feb-2011  09:30:41    58.01  2
1699114  22-Feb-2011  09:30:41    58.01  3
1699115  22-Feb-2011  09:30:42    58.02  3
1699116  22-Feb-2011  09:30:42    58.02  4
1699117  22-Feb-2011  09:30:45    58.04  1
1699118  22-Feb-2011  09:30:54    58     2
1699119  22-Feb-2011  09:30:57    58.05  1

ID列はIDENTITY列です。
そして、私はこのクエリを使用して、連続する行数を次のように取得しています。

  SELECT   ID, Date_trans, Time_trans, Price
          ,ROW_NUMBER() OVER(PARTITION BY Price  ORDER BY ID) RowCount
  FROM     MyTable
  ORDER    BY ID;

私が得るものはほとんどのRowCount値に対して正しいが、いくつかの値に対しては間違っている。例えば:

  • ID1699100価格58.13–カウントは1である必要があります(3を表示)。
  • ID1699104価格57.96–カウントは1である必要があります(2を表示)。
  • ID 1699105、1699106価格57.93 –カウントは1、2(2、3を表示)である必要があります。

PostgreSQLで同じクエリを試しましたが、同じ結果が見つかりました。ここにcsvデータサンプル
をアップロードしました。

私はパーティションのそのような予期しない結果で立ち往生しています。誰か助けてもらえますか?

4

4 に答える 4

4

関数のPARTITION BY句は、行セット全体ROW_NUMBER()を値で分割し、 s の昇順で行番号を割り当てるように指示します。PriceID

Price異なる. _ _ _Price

それを達成するためのさまざまな方法があるかもしれません。SQL Server では (PostgreSQL でも同じことができると思います)、最初に 2 つのROW_NUMBER()呼び出しを使用して追加のパーティショニング基準を取得し、次にその基準を使用して行をもう一度ランク付けします。次のようにします。

WITH partitioned AS (
  SELECT
    ID,
    Date_trans,
    Time_trans,
    Price,
    ROW_NUMBER() OVER (                   ORDER BY ID) -
    ROW_NUMBER() OVER (PARTITION BY Price ORDER BY ID) AS PriceGroup
  FROM MyTable
)
SELECT
  ID,
  Date_trans,
  Time_trans,
  Price,
  ROW_NUMBER() OVER (PARTITION BY Price, PriceGroup ORDER BY ID) AS RowCount
FROM partitioned
ORDER BY ID
;

これはSQL Fiddle のデモです。

于 2012-08-05T19:35:12.200 に答える
2

純粋なSQL

WITH x AS (
    SELECT id, date_trans, time_trans, price
         ,(price <> lag(price) OVER (ORDER BY id))::int AS step
    FROM   tbl
    )
    ,y AS (
    SELECT *, sum(step) OVER (ORDER BY id) AS grp
    FROM   x
    )
SELECT id, date_trans, time_trans, price
      ,row_number() OVER (PARTITION BY grp ORDER BY id) As row_ct
FROM   y
ORDER  BY id;

ロジック:

  1. の最後の行と比較して価格がいつ変わるかを覚えておいてくださいstep。(1列目の特殊ケースも動作します。)
  2. ステップを合計して、同じ価格が順番に同じグループに含まれるようにしますgrp
  3. グループごとの行数。

正直なところ、 @Andriyのソリューションはもう少しエレガントだと思います。3つのウィンドウ関数も必要ですが、2つのクエリステップでしか実行できません。小さなサンプルでの簡単なテストでも、わずかに高速でした。だから、私から+1。

パフォーマンスが重要な場合は、

PL/pgSQL関数

テーブルをスキャンして注文する必要があるのは1回だけなので、かなり高速になるはずです。

CREATE OR REPLACE FUNCTION f_my_row_ct()
  RETURNS TABLE (
    id         int
   ,date_trans date
   ,time_trans time
   ,price      numeric
   ,row_ct     int
  ) AS
$BODY$
DECLARE
   _last_price numeric;   -- remember price of last row
BEGIN

FOR id, date_trans, time_trans, price IN 
   SELECT t.id, t.date_trans, t.time_trans, t.price
   FROM   tbl t
   ORDER  BY t.id
LOOP
   IF _last_price = price THEN   -- works with 1st row, too
      row_ct := row_ct + 1;
   ELSE
      row_ct := 1;
   END IF;

   RETURN NEXT;
   _last_price = price;   -- remember last price
END LOOP;

END;
$BODY$  LANGUAGE plpgsql;

電話:

SELECT * FROM f_my_row_ct()

小さなサンプルでの別のクイックテストでは、これは3〜4倍高速でした。でテストしEXPLAIN ANALYZEて確認してください。


余談ですが、とをマージすることで、テーブル(およびクエリ)を簡素化し、ストレージのバイト数を節約date_trans dateできtime_trans timeますts_trans timestamp

キャストを使用して、dateまたはtimeから抽出するのは非常に簡単で非常に高速です。timestamp

ts_trans::date
ts_trans::time

日付/時刻タイプに関するマニュアル。

于 2012-08-05T23:30:18.663 に答える
0
  • 1699100価格58.0-1699097,8が1,2であるため、3を表示しています

  • 1699104価格57.96– 1669101が1であるため、2を表示しています。

  • 1699105、1699106価格57.93 – 1699103は1であるため、2、3を表示

シーケンス内で同じ値のアイテムを検索する場合、1つのオプションは、データを前のIDに結合し、値が同じであるかどうかを確認することです。

于 2012-08-05T19:22:22.527 に答える
0

結果の期待から私が収集できることから、Time_transにもパーティションを作成する必要があります。

  SELECT   ID, Date_trans, Time_trans, Price
           ,ROW_NUMBER() OVER(PARTITION BY Time_trans, Price ORDER BY ID) RowCount
  FROM     MyTable
  ORDER BY ID

これは、データの進行に伴ってTime-trans値が変化したときに、ROW_NUMBERが再開することを期待している場合に当てはまると思います。

また、テーブルに複数の日付が存在する可能性がある場合は、そこにもDate_transを追加することをお勧めします。

于 2012-08-05T19:23:40.607 に答える