【问题标题】:Generate Row Number for every 3 rows为每 3 行生成行号
【发布时间】:2017-02-09 13:09:42
【问题描述】:

我想为每三行生成一个数字

CREATE TABLE #test(period INT)

INSERT INTO #test
VALUES      (602),(603),(604),(605),(606),(607),(608),(609)

我知道我们可以使用row_number窗口函数或while循环或cursor生成序列

SELECT period,
       ( Row_number()OVER(ORDER BY period) - 1 ) / 3 + 1
FROM   #test 

结果;

+--------+-----+
| period | seq |
+--------+-----+
|    602 |   1 |
|    603 |   1 |
|    604 |   1 |
|    605 |   2 |
|    606 |   2 |
|    607 |   2 |
|    608 |   3 |
|    609 |   3 |
+--------+-----+

还有其他方法可以在数学上实现。期间不会有任何间隔

【问题讨论】:

  • 就我而言,没有比这更好的方法了。毕竟,这就是 ROW_NUMBER 的设计目的,不是吗?为什么要使用另一种方法?
  • 如果可以正常工作,为什么还要使用其他方法?你有什么理由想在数学上做更多的事情吗?
  • @Saypontigohe & @Tyron78 - 原始需求比这更复杂。我在另一个窗口函数的partition by 中使用该序列。因为Row_Number查询变得很慢
  • 您的数据集中有最小周期数吗?
  • @scsimon - 不,它可以以任何数字开头.. 但总是数字

标签: sql sql-server tsql sql-server-2012


【解决方案1】:

数学或算术方法可以是使用周期数本身:

-- table init here
DECLARE @MIN_PERIOD INT = (SELECT MIN(period) FROM #test)

SELECT period,
       (period - @MIN_PERIOD) / 3 + 1 AS seq
FROM   #test 

只要“期间之间不会有任何间隙”保持正确,这就会起作用。

如果您需要主查询上的WHERE 子句,也将其应用于SELECT MIN() 查询。只要WHERE 不会导致周期间隔,就可以工作。

【讨论】:

  • 不错的一个……没想到这个。
  • 太棒了……很有帮助
【解决方案2】:

NTILE()函数是可以实现的,但我认为它并不比ROW_NUMBER()更有效,主要是因为这个方法必须得到总数来确定组的数量。

创建测试环境:

/*  -- SQL 2016
DROP TABLE IF EXISTS #test;
GO
*/

IF OBJECT_ID('tempdb.dbo.#test') IS NOT NULL DROP TABLE #test

CREATE TABLE #test(period INT);
GO

INSERT INTO #test -- Make it bigger
VALUES      (602),(603),(604),(605),(606),(607),(608),(609);
GO 51

ROW_NUMBER 方法:

SELECT /*ROW_NUM*/ period,
       ( Row_number()OVER(ORDER BY period) - 1 ) / 3 + 1
FROM   #test;
GO

IO 和时间表现: 为了便于阅读而缩短

(受影响的 400 行)表“工作表”。扫描计数 0,逻辑读取 0,物理读取 0 '#test_00000000000E'。扫描计数 1,逻辑读取 1,物理读取 0

(受影响的 1 行)

SQL Server 执行时间:CPU 时间 = 0 毫秒,经过时间 = 85 女士。

NTILE 方法

DECLARE @ntile_var int;

SELECT @ntile_var = COUNT(*) FROM #test;

SELECT /*NTILE*/period
    , NTILE(@ntile_var / 3) OVER (ORDER BY period)
FROM #test

IO 和时间表现: 为了便于阅读而缩短

SQL Server 解析和编译时间:CPU 时间 = 0 毫秒,已用时间 = 0 毫秒。

SQL Server 执行时间:CPU 时间 = 0 毫秒,运行时间 = 0 毫秒。

SQL Server 解析和编译时间:CPU 时间 = 0 毫秒,已用时间 = 0 毫秒。

表 '#test__00000000000E'。扫描计数 1,逻辑读取 1,物理读取 0

(受影响的 1 行)

SQL Server 执行时间:CPU 时间 = 0 毫秒,运行时间 = 0 毫秒。

(受影响的 400 行)表“工作表”。扫描计数 3,逻辑读取 811,物理读取 0 桌子 '#test___00000000000E'。扫描计数 1,逻辑读取 1,物理读取 0

(受影响的 1 行)

SQL Server 执行时间:CPU 时间 = 0 毫秒,经过时间 = 93

这两个给出相同的结果:

但是有一个警告! MSDN 说得够清楚了(重点加了)

如果一个分区中的行数不能被 integer_expression,这将导致两个大小不同的组 一名成员。较大的组按顺序排在较小的组之前 由 OVER 子句指定。例如,如果总行数 是53,组数是5,前三组将 有 11 行,其余两个组各有 10 行。

所以使用 NTILE 方法,你可以得到几组 4 个,所以其余的可以是 3 个。

【讨论】:

    【解决方案3】:

    这样的事情怎么办……

    WITH X AS (
    SELECT *
         ,ROW_NUMBER() OVER (ORDER BY [period] ASC) rn 
    FROM #test
     )
     SELECT [period]
          ,ROW_NUMBER() OVER (PARTITION BY (X.rn % 3) ORDER BY rn ASC) rn 
    FROM X
    ORDER BY [period]
    

    【讨论】:

    • 谢谢.. 我不想用Row_Number
    猜你喜欢
    • 2019-11-30
    • 2018-12-08
    • 2021-05-20
    • 2016-05-01
    • 2014-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多