SQL Server 2000: Des idées pour effectuer concaténation d'agrégation sous-requête
-
18-09-2019 - |
Question
J'ai une requête qui renvoie des lignes que je veux, par exemple.
QuestionID QuestionTitle UpVotes DownVotes
========== ============= ======= =========
2142075 Win32: Cre... 0 0
2232727 Win32: How... 2 0
1870139 Wondows Ae... 12 0
Maintenant, je veux avoir un colonne de retour, qui contient une liste séparée par des virgules « Auteurs » (par exemple l'affiche originale et éditeurs). par exemple:.
QuestionID QuestionTitle UpVotes DownVotes Authors
========== ============= ======= ========= ==========
2142075 Win32: Cre... 0 0 Ian Boyd
2232727 Win32: How... 2 0 Ian Boyd, roygbiv
1870139 Wondows Ae... 12 0 Ian Boyd, Aaron Klotz, Jason Diller, danbystrom
faking
SQL Server 2000 ne dispose pas d'une opération d'agrégation de CONCAT(AuthorName, ', ')
, j'ai truquer -. Effectuer sous-requêtes simples pour l'auteur de TOP 1
, et le nombre auteur
QuestionID QuestionTitle UpVotes DownVotes FirstAuthor AuthorCount
========== ============= ======= ========= =========== ===========
2142075 Win32: Cre... 0 0 Ian Boyd 1
2232727 Win32: How... 2 0 Ian Boyd 2
1870139 Wondows Ae... 12 0 Ian Boyd 3
S'il y a plus d'un auteur, alors je montre l'utilisateur points de suspension ( « ... »), pour indiquer il y a plus d'un. par exemple. l'utilisateur verrait:
QuestionID QuestionTitle UpVotes DownVotes Authors
========== ============= ======= ========= ==========
2142075 Win32: Cre... 0 0 Ian Boyd
2232727 Win32: How... 2 0 Ian Boyd, …
1870139 Wondows Ae... 12 0 Ian Boyd, …
Et ça fonctionne assez bien, puisque normalement question est pas modifié - ce qui signifie que je soutiens le cas de 99% parfaitement, et le seul cas 1% de la moitié -assed ainsi.
Re-requête hiérarchique
En tant que plus complexe, et la solution sujette bug, je pensais à itérer la liste affichée, et tourner un thread de travail thread-pool pour chaque « question » dans la liste, effectuez une requête sur la base de données pour obtenir la liste des auteurs, l'agrégation puis la liste en mémoire. Cela signifie que la liste se remplit d'abord dans l'application (native). Alors j'émets quelques milliers de requêtes individuelles par la suite.
Mais ce serait horriblement, terriblement, terriblement, lent. Sans parler criblé de bugs, car il sera le travail de fil.
Ouais ouais ouais
Adam Mechanic dit tout à fait clairement :
Ne pas concaténer lignes dans délimité chaînes dans SQL Server. Faites-le client côté.
Dites-moi comment, et je vais le faire.
/ cri
Quelqu'un peut-il penser à une meilleure solution, qui est aussi rapide (par exemple ... dans un ordre de grandeur) que mon original "TOP 1 plus" solution ellipses?
Par exemple, est-il un moyen de retourner un ensemble de résultats, où la ligne de portée a un résultat associé établi? Donc, pour chaque ligne « maître », je pourrais obtenir à un résultat « détail » ensemble qui contient la liste.
Code pour la meilleure réponse
lien de Cade à solution d'Adam Machanic j'aime le meilleur. Une fonction définie par l'utilisateur, qui semble fonctionner via magique:
CREATE FUNCTION dbo.ConcatAuthors(@QuestionID int)
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE @Output VARCHAR(8000)
SET @Output = ''
SELECT @Output = CASE @Output
WHEN '' THEN AuthorName
ELSE @Output + ', ' + AuthorName
END
FROM (
SELECT QuestionID, AuthorName, QuestionDate AS AuthorDate FROM Questions
UNION
SELECT QuestionID, EditorName, EditDate FROM QuestionEdits
) dt
WHERE dt.QuestionID = @QuestionID
ORDER BY AuthorDate
RETURN @Output
END
Avec une utilisation T-SQL de:
SELECT QuestionID, QuestionTitle, UpVotes, DownVotes, dbo.ConcatAuthors(AuthorID)
FROM Questions
La solution
Jetez un oeil à ces articles:
http://dataeducation.com/rowset-string- concaténation-qui-est-procédé-best /
http: //www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/ (Voir les croix de Phil Factor rejoignent la solution dans les réponses - qui fonctionnera dans SQL server 2000)
Il est évident que dans SQL Server 2005, le FOR XML astuce est plus facile, plus souple et généralement le plus performant.
En ce qui concerne le retour d'un ensemble de lignes pour chaque ligne, si vous voulez toujours le faire pour une raison quelconque, vous pouvez le faire dans une procédure stockée, mais le client devra consommer toutes les lignes dans le premier ensemble de lignes et ensuite à la prochaine ligne de résultats et l'associer à la première ligne du premier ensemble de lignes, etc. Votre SP aurait besoin d'ouvrir un curseur sur le même ensemble, il est revenu que le premier ensemble de lignes et d'exécuter plusieurs sélectionne en séquence pour générer tous les jeux de lignes enfants. C'est une technique que je l'ai fait, mais seulement si ALL les données réellement nécessaires (était par exemple, dans une vue arborescente entièrement peuplée).
Et peu importe ce que les gens disent, le faire côté client est souvent un très grand gaspillage de bande passante, car le retour de toutes les lignes et de faire la boucle et la rupture du côté client signifie que grand nombre de colonnes identiques sont en cours de transfert à le début de chaque ligne juste pour obtenir la colonne changer à la fin de la ligne.
Où que vous le faites, il devrait être décision éclairée basé sur votre cas d'utilisation.
Autres conseils
J'ai essayé 3 approches de cette solution, celui affiché ici, les scripts et les fonctions UDF activex.
Le script le plus efficace (vitesse-sage) pour moi était bizzarely scénario Axtive-X exécutant plusieurs requêtes pour obtenir les données de additioanl à concat.
UDF a pris une moyenne de 22 minutes pour transformer, la méthode des sous-requêtes (affiché ici) a pris environ 5 m et le script activeX a 4m30, à mon ennui puisque ce fut le scénario que j'espérais fossé. Je dois voir si je peux gommer un peu plus d'efficacité ailleurs.
Je pense que les années 30 supplémentaires est utilisé par le tempdb utilisé pour stocker les données depuis mon script nécessite une commande par.
Il convient de noter que je concatanating d'énormes quantités de données textuelles.
Vous pouvez également jeter un oeil à cette . Il est essentiellement la jointure croisée approche Cade Roux a également mentionné dans son poste.
L'approche ci-dessus semble très propre: vous devez faire une vue d'abord et ensuite créer une instruction sur la base des valeurs dans la vue. La deuxième instruction SQL, vous pouvez construire dynamiquement dans votre code, il devrait être d'utiliser directement.
Je ne sais pas si cela fonctionne dans SQL Server 2000, mais vous pouvez essayer:
--combine parent and child, children are CSV onto parent row
CREATE TABLE #TableA (RowID int, Value1 varchar(5), Value2 varchar(5))
INSERT INTO #TableA VALUES (1,'aaaaa','A')
INSERT INTO #TableA VALUES (2,'bbbbb','B')
INSERT INTO #TableA VALUES (3,'ccccc','C')
CREATE TABLE #TableB (RowID int, TypeOf varchar(10))
INSERT INTO #TableB VALUES (1,'wood')
INSERT INTO #TableB VALUES (2,'wood')
INSERT INTO #TableB VALUES (2,'steel')
INSERT INTO #TableB VALUES (2,'rock')
INSERT INTO #TableB VALUES (3,'plastic')
INSERT INTO #TableB VALUES (3,'paper')
SELECT
a.*,dt.CombinedValue
FROM #TableA a
LEFT OUTER JOIN (SELECT
c1.RowID
,STUFF(
(SELECT
', ' + TypeOf
FROM (SELECT
a.RowID,a.Value1,a.Value2,b.TypeOf
FROM #TableA a
LEFT OUTER JOIN #TableB b ON a.RowID=b.RowID
) c2
WHERE c2.rowid=c1.rowid
ORDER BY c1.RowID, TypeOf
FOR XML PATH('')
)
,1,2, ''
) AS CombinedValue
FROM (SELECT
a.RowID,a.Value1,a.Value2,b.TypeOf
FROM #TableA a
LEFT OUTER JOIN #TableB b ON a.RowID=b.RowID
) c1
GROUP BY RowID
) dt ON a.RowID=dt.RowID
Sortie de SQL Server 2005:
RowID Value1 Value2 CombinedValue
----------- ------ ------ ------------------
1 aaaaa A wood
2 bbbbb B rock, steel, wood
3 ccccc C paper, plastic
(3 row(s) affected)
EDIT requête qui remplace POUR PATH XML avec FOR XML RAW, donc cela devrait fonctionner sur SQL Server 2000
SELECT
a.*,dt.CombinedValue
FROM #TableA a
LEFT OUTER JOIN (SELECT
c1.RowID
,STUFF(REPLACE(REPLACE(
(SELECT
', ' + TypeOf as value
FROM (SELECT
a.RowID,a.Value1,a.Value2,b.TypeOf
FROM #TableA a
LEFT OUTER JOIN #TableB b ON a.RowID=b.RowID
) c2
WHERE c2.rowid=c1.rowid
ORDER BY c1.RowID, TypeOf
FOR XML RAW
)
,'<row value="',''),'"/>','')
, 1, 2, '') AS CombinedValue
FROM (SELECT
a.RowID,a.Value1,a.Value2,b.TypeOf
FROM #TableA a
LEFT OUTER JOIN #TableB b ON a.RowID=b.RowID
) c1
GROUP BY RowID
) dt ON a.RowID=dt.RowID
SORTIE, même que la requête d'origine