ROW_NUMBER()显示意外值

时间:2012-08-05 19:07:49

标签: sql sql-server aggregate-functions window-functions

我的表格的值类似于(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中尝试了相同的查询并找到了相同的结果 我上传了a csv data sample here

我遇到了这种意想不到的分区结果。有人能帮助我吗?

4 个答案:

答案 0 :(得分:4)

PARTITION BY函数的ROW_NUMBER()子句指示它按Price值对整个行集进行分区,并按{的升序分配行号。 {1}}秒。

您似乎希望区分具有相同ID值的任意两组行,这些值分隔至少一行具有不同的Price

可能有多种方法可以实现这一目标。在SQL Server中(我认为同样适用于PostgreSQL),我首先使用两个Price调用来获得额外的分区标准,然后再使用该标准对行进行排名,如下所示:

ROW_NUMBER()

这是SQL Fiddle demo

答案 1 :(得分: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. 每组的行数。
  4. 老实说,我认为@Andriy's solution更优雅一点。它也需要三个窗口函数,但只能在两个查询步骤中完成。在对小样品进行快速测试时,它也略快一些。所以,+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 time合并到ts_trans timestamp来简化您的表(和查询)并节省一些存储空间。

    使用强制转型从date中提取timetimestamp非常简单,速度非常快:

    ts_trans::date
    ts_trans::time
    

    The manual about date/time types.

答案 2 :(得分:0)

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

  • 1699104价格57.96 - 显示2,因为1669101是1.

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

如果要在序列中查找具有相同值的项目,一个选项是将数据连接到先前的ID并查看值是否相同

答案 3 :(得分: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,这是我期望的。