سؤال
خبراء 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
هل كنت تريد بالفعل أن يظهر جون سميث الآخر في نهاية نتائجك؟
يحرر:أوه أرى أنك تريد تجميع عمليات التشغيل المتتالية.في هذه الحالة، أود أن أقول أنك بحاجة إلى مؤشر أو للقيام بذلك في رمز البرنامج الخاص بك.
وماذا عن هذا:
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