如何通过排序、分区和分组进行行编号

2024-02-01

我需要通过排序、分区和分组进行行编号。订购依据IdDocument, DateChange, 划分为IdDocument并分组依据IdRole。问题尤其出在分组上。从例子中可以看出(NumberingExpected) DENSE_RANK()必须是用于此目的的最佳函数,但仅当用于排序的值相同时才会重复编号。就我而言,用于排序的值(IdDocument, DateChange)总是不同的,重复编号必须通过IdRole.

当然,使用光标可以很容易地解决这个问题。但是有没有办法用编号/排名功能来实现呢?

测试数据:

declare @LogTest as table (
    Id INT
    ,IdRole INT
    ,DateChange DATETIME
    ,IdDocument INT
    ,NumberingExpected INT
)
insert into @LogTest
select 1 as Id, 7 as IdRole, GETDATE() as DateChange, 13 as IdDocument, 1 as NumberingExpected
union 
select 2, 3, DATEADD(HH, 1, GETDATE()), 13, 2
union 
select 3, 3, DATEADD(HH, 2, GETDATE()), 13, 2
union 
select 4, 3, DATEADD(HH, 3, GETDATE()), 13, 2
union 
select 5, 5, DATEADD(HH, 4, GETDATE()), 13, 3
union 
select 7, 3, DATEADD(HH, 6, GETDATE()), 13, 4
union 
select 6, 3, DATEADD(HH, 5, GETDATE()), 27, 1
union 
select 8, 3, DATEADD(HH, 7, GETDATE()), 27, 1
union 
select 9, 5, DATEADD(HH, 8, GETDATE()), 27, 2
union 
select 10, 3, DATEADD(HH, 9, GETDATE()), 27, 3


select * from @LogTest order by IdDocument, DateChange;

从函数式编程的角度解释:

  1. 按 IdDocument、DateChange 排序数据
  2. 将第一行号设置为 i=1 转到下一行
  3. 如果 IdDocument 已更改 { 我=1; } 别的 { 如果 IdRow 已更改 { i++; } }
  4. 设置行号为i;
  5. 转到下一行;
  6. 如果 EOF { 退出; } else { 转到步骤 3; }

自 2012 年起您可以使用LAG/LEAD,但在 2008 年还没有,所以我们将模仿它。性能可能很差,您应该检查您的实际数据。

这是最终的查询:

WITH
CTE_rn
AS
(
    SELECT
        Main.IdRole
        ,Main.IdDocument
        ,Main.DateChange
        ,ROW_NUMBER() OVER(PARTITION BY Main.IdDocument ORDER BY Main.DateChange) AS rn
    FROM
        @LogTest AS Main
        OUTER APPLY
        (
            SELECT TOP (1) T.IdRole
            FROM @LogTest AS T
            WHERE
                T.IdDocument = Main.IdDocument
                AND T.DateChange < Main.DateChange
            ORDER BY T.DateChange DESC
        ) AS Prev
    WHERE Main.IdRole <> Prev.IdRole OR Prev.IdRole IS NULL
)
SELECT *
FROM
    @LogTest AS LT
    CROSS APPLY
    (
        SELECT TOP(1) CTE_rn.rn
        FROM CTE_rn
        WHERE
            CTE_rn.IdDocument = LT.IdDocument
            AND CTE_rn.IdRole = LT.IdRole
            AND CTE_rn.DateChange <= LT.DateChange
        ORDER BY CTE_rn.DateChange DESC
    ) CA_rn
ORDER BY IdDocument, DateChange;

最终结果集:

Id    IdRole    DateChange                 IdDocument    NumberingExpected    rn
1     7         2015-01-26 20:00:41.210    13            1                    1
2     3         2015-01-26 21:00:41.210    13            2                    2
3     3         2015-01-26 22:00:41.210    13            2                    2
4     3         2015-01-26 23:00:41.210    13            2                    2
5     5         2015-01-27 00:00:41.210    13            3                    3
7     3         2015-01-27 02:00:41.210    13            4                    4
6     3         2015-01-27 01:00:41.210    27            1                    1
8     3         2015-01-27 03:00:41.210    27            1                    1
9     5         2015-01-27 04:00:41.210    27            2                    2
10    3         2015-01-27 05:00:41.210    27            3                    3

怎么运行的

1) 当表按 IdDocument 和 DateChange 排序时,我们需要上一行的 IdRole 值。为了得到它,我们使用OUTER APPLY(因为LAG不可用):

SELECT *
FROM
    @LogTest AS Main
    OUTER APPLY
    (
        SELECT TOP (1) T.IdRole
        FROM @LogTest AS T
        WHERE
            T.IdDocument = Main.IdDocument
            AND T.DateChange < Main.DateChange
        ORDER BY T.DateChange DESC
    ) AS Prev
ORDER BY Main.IdDocument, Main.DateChange;

这是第一步的结果集:

Id    IdRole    DateChange                 IdDocument    NumberingExpected    IdRole
1     7         2015-01-26 20:50:32.560    13            1                    NULL
2     3         2015-01-26 21:50:32.560    13            2                    7
3     3         2015-01-26 22:50:32.560    13            2                    3
4     3         2015-01-26 23:50:32.560    13            2                    3
5     5         2015-01-27 00:50:32.560    13            3                    3
7     3         2015-01-27 02:50:32.560    13            4                    5
6     3         2015-01-27 01:50:32.560    27            1                    NULL
8     3         2015-01-27 03:50:32.560    27            1                    3
9     5         2015-01-27 04:50:32.560    27            2                    3
10    3         2015-01-27 05:50:32.560    27            3                    5

2)我们想要删除具有重复IdRole的行,所以我们添加一个WHERE并对行进行编号。您可以看到行号符合预期结果:

SELECT
    Main.IdRole
    ,Main.IdDocument
    ,Main.DateChange
    ,ROW_NUMBER() OVER(PARTITION BY Main.IdDocument ORDER BY Main.DateChange) AS rn
FROM
    @LogTest AS Main
    OUTER APPLY
    (
        SELECT TOP (1) T.IdRole
        FROM @LogTest AS T
        WHERE
            T.IdDocument = Main.IdDocument
            AND T.DateChange < Main.DateChange
        ORDER BY T.DateChange DESC
    ) AS Prev
WHERE Main.IdRole <> Prev.IdRole OR Prev.IdRole IS NULL
;

这是此步骤的结果集(它成为 CTE):

IdRole    IdDocument    DateChange                 rn
7         13            2015-01-26 20:13:26.247    1
3         13            2015-01-26 21:13:26.247    2
5         13            2015-01-27 00:13:26.247    3
3         13            2015-01-27 02:13:26.247    4
3         27            2015-01-27 01:13:26.247    1
5         27            2015-01-27 04:13:26.247    2
3         27            2015-01-27 05:13:26.247    3

3) 最后,我们需要从 CTE 中获取原始表每一行的正确行号。我用CROSS APPLY从原始表的每一行的 CTE 中获取一行。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何通过排序、分区和分组进行行编号 的相关文章

随机推荐

  • 如何检测DrawerLayout是否开始打开?

    所以我想在导航抽屉中隐藏一些选项卡starts开放 我的代码在打开后隐藏了它们 但这不是我想要的 mDrawerToggle new ActionBarDrawerToggle this mDrawerLayout R drawable i
  • JavaScript(ES6) 将对象设置为 null 时 WeakMap 垃圾回收

    我刚刚读到 WeakMaps 通过专门使用对象作为键来利用垃圾收集 并且将对象分配给 null 相当于删除它 let planet1 name Coruscant city Galactic City let planet2 name Ta
  • 在函数类型签名中替换 => 代替 ->

    我只是观察到如果不是 gt 我写的 gt 在函数的类型签名定义中 它不会导致编译时错误 示例代码 mysum Num a gt a gt a Notice gt after the list a mysum 0 mysum x xs x m
  • dijit.Tree搜索和刷新

    我似乎无法弄清楚如何使用 ItemFileWriteStore 和 TreeStoreModel 在 dijit Tree 中进行搜索 一切都是声明性的 我使用的是 Dojo 1 7 1 这是我到目前为止所拥有的
  • VSCode 扩展修复 Python 选项卡不一致问题 [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 首先 我想知道是谁决定使用基于缩进的语言的无脑天才 以及为什么他不能简单地使用分号 令人沮丧的是 Pyt
  • 使用模型时 CakePHP AJAX Json 解析错误

    我正在使用 CakePHP 2 5 3 0 开发一个应用程序 然后我偶然发现了 AJAX 的一个问题 我使用从 jQuery 到 CakePHP 的 AJAX 请求来发送用户的登录名和密码 然后 CakePHP 应该返回经过验证的 JSON
  • 展开可选 IBOutlet 值时为 Nil

    我有一个fatal error unexpectedly found nil while unwrapping an Optional value for an IBOutlet 在我的自定义视图中 我已将我的插座与XIB文件 我已经仔细检
  • 在 Objective-C 中打乱 NSString 中的字母

    我写了这个函数 它会打乱 a 的内容NSString 它似乎可以工作 但时不时会崩溃 这可能是一种迂回的方式 但我将字符放入一个数组中 随机交换数组中的元素 然后将数组转回字符串 我不确定我正在做的事情是不安全的 这会导致它崩溃 我想这可能
  • 从视图控制器返回到表视图控制器时,表视图单元格“跳转”

    我有一个TableViewController 然后单击自定义单元格会将您带到相关的WebViewController 我遇到的问题是 当我点击 返回 时WebViewController中的表视图单元格TableViewControlle
  • pandoc 文档转换失败,错误 2

    当我在 R studio 中编写 HTML 时 我得到以下结果 C Users Susan ANACON 1 Scripts pandoc RTS K512m RTS Lesson3 student utf8 md to html from
  • Ruby:KML 库?

    我正在寻找一个 ruby 库来将位置数据导出到 KML 文件中 要导出的数据主要包含带有纬度和经度的简单点 但我也希望能够导出更复杂的多边形 I tried 龟豹 http rubygems org gems kamelopard 但觉得不
  • Python __dict__

    属性 dict 应该包含用户定义的属性 但是如果我们打印 dict 一个空的类 我还会得到 module dict weakref doc 哪些是由 Python 预先填充的 dict 相应于类对象类型的属性 Now base and cl
  • 主 NSWindow 阴影不会像子窗口那样改变

    我的应用程序中的主窗口是无边框的 self window setStyleMask NSBorderlessWindowMask 从该窗口 我还打开其他子窗口 当我在其他子窗口之间单击时 我看到当该子窗口被单击并成为焦点窗口时 这些子窗口的
  • 如何使嵌套双引号在 bash 解释器中幸存下来?

    鉴于下面的上下文 是否存在任何可以插入到 WGETOPT 定义中的神奇语法 以允许 吸收 USERAGENT 变量 并且仍然允许调用 wget 命令 如下所示syntax 1 我目前已经求助于使用 eval 但我对此并不满意 但也许这是我可
  • 根据 React.Js 中第一个下拉列表中的选择填充第二个下拉列表

    我正在学习反应并努力根据从第一个下拉列表中单击的选项来填充第二个下拉列表 我在下面包含了我的代码 我认为问题在于我尝试设置的位置this state selected param tableName 我认为这行不通 但我不知道该用什么来代替
  • SVG 作为 CSS 背景 - 有没有办法重复 x 且中间没有空格?

    在基于 Webkit 和 Blink 的浏览器中 SVG 无法正确平铺 不同缩放级别的图像之间存在间隙 StackOverflow 上有许多主题解决了这个问题 所有主题都标记为已解决 但这些人一定没有足够彻底地测试他们的解决方案 我的 SV
  • Groovy Abstract ConcurrentMap 中的错误?

    抽象ConcurrentMap http grepcode sadcom file repo1 maven org maven2 org codehaus groovy groovy all 2 4 3 org codehaus groov
  • 是否可以使用VBA单击报表记录以在Access中打开相关表单

    我有一份包含工作 任务详细信息的报告 还有一个为该报告提供大部分数据的表格 鉴于报告是查看数据大图的好方法 而表单是编辑数据的最佳方法 我希望能够单击一行 并让它打开相关记录表单视图 有谁知道如何通过VBA 做到这一点 在我看来 这应该是可
  • 我可以在不可变类中使用 StringBuilder

    如果我创建一个不可变的类 所有字段都必须是最终的 如果我像这样使用 stringbuilder 最终 StringBuilder s new StringBuilder Hello 那么append函数可以追加s的值并且类不会是不可变的 请
  • 如何通过排序、分区和分组进行行编号

    我需要通过排序 分区和分组进行行编号 订购依据IdDocument DateChange 划分为IdDocument并分组依据IdRole 问题尤其出在分组上 从例子中可以看出 NumberingExpected DENSE RANK 必须