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
Était-ce utile?

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.

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

scroll top