将行连接成单个字符串查询,运行 5 小时并计数

use*_*340 5 sql-server t-sql group-concatenation

我有一张有 260 万条记录的表。它看起来像这样:

email                           prject_name
rafael.nadal@xyz.com              lab1
rafael.nadal@xyz.com              lab2
rafael.nadal@xyz.com              lab3
TEST@TEST.COM                     shift1
TEST@TEST.COM                     shift2
Run Code Online (Sandbox Code Playgroud)

但我希望我的桌子看起来像这样:

email                     project_name
rafael.nadal@xyz.com     lab1, lab2, lab3
TEST@TEST.COM            shift1, shift2, shift3
Run Code Online (Sandbox Code Playgroud)

我用过这个查询

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;
Run Code Online (Sandbox Code Playgroud)

它已经运行了5个小时。
如何加快流程?

Mar*_*ith 9

当你不关心的串接项目这将是顺序很容易磕了一个自定义的CLR聚合做到这一点,它很可能会出执行XML的方法,有一个在示例这篇文章

不过,您可以对现有代码进行快速简便的更改。

代替

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; 
Run Code Online (Sandbox Code Playgroud)

你可以用

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; 
Run Code Online (Sandbox Code Playgroud)

不同之处在于,第一个计算 中所有行的连接字符串[UMG sent 2016],然后删除 中的重复项email,string。第二个首先找到不同的email,然后只对这些不同的值执行字符串连接工作。因此,在您的示例数据中,而不是执行 5 次工作(测试两次,纳达尔执行 3 次),然后扔掉其中的三个,它只会执行 2 次工作,每次执行一次。


wBo*_*Bob 7

这种STUFF FOR XML PATH字符串连接技术确实很可爱,但它不能很好地扩展,而且在数百万行中它可能不是一个很好的主意。对于较大的表,您可能需要编写一些带有循环的老式程序 SQL,如下所示:

-- 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
Run Code Online (Sandbox Code Playgroud)

连接的结果都以“bucket 1”结尾。在我的简单重现中,有 260 万条记录,每个记录有 1 到 26 个项目,这个脚本在几分钟内运行。完整的复制脚本在这里

请记住,此模式针对要连接的项目较少的大型表进行了优化。它还依赖于电子邮件/项目组合的唯一性,因此是我复制中的主键。将有一个临界点,该STUFF技术更快。还有其他技术,例如 CLR、甚至游标,它们可能适合您的数据分布。

最后,您能否告诉我更多有关您的数据的信息,以便我可以调整我的重现?例如,平均每封电子邮件有多少个项目,分布是什么样的?