分组数据运行
时间:2020-03-05 18:41:50 来源:igfitidea点击:
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表格格式化数据。
解决方案
回答
对于这种特殊情况,我们要做的就是按名称分组并要求计数,如下所示:
select Name, count(*) from MyTable group by Name
这样一来,我们就可以将每个名称的计数作为第二列。
我们可以通过以下方式将其全部合并为一列:
select Name + ' (' + cast(count(*) as varchar) + ')' from MyTable group by Name
回答
好吧,这:
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?
编辑:哦,我明白了,我们希望将连续运行分组。在这种情况下,我会说我们需要一个游标或者在程序代码中进行操作。
回答
试试这个:
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
回答
这个怎么样:
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
回答
我的解决方案只是踢(这是一个有趣的练习),没有游标,没有迭代,但是我确实有一个辅助字段
-- 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 */