ROW_NUMBER()示出了意外的值(ROW_NUMBER() shows unexpected

2019-10-17 03:24发布

我的表中有值一样( 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我得到的是正确的最值的,但错误的一些值。 例如:

  • ID 1699100价格58.13 - 计数应1(示出3)。
  • ID 1699104价格57.96 - 计数应1(示出2)。
  • ID 1699105,1699106价格57.93 - 计数应为1,2(图2,3)。

我试图在PostgreSQL中相同的查询,发现相同的结果。
我已上载这里CSV数据样本 。

我坚持分区的这种意想不到的结果。 有谁能够帮助我?

Answer 1:

PARTITION BY的条款ROW_NUMBER()函数指示它整个行通过设置划分Price值并分配行号中的升序ID秒。

好像你要使用相同的任何行的两组区分Price值由至少一排用不同的分离 Price

可能有不同的方式来实现这一目标。 在SQL Server(和我想的一样将在PostgreSQL的工作太),我先用两个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小提琴演示 。



Answer 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 。 (第一行的特殊情况也可实现此。)
  2. 综上所述步骤,以使得相同的价格在序列中的相同的组结束grp
  3. 每组的行数。

老实说,我认为@舍甫琴科的解决方案更优雅一丁点。 它需要三个窗口的功能,太多,但只能在两个查询的步骤去做。 在小样本的快速测试也稍快。 所以,从+1我。

如果性能是至关重要的,以更专业的解决方案

PL / pgSQL函数

应该是相当快的,因为它只需要扫描和订购表一次。

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 datetime_trans timets_trans timestamp

这是非常简单,非常快,提取datetimetimestamp与投:

ts_trans::date
ts_trans::time

关于日期/时间类型的手册。



Answer 3:

  • 1699100价格58.0 - 正显示出3因为1699097,8是1,2-

  • 1699104价格57.96 - 正显示出2,因为1669101是1。

  • 1699105,1699106价格57.93 - 表示2,3,因为1699103是1

如果你想找到一个序列相同价值的物品,一种选择是将数据加入到以前的ID,看看是否值是相同的



Answer 4:

从我可以通过你的结果expections收集,你需要分区在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

我相信这是情况下,你希望到时反价值的变动,你通过数据进步的ROW_NUMBER重新开始。

你可能也想在那里添加Date_trans太多,如果有可能是在餐桌上,这是我所期望的多个日期。



文章来源: ROW_NUMBER() shows unexpected values