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?
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.
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?
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)