代码之家  ›  专栏  ›  技术社区  ›  Nic Strong

分组数据运行

sql
  •  7
  • Nic Strong  · 技术社区  · 16 年前

    SQL专家,

    有没有一种使用SQL将数据运行分组的有效方法?
    或者用代码处理数据更有效。

    例如,如果我有以下数据:

    ID|Name
    01|Harry Johns
    02|Adam Taylor
    03|John Smith
    04|John Smith
    05|Bill Manning
    06|John Smith
    

    我需要显示这个:

    Harry Johns
    Adam Taylor
    John Smith (2)
    Bill Manning
    John Smith
    

    @马特:对不起,我在使用嵌入的HTML表格式化数据时遇到了问题。它在预览时工作,但在最终显示时不工作。

    6 回复  |  直到 10 年前
        1
  •  2
  •   Matt Hamilton    16 年前

    试试这个:

    select n.name, 
        (select count(*) 
         from myTable n1
         where n1.name = n.name and n1.id >= n.id and (n1.id <=
            (
            select isnull(min(nn.id), (select max(id) + 1 from myTable))
            from myTable nn
            where nn.id > n.id and nn.name <> n.name
            )
         ))
    from myTable n
    where not exists (
       select 1
       from myTable n3
       where n3.name = n.name and n3.id < n.id and n3.id > (
                select isnull(max(n4.id), (select min(id) - 1 from myTable))
                from myTable n4
                where n4.id < n.id and n4.name <> n.name
                )
    )
    

    我想这会满足你的需要。不过,有点含混不清。

    唷!经过几次编辑,我想我已经整理好了所有的边缘案例。

        2
  •  2
  •   Leon Bambrick jon Z    16 年前

    我讨厌充满激情的诅咒…但这里有一个狡猾的光标版本…

    Declare @NewName Varchar(50)
    Declare @OldName Varchar(50)
    Declare @CountNum int
    Set @CountNum = 0
    
    DECLARE nameCursor CURSOR FOR 
    SELECT Name
    FROM NameTest
    OPEN nameCursor
    
    FETCH NEXT FROM nameCursor INTO @NewName
    
      WHILE @@FETCH_STATUS = 0 
    
        BEGIN
    
          if @OldName <> @NewName
          BEGIN
             Print @OldName + ' (' + Cast(@CountNum  as Varchar(50)) + ')'
             Set @CountNum = 0
          END
          SELECT @OldName = @NewName
          FETCH NEXT FROM nameCursor INTO @NewName
          Set @CountNum = @CountNum + 1
    
        END
    Print @OldName + ' (' + Cast(@CountNum  as Varchar(50)) + ')'
    
    CLOSE nameCursor
    DEALLOCATE nameCursor
    
        3
  •  2
  •   Jon Erickson    16 年前

    我的解决方案只是为了踢(这是一个有趣的练习),没有光标,没有迭代,但是我有一个助手字段

    -- Setup test table
    DECLARE @names TABLE    (
                            id      INT                 IDENTITY(1,1),
                            name    NVARCHAR(25)        NOT NULL,
                            grp     UNIQUEIDENTIFIER    NULL
                            )
    
    INSERT @names (name)
    SELECT 'Harry Johns'    UNION ALL 
    SELECT 'Adam Taylor'    UNION ALL
    SELECT 'John Smith'     UNION ALL
    SELECT 'John Smith'     UNION ALL
    SELECT 'Bill Manning'   UNION ALL
    SELECT 'Bill Manning'   UNION ALL
    SELECT 'Bill Manning'   UNION ALL
    SELECT 'John Smith'     UNION ALL
    SELECT 'Bill Manning'   
    
    -- Set the first id's group to a newid()
    UPDATE      n
    SET         grp = newid()
    FROM        @names n
    WHERE       n.id = (SELECT MIN(id) FROM @names)
    
    -- Set the group to a newid() if the name does not equal the previous
    UPDATE      n
    SET         grp = newid()
    FROM        @names n
    INNER JOIN  @names b
            ON  (n.ID - 1) = b.ID
            AND ISNULL(b.Name, '') <> n.Name
    
    -- Set groups that are null to the previous group
    -- Keep on doing this until all groups have been set
    WHILE (EXISTS(SELECT 1 FROM @names WHERE grp IS NULL))
    BEGIN
        UPDATE      n
        SET         grp = b.grp
        FROM        @names n
        INNER JOIN  @names b
                ON  (n.ID - 1) = b.ID
                AND n.grp IS NULL
    END
    
    -- Final output
    SELECT      MIN(id)     AS id_start,
                MAX(id)     AS id_end,
                name,
                count(1)    AS consecutive
    FROM        @names
    GROUP BY    grp, 
                name
    ORDER BY    id_start
    
    /*
    Results:
    
    id_start    id_end  name            consecutive
    1           1       Harry Johns     1
    2           2       Adam Taylor     1
    3           4       John Smith      2
    5           7       Bill Manning    3
    8           8       John Smith      1
    9           9       Bill Manning    1
    */
    
        4
  •  1
  •   Blorgbeard    16 年前

    嗯,这个:

    select Name, count(Id)
    from MyTable
    group by Name
    

    会给你这个:

    Harry Johns, 1
    Adam Taylor, 1
    John Smith, 2
    Bill Manning, 1
    

    这个(MS SQL语法):

    select Name +
        case when ( count(Id) > 1 ) 
             then ' ('+cast(count(Id) as varchar)+')' 
             else ''
        end
    from MyTable
    group by Name
    

    会给你这个:

    Harry Johns
    Adam Taylor
    John Smith (2)
    Bill Manning
    

    你真的想让另一个约翰·史密斯知道你的结果吗?

    编辑:哦,我明白了,你想要连续的跑步分组。在这种情况下,我会说您需要一个光标,或者在您的程序代码中这样做。

        5
  •  1
  •   Eric Z Beard    16 年前

    这个怎么样?

    declare @tmp table (Id int, Nm varchar(50));
    
    insert @tmp select 1, 'Harry Johns';
    insert @tmp select 2, 'Adam Taylor';
    insert @tmp select 3, 'John Smith';
    insert @tmp select 4, 'John Smith';
    insert @tmp select 5, 'Bill Manning';
    insert @tmp select 6, 'John Smith';
    
    select * from @tmp order by Id;
    
    select Nm, count(1) from 
    (
    select Id, Nm, 
        case when exists (
            select 1 from @tmp t2 
            where t2.Nm=t1.Nm 
            and (t2.Id = t1.Id + 1 or t2.Id = t1.Id - 1)) 
            then 1 else 0 end as Run
    from @tmp t1
    ) truns group by Nm, Run
    

    [编辑]可以缩短一点

    select Nm, count(1) from (select Id, Nm, case when exists (
            select 1 from @tmp t2 where t2.Nm=t1.Nm 
            and abs(t2.Id-t1.Id)=1) then 1 else 0 end as Run
    from @tmp t1) t group by Nm, Run
    
        6
  •  0
  •   Matt Hamilton    16 年前

    对于这个特殊的情况,您所需要做的就是按名称分组并请求计数,如下所示:

    select Name, count(*)
    from MyTable
    group by Name
    

    这将把每个名称的计数作为第二列。

    您可以通过如下连接将其全部作为一列:

    select Name + ' (' + cast(count(*) as varchar) + ')'
    from MyTable
    group by Name