2008-08-22 58 views
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 

@馬特:對不起,我遇到了麻煩,使用它的工作嵌入式HTML表格格式化數據在預覽中而不是在最終的顯示中。

回答

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

我認爲這會做你想要什麼。雖然是一個kludge的位。

唷!經過幾次編輯後,我認爲我已經整理了所有邊緣案例。

+0

that a group a填寫具有特定名稱的行。 OP希望將連續的行分組,我不知道在SQL中執行它的方式。 – Nickolay 2008-08-22 00:20:01

0

對於這種特殊的情況下,所有你需要做的是通過組的名稱,並要求計數,像這樣:

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

這會讓你的計數爲每名作爲第二列。

您可以通過連接這樣得到這一切爲一列:

select Name + ' (' + cast(count(*) as varchar) + ')' 
from MyTable 
group by Name 
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 

你真的想其他約翰·史密斯對結果的結束?

編輯:哦,我明白了,你想連續運行分組。在這種情況下,我會說你需要一個遊標或者在你的程序代碼中執行它。

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 
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 
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 
*/