7

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

@Matt:抱歉,我在使用嵌入的 html 表格格式化数据时遇到了问题,它在预览中有效,但在最终显示中无效。

4

6 回答 6

2

试试这个:

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
            )
)

我认为那会做你想要的。不过有点杂乱无章。

呸!经过几次编辑,我想我已经整理出了所有的边缘情况。

于 2008-08-22T00:57:08.157 回答
2

我讨厌带有激情的光标……但这是一个狡猾的光标版本……

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
于 2008-08-22T01:08:59.690 回答
2

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

-- 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
*/
于 2008-08-22T04:22:17.477 回答
1

嗯,这个:

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

你真的想要另一个约翰史密斯在你的结果结束吗?

编辑:哦,我明白了,您希望连续运行分组。在这种情况下,我会说您需要一个光标或在您的程序代码中执行它。

于 2008-08-22T00:25:27.033 回答
1

这个怎么样:

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
于 2008-08-22T01:17:34.007 回答
0

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

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

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

您可以通过这样的连接将其全部作为一列:

select Name + ' (' + cast(count(*) as varchar) + ')'
from MyTable
group by Name
于 2008-08-22T00:16:00.940 回答