【问题标题】:SQL first order, then partition in over clauseSQL 先排序,然后在 over 子句中分区
【发布时间】:2018-12-07 06:01:39
【问题描述】:

我有一个问题,我想对排序表进行分区。有什么办法可以做到吗?

我使用的是 SQL Server 2016。

输入表:

|---------|-----------------|-----------|------------|
|  prod   |   sortcolumn    |    type   |    value   |
|---------|-----------------|-----------|------------|
|    X    |        1        |     P     |     12     |
|    X    |        2        |     P     |     23     |
|    X    |        3        |     E     |     34     |
|    X    |        4        |     P     |     45     |
|    X    |        5        |     E     |     56     |
|    X    |        6        |     E     |     67     |
|    Y    |        1        |     P     |     78     |
|---------|-----------------|-----------|------------|

期望的输出

|---------|-----------------|-----------|------------|------------|
|  prod   |   sortcolumn    |    type   |    value   |    rowNr   |
|---------|-----------------|-----------|------------|------------|
|    X    |        1        |     P     |     12     |      1     |
|    X    |        2        |     P     |     23     |      2     |
|    X    |        3        |     E     |     34     |      1     |
|    X    |        4        |     P     |     45     |      1     |
|    X    |        5        |     E     |     56     |      1     |
|    X    |        6        |     E     |     67     |      2     |
|    Y    |        1        |     P     |     78     |      1     |
|---------|-----------------|-----------|------------|------------|

我已经这么远了:

SELECT
  table.*,
  ROW_NUMBER() OVER(PARTITION BY table.prod, table.type ORDER BY table.sortColumn) rowNr
FROM table

但这不会重新启动第 4 行的行号,因为它是相同的产品和类型。 我如何根据排序标准在每个产品和每个类型更改时重新启动,即使类型更改回以前的状态?这甚至可以使用 ROW_NUMBER 函数还是我必须使用 LEAD 和 LAG 和 CASES (这可能会使其非常慢,对吧?)

谢谢!

【问题讨论】:

  • 当您确实需要前一行的信息用于您的逻辑时,我看不出如何避免滞后/领先
  • 您为什么认为LEAD 和/或LAG 会使查询变慢?
  • 这就是我到目前为止的经验......但是它来自旧版本的 oracle,在新的 sql server 上会更好吗?请记住,该表是中等大小(大约 2 mio 行)
  • Oracle 和 SQl Server 是非常不同的产品。根据我的经验,LAGLEAD 非常有效,并且比按照FROM MyTable MT LEFT JOIN MyTable MTl ON MT.ID +1 = MTl.ID 的方式执行JOIN 要好得多

标签: sql sql-server tsql row-number gaps-and-islands


【解决方案1】:

这是一个空白和孤岛问题。您可以使用以下查询:

SELECT t.*, 
       ROW_NUMBER() OVER (PARTITION BY prod ORDER BY sortcolumn)
       -
       ROW_NUMBER() OVER (PARTITION BY prod, type ORDER BY sortcolumn) AS grp
FROM mytable t

得到:

prod    sortcolumn  type    value   grp
----------------------------------------
X       1           P       12      0
X       2           P       23      0
X       3           E       34      2
X       4           P       45      1
X       5           E       56      3
X       6           E       67      3
Y       1           P       78      0

现在,grp 字段可用于分区:

;WITH IslandsCTE AS (
    SELECT t.*, 
           ROW_NUMBER() OVER (PARTITION BY prod ORDER BY sortcolumn)
           -
           ROW_NUMBER() OVER (PARTITION BY prod, type ORDER BY sortcolumn) AS grp
    FROM mytable t  
)
SELECT prod, sortcolumn, type, value,
       ROW_NUMBER() OVER (PARTITION BY prod, type, grp ORDER BY sortcolumn) AS rowNr
FROM IslandsCTE
ORDER BY prod, sortcolumn

Demo here

【讨论】:

  • 是的,这正是我所需要的!我试过了,它按预期工作,而且速度非常快。谢谢!
【解决方案2】:

这是一个经典的“孤岛”问题,因为您需要找到与prodtype 相关的记录的“孤岛”,但没有将所有与@ 匹配的记录分组在一起987654324@和type

这是通常解决此问题的一种方法。设置:

DECLARE @t TABLE (
    prod varchar(1),
    sortcolumn int,
    type varchar(1),
    value int
);

INSERT @t VALUES
('X', 1, 'P', 12),
('X', 2, 'P', 23),
('X', 3, 'E', 34),
('X', 4, 'P', 45),
('X', 5, 'E', 56),
('X', 6, 'E', 67),
('Y', 1, 'P', 78)
;

获取一些行号:

;WITH numbered AS (
    SELECT
        *,
        ROW_NUMBER() OVER (PARTITION BY prod, type ORDER BY sortcolumn) as rnX,
        ROW_NUMBER() OVER (PARTITION BY prod ORDER BY sortcolumn) as rn
    FROM
        @t
)

numbered 现在看起来像这样:

prod sortcolumn  type value       rnX                  rn
---- ----------- ---- ----------- -------------------- --------------------
X    1           P    12          1                    1
X    2           P    23          2                    2
X    3           E    34          1                    3
X    4           P    45          3                    4
X    5           E    56          2                    5
X    6           E    67          3                    6
Y    1           P    78          1                    1

为什么这很有用?好吧,看看rnXrn 之间的区别

prod sortcolumn  type value       rnX                  rn                   rn - rnX
---- ----------- ---- ----------- -------------------- -------------------- --------------------
X    1           P    12          1                    1                    0
X    2           P    23          2                    2                    0
X    3           E    34          1                    3                    2
X    4           P    45          3                    4                    1
X    5           E    56          2                    5                    3
X    6           E    67          3                    6                    3
Y    1           P    78          1                    1                    0

如您所见,每个“组”共享一个 rn - rnX 值,并且从一个组更改为下一个组。

所以现在如果我们按prodtype和组号进行分区,那么编号在其中

SELECT
    *,
    ROW_NUMBER() OVER (PARTITION BY prod, type, rn - rnX ORDER BY sortcolumn) rowNr
FROM
    numbered
ORDER BY 
    prod, sortcolumn

我们完成了:

prod sortcolumn  type value       rnX                  rn                   rowNr
---- ----------- ---- ----------- -------------------- -------------------- --------------------
X    1           P    12          1                    1                    1
X    2           P    23          2                    2                    2
X    3           E    34          1                    3                    1
X    4           P    45          3                    4                    1
X    5           E    56          2                    5                    1
X    6           E    67          3                    6                    2
Y    1           P    78          1                    1                    1

相关阅读:Things SQL needs: SERIES()

【讨论】:

  • 解释得很好!
【解决方案3】:

试试这个

select prod, sortcolumn, type, value, row_number() over (partition by prod, sortcolumn, type order by value) rowNr    
from table_name

【讨论】:

    猜你喜欢
    • 2019-04-14
    • 2016-10-15
    • 2017-12-27
    • 1970-01-01
    • 2016-11-10
    • 1970-01-01
    • 2012-12-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多