문제

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 테이블을 사용하여 데이터 형식을 지정하는 데 문제가 있었습니다. 미리보기에서는 작동했지만 최종 디스플레이에서는 작동하지 않았습니다.

도움이 되었습니까?

해결책

이 시도:

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

내 생각엔 그게 당신이 원하는 대로 될 것 같아요.그래도 약간의 문제입니다.

휴!몇 번 편집한 후에 모든 극단적인 경우가 정리된 것 같습니다.

다른 팁

나는 열정이 있는 커서를 싫어한다...하지만 여기에는 의심스러운 커서 버전이 있습니다.

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

단지 재미를 위한 내 솔루션(재미있는 연습이었습니다), 커서도 없고 반복도 없지만 도우미 필드가 있습니다.

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

음, 이것은:

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

실제로 결과가 끝나면 다른 John Smith를 원하셨나요?

편집하다:아, 알겠습니다. 연속 실행을 그룹화하고 싶으신가요?이 경우 커서가 필요하거나 프로그램 코드에서 커서를 사용해야 한다고 말하고 싶습니다.

이건 어때:

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

이 특별한 경우에 해야 할 일은 이름별로 그룹화하고 다음과 같이 개수를 요청하는 것뿐입니다.

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

그러면 각 이름의 개수가 두 번째 열로 표시됩니다.

다음과 같이 연결하면 모든 항목을 하나의 열로 가져올 수 있습니다.

select Name + ' (' + cast(count(*) as varchar) + ')'
from MyTable
group by Name
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top