IMO, l'approche de conception est ce qui rend cela difficile. Le simple fait que vous autorisiez les utilisateurs à attribuer des tags ne signifie pas que les tags doivent être stockés dans une liste de mots délimitée. Vous pouvez normaliser la structure en quelque chose comme:
Create Table Posts (Id ... not null primary key)
Create Table Tags(Id ... not null primary key, Name ... not null Unique)
Create Table PostTags
(PostId ... not null References Posts(Id)
, TagId ... not null References Tags(Id))
Maintenant, votre question devient trivial:
Select T.Id, T.Name, Count(*) As TagCount
From PostTags As PT
Join Tags As T
On T.Id = PT.TagId
Group By T.Id, T.Name
Order By Count(*) Desc
Si vous insistez sur le stockage des balises comme valeurs délimitées, seule solution est de diviser les valeurs de leur delimiter en écrivant une fonction Split personnalisée, puis faites votre compte. En bas se trouve un exemple de fonction Split. Avec elle, votre requête ressemblerait à quelque chose comme (en utilisant un séparateur virgule):
Select Tag.Value, Count(*) As TagCount
From Posts As P
Cross Apply dbo.Split(P.Tags, ',') As Tag
Group By Tag.Value
Order By Count(*) Desc
Fonction Split:
Create Function [dbo].[Split]
(
@DelimitedList nvarchar(max)
, @Delimiter nvarchar(2) = ','
)
RETURNS TABLE
AS
RETURN
(
With CorrectedList As
(
Select Case When Left(@DelimitedList, DataLength(@Delimiter)/2) <> @Delimiter Then @Delimiter Else '' End
+ @DelimitedList
+ Case When Right(@DelimitedList, DataLength(@Delimiter)/2) <> @Delimiter Then @Delimiter Else '' End
As List
, DataLength(@Delimiter)/2 As DelimiterLen
)
, Numbers As
(
Select TOP (Coalesce(Len(@DelimitedList),1)) Row_Number() Over (Order By c1.object_id) As Value
From sys.objects As c1
Cross Join sys.columns As c2
)
Select CharIndex(@Delimiter, CL.list, N.Value) + CL.DelimiterLen As Position
, Substring (
CL.List
, CharIndex(@Delimiter, CL.list, N.Value) + CL.DelimiterLen
, Case
When CharIndex(@Delimiter, CL.list, N.Value + 1)
- CharIndex(@Delimiter, CL.list, N.Value)
- CL.DelimiterLen < 0 Then Len(CL.List)
Else CharIndex(@Delimiter, CL.list, N.Value + 1)
- CharIndex(@Delimiter, CL.list, N.Value)
- CL.DelimiterLen
End
) As Value
From CorrectedList As CL
Cross Join Numbers As N
Where N.Value < Len(CL.List)
And Substring(CL.List, N.Value, CL.DelimiterLen) = @Delimiter
)
Vous auriez besoin d'utiliser la manipulation de chaînes pour convertir chaque Sentance à un ensemble de mots. Si vous créez une fonction de valeur de table qui accepte une chaîne et affiche une table de mots, vous pouvez utiliser 'myData CROSS APPLY myFunction (myTable.sentance)', puis utiliser un GROUP BY pour tout compter. Exactement ce que les règles seraient nécessaires pour casser une phrase en mots individuels, je vais laisser à vous ou à d'autres :) – MatBailie