Ljt*_*Ljt 1 sql-server sql-server-2008
我正在尝试根据值 90 编写 q 查询哪个分区。下面是我的表
create table #temp(StudentID char(2), Status int)
insert #temp values('S1',75 )
insert #temp values('S1',85 )
insert #temp values('S1',90)
insert #temp values('S1',85)
insert #temp values('S1',83)
insert #temp values('S1',90 )
insert #temp values('S1',85)
insert #temp values('S1',90)
insert #temp values('S1',93 )
insert #temp values('S1',93 )
insert #temp values('S1',93 )
Run Code Online (Sandbox Code Playgroud)
要求输出:
ID Status Result
S1 75 0
S1 85 0
S1 90 0
S1 85 1
S1 83 1
S1 90 1
S1 85 2
S1 90 2
S1 93 3
S1 93 3
S1 93 3
Run Code Online (Sandbox Code Playgroud)
请任何人解决基于分区的状态 id 90,结果应该是 1,2,3 ..etc 根据时间值 90 的数量递增
假设实际问题是“如何找到递增值的范围/孤岛”,则答案可以使用LAG将当前Status值与基于某个顺序的前一个值进行比较。如果先前的值为 90,则您有一个新岛:
declare @temp table (ID int identity PRIMARY KEY, StudentID char(2), Status int)
insert into @temp (StudentID,Status)
values
('S1',75),
('S1',85),
('S1',90),
('S1',85),
('S1',83),
('S1',90),
('S1',85),
('S1',90),
('S1',93),
('S1',93),
('S1',93);
select
* ,
case LAG(Status,1,0) OVER (PARTITION BY StudentID ORDER BY ID)
when 90 then 1 else 0 end as NewIsland
from @temp
Run Code Online (Sandbox Code Playgroud)
这返回:
+----+-----------+--------+-----------+
| ID | StudentID | Status | NewIsland |
+----+-----------+--------+-----------+
| 1 | S1 | 75 | 0 |
| 2 | S1 | 85 | 0 |
| 3 | S1 | 90 | 0 |
| 4 | S1 | 85 | 1 |
| 5 | S1 | 83 | 0 |
| 6 | S1 | 90 | 0 |
| 7 | S1 | 85 | 1 |
| 8 | S1 | 90 | 0 |
| 9 | S1 | 93 | 1 |
| 10 | S1 | 93 | 0 |
| 11 | S1 | 93 | 0 |
+----+-----------+--------+-----------+
Run Code Online (Sandbox Code Playgroud)
您可以通过将当前值之前的所有 NewIsland 值相加,使用 SUM 和OVER的 ROWS 子句,从中创建一个 Island ID :
with islands as
(
select
* ,
case LAG(Status,1,0) OVER (PARTITION BY StudentID ORDER BY ID)
when 90 then 1 else 0 end as NewIsland
from @temp
)
select * ,
SUM(NewIsland) OVER (PARTITION BY StudentID ORDER BY ID ROWS UNBOUNDED PRECEDING)
from islands
Run Code Online (Sandbox Code Playgroud)
这产生:
+----+-----------+--------+-----------+--------+
| ID | StudentID | Status | NewIsland | Result |
+----+-----------+--------+-----------+--------+
| 1 | S1 | 75 | 0 | 0 |
| 2 | S1 | 85 | 0 | 0 |
| 3 | S1 | 90 | 0 | 0 |
| 4 | S1 | 85 | 1 | 1 |
| 5 | S1 | 83 | 0 | 1 |
| 6 | S1 | 90 | 0 | 1 |
| 7 | S1 | 85 | 1 | 2 |
| 8 | S1 | 90 | 0 | 2 |
| 9 | S1 | 93 | 1 | 3 |
| 10 | S1 | 93 | 0 | 3 |
| 11 | S1 | 93 | 0 | 3 |
+----+-----------+--------+-----------+--------+
Run Code Online (Sandbox Code Playgroud)
顺便说一句,这是 SQL 中更广泛的间隙和岛屿问题的一个案例。
更新
LAG 和 OVER 可用于所有受支持的 SQL Server 版本,即 SQL Server 2012 及更高版本。OVER 在 SQL Server 2008 中也可用,但在 LAG 中不可用。在这些版本中,使用了不同的、较慢的技术来计算岛屿:序列中的间隙和岛屿的 SQL
在大多数情况下,ROW_NUMBER() 用于计算行排序,这会导致一个额外的 CTE。如果所需的排序与 ID 或任何其他唯一递增列相同,则可以避免这种情况。以下查询返回与使用的查询相同的结果LAG:
select
* ,
case when exists (select ID
from @temp t1
where t1.StudentID=t2.StudentID
and t1.ID=t2.ID-1
and t2.status=90) then 1
else 0 end
as NewIsland
from @temp t2
Run Code Online (Sandbox Code Playgroud)
如果有任何具有相同 StudentID、Status 90 和 ID 或 ROW_NUMBER 的行,则此查询返回 1,即与 LAG(,1) 相同。
之后,我们只需要对以前的值求和。虽然 SUM OVER 于 2008 年可用,但它仅支持 PARTITION BY。我们需要使用另一个子查询:
;with islands as
(
select
* ,
case when exists (select ID from @temp t1 where t1.StudentID=t2.StudentID and t1.ID=t2.ID-1 and t2.status=90) then 1
else 0 end
as NewIsland
from @temp t2
)
select * ,
(select ISNULL(SUM(NewIsland),0)
from islands i1
where i1.ID<i2.ID) AS Result
from islands i2
Run Code Online (Sandbox Code Playgroud)
这总结了所有 NewIslandID 小于当前 ID 的行的值。
表现
所有这些子查询都会导致大量重复扫描。令人惊讶的是,较旧的查询速度更快比使用 LAG 的查询,因为第一个查询必须多次排序临时结果并按状态过滤,执行计划成本分别为 45% 和 55%。
添加索引后,情况会发生巨大变化:
declare @temp table ( ID int identity PRIMARY KEY, StudentID char(2), Status int,
INDEX IX_TMP(StudentID,ID,Status))
Run Code Online (Sandbox Code Playgroud)
多重排序消失了,成本变成了 80% 和 20%。查询只扫描索引值一次,而不对中间结果进行排序。
子查询版本无法利用索引
更新 2
uzi 建议删除 LAG 并仅汇总到前一行会更好:
select * ,
SUM(case when status =90 then 1 else 0 end)
OVER (PARTITION BY StudentID
ORDER BY ID ROWS
BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)
from @temp;
Run Code Online (Sandbox Code Playgroud)
从语义上讲,这是同一件事 - 对于每一行,找到所有以前的行,为 90 年代计算 1,为其他行计算 0,然后将它们相加。
服务器在两种情况下都会生成类似的执行计划。LAG 版本使用了两个流聚合运算符,而没有它的版本使用了一个。不过,这个有限数据集的最终结果基本相同。
对于更大的数据集,结果可能会有所不同,例如,如果服务器必须将数据假脱机,tempdb因为它们不适合内存。
| 归档时间: |
|
| 查看次数: |
76 次 |
| 最近记录: |