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值对于大多数值是正确的,但对于某些值是错误的。例如:

  • ID 1699100 价格 58.13 – 计数应为 1(显示 3)。
  • ID 1699104 价格 57.96 – 计数应为 1(显示 2)。
  • ID 1699105、1699106 价格 57.93 – 计数应为 1、2(显示 2、3)。

我在 PostgreSQL 中尝试了相同的查询并发现了相同的结果。
我在这里上传了一个 csv 数据样本

我对分区的这种意外结果感到困惑。有谁能够帮助我?

4

4 回答 4

4

该函数的PARTITION BY子句ROW_NUMBER()指示它按值对整个行集进行分区,并按 sPrice的升序分配行号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 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。(第一行的特殊情况也可以。)
  2. 总结步骤,使顺序相同的价格最终在同一组grp中。
  3. 每组的行数。

老实说,我认为@Andriy 的解决方案更优雅一些。它也需要三个窗口函数,但只需两个查询步骤即可完成。在对小样本的快速测试中,它也稍微快了一点。所以,我+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 date空间。time_trans timets_trans timestamp

date提取或time从 a中提取非常简单且非常快速timestamp

ts_trans::date
ts_trans::time

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

于 2012-08-05T23:30:18.663 回答
0
  • 1699100 价格 58.0 - 显示 3 因为 1699097,8 是 1,2

  • 1699104 价格 57.96 – 显示为 2,因为 1669101 为 1。

  • 1699105、1699106 价格 57.93 – 显示 2、3,因为 1699103 是 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

我相信这是这种情况,因为您期望 ROW_NUMBER 在您处理数据时随着 Time-trans 值的变化而重新开始。

此外,如果表中可能有多个日期,您可能也想在其中添加 Date_trans,这是我所期望的。

于 2012-08-05T19:23:40.607 回答