Sql server MSSQL组,直到类型更改
我目前有以下表格:Sql server MSSQL组,直到类型更改,sql-server,Sql Server,我目前有以下表格: ╔════╦══════════╦════════════╗ ║ ID ║ PartType ║ PartStatus ║ ╠════╬══════════╬════════════╣ ║ 1 ║ A ║ OK ║ ║ 2 ║ A ║ BAD ║ ║ 3 ║ A ║ OK ║ ║ 4 ║ A ║ OK ║ ║ 5 ║ B ║ OK
╔════╦══════════╦════════════╗
║ ID ║ PartType ║ PartStatus ║
╠════╬══════════╬════════════╣
║ 1 ║ A ║ OK ║
║ 2 ║ A ║ BAD ║
║ 3 ║ A ║ OK ║
║ 4 ║ A ║ OK ║
║ 5 ║ B ║ OK ║
║ 6 ║ B ║ BAD ║
║ 7 ║ A ║ OK ║
╚════╩══════════╩════════════╝
我希望能够按零件类型对它们进行分组,直到零件类型发生变化。所以它的输出应该是这样的:
╔══════════╦══════════╗
║ PartType ║ Quantity ║
╠══════════╬══════════╣
║ A ║ 4 ║
║ B ║ 2 ║
║ A ║ 1 ║
╚══════════╩══════════╝
使用您的示例输入考虑此测试表:
DECLARE @test TABLE
(
ID int IDENTITY(1,1) NOT NULL,
PartType nvarchar(1) NOT NULL,
PartStatus nvarchar(50) NOT NULL
)
INSERT INTO @test (PartType,PartStatus)
VALUES
(N'A',N'OK'),
(N'A',N'BAD'),
(N'A',N'OK'),
(N'A',N'OK'),
(N'B',N'OK'),
(N'B',N'BAD'),
(N'A',N'OK');
当PartType更改时,我使用应用程序获取下一个ID:
SELECT t.PartType
, COUNT(t.ID) AS Quantity
FROM @test t
INNER JOIN (
SELECT MAX(ID) + 1 axID
FROM @test
) m
ON 1 = 1
OUTER APPLY (
SELECT TOP 1 s.ID as extID
FROM @test s
WHERE s.ID > t.ID
AND s.PartType <> t.PartType
ORDER BY s.ID ASC
) n
GROUP BY t.PartType, ISNULL(n.extID,m.axID)
ORDER BY ISNULL(n.extID,m.axID)
您还可以使用row_number进行此类分组,这对于更大的数据集应该会更好,因为您不必进行任何连接。这还应返回预期结果:
select PartType, count(*)
from (
select *,
row_number() over (order by ID) as RN1,
row_number() over (partition by PartType order by ID) as RN2
from yourtable
) X
group by PartType, RN1 - RN2
order by min(ID)
这里的诀窍是,第一个行号对所有行进行编号,第二个行号按PartType对它们进行分区。因此,当RN1和RN2之间的差异发生变化时,它是一种不同的类型。尝试使用递归CTE编写这个简单的脚本
WITH cte_test as(
select *,1 as recno from @Table1 where id=1
union all
select t.*,(case when c.PartType = t.PartType then recno else recno+1 end )
from @Table1 t inner join cte_test c on t.ID = c.ID+1
)
select PartType,count(*) from cte_test
group by recno,PartType
order by recno
option (maxrecursion 0)
如果您使用的是SQL Server 2012或更高版本,那么另一个值得一提的方法是使用2012年提供的窗口功能 您可以使用LAG函数检测数据集中何时发生状态更改,并且可以使用SUM OVER子句为数据生成分组id。下面的示例演示了如何做到这一点
DECLARE @parts TABLE
(
ID int IDENTITY(1,1) NOT NULL PRIMARY KEY,
PartType nvarchar(1) NOT NULL,
PartStatus nvarchar(50) NOT NULL
)
INSERT INTO @parts (PartType,PartStatus)
VALUES
(N'A',N'OK'),
(N'A',N'BAD'),
(N'A',N'OK'),
(N'A',N'OK'),
(N'B',N'OK'),
(N'B',N'BAD'),
(N'A',N'OK');
WITH CTE_PartTypeWithStateChange
AS
(
SELECT ID
,PartType
,PartStatus
,(
CASE
WHEN (LAG(PartType, 1, '') OVER (ORDER BY ID) <> PartType) THEN 1
ELSE 0
END
) HasStateChanged
FROM @parts
)
,
CTE_PartTypeWithGroupID
AS
(
SELECT ID
,PartType
,PartStatus
,SUM(HasStateChanged) OVER (ORDER BY ID ROWS UNBOUNDED PRECEDING) AS GroupID
FROM CTE_PartTypeWithStateChange
)
SELECT MAX(PartType) AS PartType
,COUNT(PartType) AS Quantity
FROM CTE_PartTypeWithGroupID
GROUP BY GroupID
虽然代码要多一些,但这种方法确实可以减少源表上的读取次数,因为您没有执行任何自联接。这种方法还减少了查询必须执行的排序数量,这将提高大型数据集的性能 您使用的是什么版本的SQL Server?在提出问题之前您尝试了什么?SQL是基于集合的,除非您引入一个顺序,否则它不知道要到什么时候。您想订购什么-ID?我几乎想问为什么,因为你似乎是在以一种没有意义的方式组织数据。原因是因为我们有一条生产线,我们需要知道他在变更前生产了多少件,以便与我们的计划交叉核对,并在变更前验证他们是否达到了目标。好的,这意味着ID应该是一个时间戳,但是如果没有明确的字段,如果按顺序插入行,您确实可以使用ID作为近似值。就在几天前,我用自己的方式解决了类似的问题……是的,我误解了这个问题,我的错。我只是觉得他想为他的团队下订单。我将编辑答案,当前的答案不符合要求。效果非常好。非常感谢。聪明的事情不是它检测分区开关,而是RN1-RN2组实际上可以工作,不管分区是如何排列的。非常感谢。因为我的表有很多记录,所以每次请求都可以节省一些时间。