MSSQL Group直到类型更改

时间:2016-05-03 11:52:30

标签: sql-server

我目前有这张表:

╔════╦══════════╦════════════╗
║ 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 UNTIL对它们进行分组。所以它应该像这样输出:

╔══════════╦══════════╗
║ PartType ║ Quantity ║
╠══════════╬══════════╣
║ A        ║        4 ║
║ B        ║        2 ║
║ A        ║        1 ║
╚══════════╩══════════╝

4 个答案:

答案 0 :(得分:3)

你也可以使用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之间的差异发生变化时,它就是一种不同的类型。

答案 1 :(得分:3)

如果您使用的是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

虽然代码更多,但这种方法确实可以减少源表上的读取次数,因为您没有执行任何自联接。此方法还减少了查询必须执行的排序数,这应该可以提高较大数据集的性能。

答案 2 :(得分: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更改时使用了apply来获取下一个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)

答案 3 :(得分:0)

使用递归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)