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

キック(これは楽しいエクササイズでした)、カーソル、反復なしの私の解決策ですが、ヘルパーフィールドがあります

-- 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 に答える
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 に答える
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

これにより、2 番目の列として各名前の数が取得されます。

次のように連結することで、すべてを 1 つの列として取得できます。

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