如何为每个岛屿的连续记录编号?

时间:2019-03-14 23:16:36

标签: sql gaps-and-islands

我有一张桌子,看起来像:

select  o.id,  name, orderdate, o.ordervalue
from orders o
cross apply (select top 1 id, ordervalue from orders where name=o.name and orderdate=o.OrderDate order by ordervalue desc) oo
where o.id=oo.id
order by o.id

并且按组和日期排序。我想要一个额外的列来显示每个组的连续颜色'R'的顺序号。

必需的输出:

group    date        color
  A      1-1-2019      R
  A      1-2-2019      Y
  B      1-1-2019      R
  B      1-2-2019      Y
  B      1-3-2019      Y
  B      1-4-2019      R
  B      1-5-2019      R
  B      1-6-2019      R

我尝试将窗口函数用于按组和颜色列进行分区,但是它返回的输出在下面是不正确的。

错误的查询和输出:

group    date        color    rank
  A      1-1-2019      R      1
  A      1-2-2019      Y      null
  B      1-1-2019      R      1
  B      1-2-2019      Y      null
  B      1-3-2019      Y      null
  B      1-4-2019      R      1
  B      1-5-2019      R      2
  B      1-6-2019      R      3

我想知道它是否可以在SQL中使用,还是应该切换到另一种语言(例如Python)?

3 个答案:

答案 0 :(得分:3)

使用用户变量可以保留排名和先前的值以产生结果:

 CREATE TABLE tbl (
   `group` VARCHAR(1),
   `date` VARCHAR(8),
   `color` VARCHAR(1)
 );

 INSERT INTO tbl
   (`group`, `date`, `color`)
 VALUES
   ('A', '1-1-2019', 'R'),
   ('A', '1-2-2019', 'Y'),
   ('B', '1-1-2019', 'R'),
   ('B', '1-2-2019', 'Y'),
   ('B', '1-3-2019', 'Y'),
   ('B', '1-4-2019', 'R'),
   ('B', '1-5-2019', 'R'),
   ('B', '1-6-2019', 'R');

 set @seq := 0, @prev := 'B'

 SELECT 
     *, 
     IF(color='R', @seq := IF(@prev = color, @seq + 1, 1), NULL) AS rank,
     @prev := color as prev
 FROM tbl
 ORDER BY `group`, `date`

group | date     | color | rank | prev
:---- | :------- | :---- | ---: | :---
A     | 1-1-2019 | R     |    1 | R   
A     | 1-2-2019 | Y     |      | Y   
B     | 1-1-2019 | R     |    1 | R   
B     | 1-2-2019 | Y     |      | Y   
B     | 1-3-2019 | Y     |      | Y   
B     | 1-4-2019 | R     |    1 | R   
B     | 1-5-2019 | R     |    2 | R   
B     | 1-6-2019 | R     |    3 | R   

db <>提琴here

答案 1 :(得分:3)

这是使用窗口功能可以完成的方法。首先,我们创建一个CTE,该CTE具有一个标志,指示新的序列已开始,然后从中生成一个对序列号进行计数的标志。最后,我们对每个序列中的行进行计数以获得排名:

WITH cte AS (SELECT `group`, date, color,
                    COALESCE(color = LAG(color) OVER(ORDER BY `group`, date), 0) AS samecolor
             FROM `table`),
sequences AS (SELECT `group`, date, color,
              SUM(samecolor = 0) OVER (ORDER BY `group`, date) AS seq_num
              FROM cte)
SELECT `group`, date, color,
       ROW_NUMBER() OVER (PARTITION BY seq_num) AS `rank`
FROM sequences
ORDER BY `group`, date

输出:

group   date        color   rank
A       1-1-2019    R       1
A       1-2-2019    Y       1
B       1-1-2019    R       1
B       1-2-2019    Y       1
B       1-3-2019    Y       2
B       1-4-2019    R       1
B       1-5-2019    R       2
B       1-6-2019    R       3

Demo on dbfiddle

请注意,如果您希望将这些值Y替换为NULL,则此查询还会给出rank值的排名:

CASE WHEN color = 'Y' THEN NULL
     ELSE ROW_NUMBER() OVER (PARTITION BY seq_num) 
     END AS `rank`

答案 2 :(得分:1)

window function row_number()用于 Postgres 中的纯标准SQL解决方案-或任何现代RDBMS,甚至包括从版本8开始的MySQL:

vec2
multiplyv2(vec2 a, vec2 b) {
  return a * b
}

vec3
multiplyv3(vec3 a, vec3 b) {
  return a * b
}

....

这假定vec2 rotate(vec2 v, float angle) { return rotation2d(angle) * v; } vec3 rotate(vec3 v, vec3 axis, float angle) { return (rotation3d(axis, angle) * vec4(v, 1.0)).xyz; } 被定义为SELECT grp, the_date, color , row_number() OVER (PARTITION BY grp, color, part ORDER BY the_date) AS rnk FROM ( SELECT * , row_number() OVER (PARTITION BY grp ORDER BY the_date, color) - row_number() OVER (PARTITION BY grp, color ORDER BY the_date) AS part FROM tbl ) sub ORDER BY grp, the_date, color; ,重复将产生不确定的结果。

减去两个不同的行号将为每个孤岛计算一个不同的数字((grp, color, the_date))。然后,您可以再次运行UNIQUE,现在再按子组进行分区。 Voilá。

要仅查看特定颜色的数字,在示例中为“ R”:

part

虽然基于集合的解决方案是RDBMS的要塞,并且通常更快,但是过程解决方案仅需要对此类问题进行一次扫描,因此,该plpgsql函数应实质上为 < em>更快

row_number()

致电:

SELECT grp, the_date, color, CASE WHEN color = 'R' THEN rnk END AS rnk
FROM  (
   <<query from above, without ORDER BY>>
   ) sub
ORDER  BY grp, the_date, color;

db <>提琴here

在关系数据库中,循环并非总是错误的解决方案。

进一步阅读:

此外:“行”是这些行号的一个相当误导的名称,除非您有重复项应该排在同一位...