web-dev-qa-db-fra.com

Concaténer des lignes en une seule requête de chaîne exécutée pendant 5 heures et comptant

J'ai une table avec des enregistrements de 2,6 m. Cela ressemble à ceci:

email                           prject_name
[email protected]              lab1
[email protected]              lab2
[email protected]              lab3
[email protected]                     shift1
[email protected]                     shift2

Mais je veux que ma table ressemble à ceci:

email                     project_name
[email protected]     lab1, lab2, lab3
[email protected]            shift1, shift2, shift3

J'ai utilisé cette requête

select distinct email ,
STUFF((Select ','+project_name
from dbo.[UMG sent 2016] as  T1
where T1.email=T2.email
FOR XML PATH('')),1,1,'') from dbo.[UMG sent 2016] as T2;

Il fonctionne déjà depuis 5 heures.
Comment accélérer le processus?

5
user97340

Comme vous ne vous souciez pas de l'ordre des éléments concaténés, il serait assez facile de créer un agrégat CLR personnalisé pour ce faire et il exécutera probablement la méthode XML, il existe un exemple d'un dans ce article .

Cependant, vous pouvez apporter un changement rapide et facile à votre code existant.

Au lieu de

SELECT DISTINCT email,
                STUFF((SELECT ',' + project_name
                       FROM   dbo.[UMG sent 2016] AS T1
                       WHERE  T1.email = T2.email
                       FOR XML PATH('')), 1, 1, '')
FROM   dbo.[UMG sent 2016] AS T2; 

Vous pourriez utiliser

SELECT email,
       STUFF((SELECT ',' + project_name
              FROM   dbo.[UMG sent 2016] AS T1
              WHERE  T1.email = T2.email
              FOR XML PATH('')), 1, 1, '')
FROM   dbo.[UMG sent 2016] AS T2
GROUP  BY email; 

La différence étant que la première calcule des chaînes concaténées pour toutes les lignes dans [UMG sent 2016], puis supprime les doublons pour email,string. Le second trouve d'abord email distinct, puis effectue simplement le travail de concaténation de chaînes sur ces valeurs distinctes. Ainsi, dans vos données d'exemple, au lieu d'effectuer le travail 5 fois (deux fois pour le test et 3 fois pour Nadal), puis en jetant trois d'entre elles, il suffit d'effectuer le travail 2 fois, une fois pour chacune.

9
Martin Smith

Cette STUFF FOR XML PATH La technique de concaténation de chaînes est certes mignonne, mais elle n'est pas très évolutive et sur des millions de lignes, ce n'est probablement pas une très bonne idée. Pour les tables plus grandes, vous devrez peut-être écrire du bon SQL procédural à l'ancienne avec une boucle, quelque chose comme ceci:

-- Create the working table ...
IF OBJECT_ID('tempdb..#tmp') IS NOT NULL DROP TABLE #tmp

SELECT ROW_NUMBER() OVER( PARTITION BY email ORDER BY prject_name ) rowId, email, CAST( prject_name AS VARCHAR(500 ) ) prject_name
INTO #tmp
FROM dbo.[UMG sent 2016]
GO

-- Index temp table
CREATE UNIQUE CLUSTERED INDEX _cdx ON #tmp ( rowId, email )
GO

SELECT TOP 100 'before' s, *
FROM #tmp
ORDER BY email


-- Loop through appending the projects
DECLARE @n INT = 1

WHILE @@ROWCOUNT != 0
BEGIN

    IF @n > 99 BEGIN RAISERROR( 'Too many loops!', 16, 1 ) BREAK END    -- Loop safety
    SET @n += 1

    UPDATE t
    SET t.prject_name = CONCAT( t.prject_name, ', ', s.prject_name )
    FROM #tmp t
        INNER JOIN #tmp s ON t.email = s.email
    WHERE t.rowId = 1
      AND s.rowId = @n

END
GO

SELECT TOP 100 'after' s, *
FROM #tmp
WHERE rowId = 1
ORDER BY email

Le résultat concaténé se retrouve dans le "seau 1". Dans ma simple repro, avec 2,6 millions d'enregistrements avec entre 1 et 26 projets chacun, ce script s'est exécuté en quelques minutes. Script de repro complet ici .

Veuillez garder à l'esprit , ce modèle est optimisé pour les grands tableaux avec moins d'éléments à concaténer. Il repose également sur le fait que les combinaisons e-mail/projet sont uniques, d'où la clé primaire de ma repro. Il y aura un point de basculement où la technique STUFF sera plus rapide. Il existe également d'autres techniques telles que CLR, curseur même, qui pourraient convenir en fonction de la distribution de vos données.

Enfin, pouvez-vous m'en dire plus sur vos données afin que je puisse modifier ma repro? Par exemple, en moyenne, combien de projets chaque e-mail contient-il et à quoi ressemble la distribution?

7
wBob

Je sais que c'est un ancien article, mais vous avez presque certainement juste besoin d'ajouter un index sur email qui inclut prject_name

create index ixEmail on [UMG sent 2016](email) include (prject_name)
0
ubergeek