b3b*_*bel 3 sql t-sql sql-server sql-server-2008
我有一张名为crewWork的表如下:
CREATE TABLE crewWork(
FloorNumber int, AptNumber int, WorkType int, simTime int )
Run Code Online (Sandbox Code Playgroud)
在填充表格之后,我需要知道apt发生了多少次变化以及楼层发生变化的次数.通常我希望在每个apt上找到10行,在每层上找到40-50行.我可以为此编写一个标量函数,但我想知道是否有任何方法可以在t-SQL中执行此操作而无需编写标量函数.
谢谢
数据如下所示:
FloorNumber AptNumber WorkType simTime
1 1 12 10
1 1 12 25
1 1 13 35
1 1 13 47
1 2 12 52
1 2 12 59
1 2 13 68
1 1 14 75
1 4 12 79
1 4 12 89
1 4 13 92
1 4 14 105
1 3 12 115
1 3 13 129
1 3 14 138
2 1 12 142
2 1 12 150
2 1 14 168
2 1 14 171
2 3 12 180
2 3 13 190
2 3 13 200
2 3 14 205
3 3 14 216
3 4 12 228
3 4 12 231
3 4 14 249
3 4 13 260
3 1 12 280
3 1 13 295
2 1 14 315
2 2 12 328
2 2 14 346
Run Code Online (Sandbox Code Playgroud)
我需要报告的信息,我不需要将它存储在任何地方.
The*_*Pea 12
如果您使用现在所写的已接受答案(2023 年 1 月 6 日),您可以使用 OP 数据集得到正确的结果,但我认为您可能会使用其他数据得到错误的结果。
已确认:接受的答案有错误(截至 2023 年 1 月 6 日)
我在对已接受答案的评论中解释了可能出现错误结果的可能性。
在此 db<>fiddle中,我演示了错误的结果。我使用了稍微修改过的已接受答案的形式(我的语法适用于 SQL Server和PostgreSQL)。我使用稍微修改过的OP数据形式(我更改了两行)。我演示了如何稍微改变已接受的答案,以产生正确的结果。
接受的答案很聪明,但需要进行一些小的更改才能产生正确的结果(如上面的 db<>fiddle 所示并在此处描述:
COUNT(DISTINCT AptGroup)...
COUNT(DISTINCT CONCAT(AptGroup, '_', AptNumber))...
数据定义语言:
SELECT * INTO crewWork FROM (VALUES
-- data from question, with a couple changes to demonstrate problems with the accepted answer
-- /sf/ask/606640681/
--FloorNumber AptNumber WorkType simTime
(1, 1, 12, 10 ),
-- (1, 1, 12, 25 ), -- original
(2, 1, 12, 25 ), -- new, changing FloorNumber 1->2->1
(1, 1, 13, 35 ),
(1, 1, 13, 47 ),
(1, 2, 12, 52 ),
(1, 2, 12, 59 ),
(1, 2, 13, 68 ),
(1, 1, 14, 75 ),
(1, 4, 12, 79 ),
-- (1, 4, 12, 89 ), -- original
(1, 1, 12, 89 ), -- new , changing AptNumber 4->1->4)
(1, 4, 13, 92 ),
(1, 4, 14, 105 ),
(1, 3, 12, 115 ),
...
Run Code Online (Sandbox Code Playgroud)
数据标记语言:
;
WITH groupedWithConcats as (SELECT
*,
CONCAT(AptGroup,'_', AptNumber) as AptCombo,
CONCAT(FloorGroup,'_',FloorNumber) as FloorCombo
-- SQL SERVER doesnt have TEMPORARY keyword; Postgres doesn't understand # for temp tables
-- INTO TEMPORARY groupedWithConcats
FROM
(
SELECT
-- the columns shown in Andriy's answer:
-- /sf/answers/606723421/
ROW_NUMBER() OVER ( ORDER BY simTime) as RN,
-- AptNumber
AptNumber,
ROW_NUMBER() OVER (PARTITION BY AptNumber ORDER BY simTime) as RN_Apt,
ROW_NUMBER() OVER ( ORDER BY simTime)
- ROW_NUMBER() OVER (PARTITION BY AptNumber ORDER BY simTime) as AptGroup,
-- FloorNumber
FloorNumber,
ROW_NUMBER() OVER (PARTITION BY FloorNumber ORDER BY simTime) as RN_Floor,
ROW_NUMBER() OVER ( ORDER BY simTime)
- ROW_NUMBER() OVER (PARTITION BY FloorNumber ORDER BY simTime) as FloorGroup
FROM crewWork
) grouped
)
-- if you want to see how the groupings work:
-- SELECT * FROM groupedWithConcats
-- otherwise just run this query to see the counts of "changes":
SELECT
COUNT(DISTINCT AptCombo)-1 as CountAptChangesWithConcat_Correct,
COUNT(DISTINCT AptGroup)-1 as CountAptChangesWithoutConcat_Wrong,
COUNT(DISTINCT FloorCombo)-1 as CountFloorChangesWithConcat_Correct,
COUNT(DISTINCT FloorGroup)-1 as CountFloorChangesWithoutConcat_Wrong
FROM groupedWithConcats;
Run Code Online (Sandbox Code Playgroud)
替代答案
接受的答案最终可能会被更新以消除错误。如果发生这种情况,我可以删除我的警告,但我仍然想给您留下这种替代方法来产生答案。
我的方法是这样的:“检查前一行,如果前一行与当前行的值不同,则存在更改”。SQL 本身没有想法或行顺序函数(至少不像 Excel 那样;)
相反,SQL 有窗口函数。通过 SQL 的窗口函数,您可以使用窗口函数加上此处所示的RANK
自我JOIN
技术来组合当前行值和前一行值,以便可以比较它们。这是一个 db<>fiddle显示我的方法,我将其粘贴在下面。
中间表显示了如果有更改则值为 1 的列,否则为 0(即FloorChange
, AptChange
),显示在帖子的底部...
数据定义语言:
...same as above...
数据标记语言:
;
WITH rowNumbered AS (
SELECT
*,
ROW_NUMBER() OVER (
ORDER BY simTime) as RN
FROM crewWork
)
,joinedOnItself AS (
SELECT
rowNumbered.*,
rowNumberedRowShift.FloorNumber as FloorShift,
rowNumberedRowShift.AptNumber as AptShift,
CASE WHEN rowNumbered.FloorNumber <> rowNumberedRowShift.FloorNumber THEN 1 ELSE 0 END as FloorChange,
CASE WHEN rowNumbered.AptNumber <> rowNumberedRowShift.AptNumber THEN 1 ELSE 0 END as AptChange
FROM rowNumbered
LEFT OUTER JOIN rowNumbered as rowNumberedRowShift
ON rowNumbered.RN = (rowNumberedRowShift.RN+1)
)
-- if you want to see:
-- SELECT * FROM joinedOnItself;
SELECT
SUM(FloorChange) as FloorChanges,
SUM(AptChange) as AptChanges
FROM joinedOnItself;
Run Code Online (Sandbox Code Playgroud)
下面看到中间表 ( ) 的前几行joinedOnItself
。这显示了我的方法是如何运作的。请注意最后两列,当与 相比发生变化(在 中注明)或与发生变化(在 中注明)时,其值为 1 。FloorNumber
FloorShift
FloorChange
AptNumber
AptShift
AptChange
楼层号 | 设备号 | 工作类型 | 模拟时间 | rn | 地板移位 | aptshift | 换楼 | aptchange |
---|---|---|---|---|---|---|---|---|
1 | 1 | 12 | 10 | 1 | 0 | 0 | ||
2 | 1 | 12 | 25 | 2 | 1 | 1 | 1 | 0 |
1 | 1 | 13 | 35 | 3 | 2 | 1 | 1 | 0 |
1 | 1 | 13 | 47 | 4 | 1 | 1 | 0 | 0 |
1 | 2 | 12 | 52 | 5 | 1 | 1 | 0 | 1 |
1 | 2 | 12 | 59 | 6 | 1 | 2 | 0 | 0 |
1 | 2 | 13 | 68 | 7 | 1 | 2 | 0 | 0 |
请注意,您可以使用窗口函数直接将当前行与上一行中的值进行比较(无需),而不是使用窗口函数RANK
和。我这里没有这个解决方案,但维基百科文章示例中对此进行了描述:JOIN
LAG
JOIN
窗口函数允许访问当前记录之前和之后的记录中的数据。
如果我没有遗漏任何内容,您可以使用以下方法查找更改的数量:
确定具有相同值的后续行组;
算这些群体;
减去1.
单独应用该方法for AptNumber
和for FloorNumber
.
可以像在这个答案中那样确定组,只有Seq
你的案例中没有列.相反,ROW_NUMBER()
可以使用另一个表达式.这是一个近似的解决方案:
;
WITH marked AS (
SELECT
FloorGroup = ROW_NUMBER() OVER ( ORDER BY simTime)
- ROW_NUMBER() OVER (PARTITION BY FloorNumber ORDER BY simTime),
AptGroup = ROW_NUMBER() OVER ( ORDER BY simTime)
- ROW_NUMBER() OVER (PARTITION BY AptNumber ORDER BY simTime)
FROM crewWork
)
SELECT
FloorChanges = COUNT(DISTINCT FloorGroup) - 1,
AptChanges = COUNT(DISTINCT AprGroup) - 1
FROM marked
Run Code Online (Sandbox Code Playgroud)
(我在这里假设该simTime
列定义了更改的时间轴.)
UPDATE
下面的表格显示了如何获取不同的组AptNumber
.
AptNumber RN RN_Apt Apt_Group (= RN - RN_Apt)
--------- -- ------ ---------
1 1 1 0
1 2 2 0
1 3 3 0
1 4 4 0
2 5 1 4
2 6 2 4
2 7 3 4
1 8 5 => 3
4 9 1 8
4 10 2 8
4 11 3 8
4 12 4 8
3 13 1 12
3 14 2 12
3 15 3 12
1 16 6 10
… … … …
Run Code Online (Sandbox Code Playgroud)
这RN
是一个伪列代表ROW_NUMBER() OVER (ORDER BY simTime)
.你可以看到这只是从1开始的一系列排名.
另一个伪列,RN_Apt
包含由另一个产生的值ROW_NUMBER
,即ROW_NUMBER() OVER (PARTITION BY AptNumber ORDER BY simTime)
.它包含具有相同AptNumber
值的各个组内的排名.您可以看到,对于新遇到的值,序列重新开始,对于重复的序列,它会在上次停止的位置继续.
您也可以从表中可以看到,如果我们减去RN
从RN_Apt
(可能是倒过来,在这种情况下并不重要),我们得到的唯一标识相同的每个不同组的值AptNumber
值.您也可以将该值称为组ID.
所以,既然我们已经拥有了这些ID,那么我们只能计算它们(当然,计算不同的值).这将是组的数量,并且更改的数量减少一个(假设第一组不计入更改).
归档时间: |
|
查看次数: |
3241 次 |
最近记录: |