AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 125771
Accepted
Biju jose
Biju jose
Asked: 2016-01-10 21:07:26 +0800 CST2016-01-10 21:07:26 +0800 CST 2016-01-10 21:07:26 +0800 CST

多列连接

  • 772

如何将多列连接成一行?例如:

id   name    car
1    sam     dodge
1    ram     maserati
1    john    benz
1    NULL    mazda
2    kirk    lexus
2    Jim     rolls
1            GMC

预期的结果集是:

ID  name               car
1   sam,ram,john       dodge,maserati,benz,mazda,GMC
2   kirk,jim           lexus,rolls

使用我在 Stack Overflow 上找到的解决方案:

SELECT * FROM (
SELECT t.id,stuff([m].query('/name').value('/', 'varchar(max)'),1,1,'') AS [SomeField_Combined1],
stuff([m].query('/car').value('/', 'varchar(max)'),1,1,'') AS [SomeField_Combined2]
FROM dbo.test t
OUTER apply(SELECT (

SELECT id, ','+name AS name
,','+car AS car
FROM test WHERE test.id=t.id
FOR XML PATH('') ,type)
             AS  M) A)S
GROUP BY id,somefield_combined1,somefield_combined2 

有没有更好的解决方案?内部选择来自昂贵的多表连接(不是上面显示的单表“测试”)。该查询位于内联 TVF 中,因此我无法使用临时表。

此外,如果有一个空白列,结果将产生额外的逗号,如

ID  name                car
1   sam,ram,john,,      dodge,maserati,benz,mazda,GMC
2   kirk,jim           lexus,rolls

有什么办法可以防止这种情况发生吗?

sql-server sql-server-2008
  • 4 4 个回答
  • 18286 Views

4 个回答

  • Voted
  1. Best Answer
    Kenneth Fisher
    2016-01-11T00:32:03+08:002016-01-11T00:32:03+08:00

    我使用超过 6 百万行进行了一些测试。在 ID 列上有一个索引。

    这是我想出的。

    您的初始查询:

    SELECT * FROM (
        SELECT t.id,
                stuff([M].query('/name').value('/', 'varchar(max)'),1,1,'') AS [SomeField_Combined1],
                stuff([M].query('/car').value('/', 'varchar(max)'),1,1,'') AS [SomeField_Combined2]
        FROM dbo.test t
        OUTER APPLY(SELECT (
                        SELECT id, ','+name AS name
                        ,','+car AS car
                        FROM test WHERE test.id=t.id
                        FOR XML PATH('') ,type)
                     AS  M) 
                M ) S
    GROUP BY id, SomeField_Combined1, SomeField_Combined2 
    

    这个跑了~23分钟。

    我运行了这个版本,这是我第一次学习的版本。在某些方面,它似乎需要更长的时间,但事实并非如此。

    SELECT test.id,
        STUFF((SELECT ', ' + ThisTable.name
                FROM   test ThisTable
                WHERE  test.id = ThisTable.id
                AND    ThisTable.name <> ''
                FOR XML PATH ('')),1,2,'') AS ConcatenatedSomeField,
        STUFF((SELECT ', ' + car
                FROM   test ThisTable
                WHERE  test.id = ThisTable.id
                AND    ThisTable.car <> ''
                FOR XML PATH ('')),1,2,'') AS ConcatenatedSomeField2
    FROM test 
    GROUP BY id
    

    这个版本只用了 2 多分钟。

    • 6
  2. Martin Smith
    2016-01-11T04:55:42+08:002016-01-11T04:55:42+08:00

    CLR 聚合几乎肯定是最快的方法。但是也许您出于某种原因不想使用一个...

    你说这个来源是一个昂贵的查询。

    我会#temp首先将其具体化为一个表格,以确保它只被评估一次。

    CREATE TABLE #test
    (
    ID INT,
    name NVARCHAR(128),
    car  NVARCHAR(128)
    );
    
    CREATE CLUSTERED INDEX ix ON #test(ID);
    

    我为问题中的查询获得的执行计划首先对外部查询中的每一行进行连接,然后删除重复项id, SomeField_Combined1, SomeField_Combined2。

    这是非常浪费的。下面的重写避免了这种情况。

    SELECT t.id,
           stuff([M].query('/name').value('/', 'varchar(max)'), 1, 1, '') AS [SomeField_Combined1],
           stuff([M].query('/car').value('/', 'varchar(max)'), 1, 1, '')  AS [SomeField_Combined2]
    FROM   (SELECT DISTINCT id
            FROM   #test) t
           OUTER APPLY(SELECT (SELECT id,
                                      ',' + name AS name,
                                      ',' + car  AS car
                               FROM   #test
                               WHERE  #test.id = t.id
                               FOR XML PATH(''), type) AS M) M 
    

    但是对于以下测试数据(对我来说,每个 id 有 1000 个 id,每个 id 有 2156 行)

    INSERT INTO #test
    SELECT v.number, o.name, o.type_desc
    FROM   sys.all_objects o
           INNER JOIN master..spt_values v
             ON v.type = 'P' AND v.number BETWEEN 1 AND 1000 
    

    我仍然发现 Kenneth 的解决方案有两个XML PATH调用,速度更快,资源消耗更少。

    +-----------------+--------------------+------------------------+------------------+---------------------+-------------------------+-----------------------------+
    |                 | CPU Time (Seconds) | Elapsed Time (Seconds) | #test Scan Count | #test Logical Reads | Worktable logical reads | Worktable lob logical reads |
    +-----------------+--------------------+------------------------+------------------+---------------------+-------------------------+-----------------------------+
    | Single XML PATH | 51.077             | 15.521                 | 1,005            | 60,165              | 51,161                  | 1,329,207                   |
    | Double XML PATH | 3.1720             | 3.010                  | 2,005            | 92,088              | 14,951                  |   233,681                   |
    +-----------------+--------------------+------------------------+------------------+---------------------+-------------------------+-----------------------------+
    

    对于其中的每个不同id,#test它执行两个操作而不是一个,但此操作比构造 XML 然后重新解析它要便宜得多。

    • 6
  3. spaghettidba
    2016-01-12T02:49:42+08:002016-01-12T02:49:42+08:00

    正如 Martin Smith 已经指出的那样,CLR 聚合可能是您最好的选择。同样,将结果存储在临时表中是个好主意。

    这是使用 UNPIVOT/PIVOT 的另一种可能的 T-SQL 实现:

    IF OBJECT_ID('tempdb..#test') IS NOT NULL 
        DROP TABLE #test;
    
    CREATE TABLE #test (
        id int,
        name varchar(128),
        car varchar(128)
    )
    
    CREATE CLUSTERED INDEX ix ON #test(ID);
    
    INSERT INTO #test
    SELECT v.number, o.name, o.type_desc
    FROM   sys.all_objects o
           INNER JOIN master..spt_values v
             ON v.type = 'P' AND v.number BETWEEN 1 AND 1000 
    
    ;WITH info(col) AS (
        SELECT 'car'
        UNION ALL
        SELECT 'name'
    )
    SELECT *
    FROM info
    CROSS JOIN (
        SELECT DISTINCT id
        FROM #test
    ) AS ids
    CROSS APPLY (
        SELECT v = STUFF((
            SELECT ',' + value AS [text()]
            FROM #test
            UNPIVOT (value FOR col IN (name,car)) AS u
            WHERE col = info.col
                AND id = ids.id
                AND value <> ''
            FOR XML PATH(''), type
        ).value('.','varchar(max)'),1,1,SPACE(0))
    ) AS ca(val)
    PIVOT (MIN(val) FOR col IN (car,name)) AS p;
    

    它的运行时间与 Kenneth 的解决方案大致相同。

    • 1
  4. Pரதீப்
    2016-01-10T23:36:46+08:002016-01-10T23:36:46+08:00

    尝试这个

    使用Right函数删除前导逗号而不是xml函数

    使用case语句来避免空格的逗号

    SELECT t.id, 
           RIGHT(A.NAME, Len(A.NAME) - 1) AS NAME, 
           RIGHT(A.car, Len(A.car) - 1)   AS car 
    FROM   dbo.test t 
           OUTER apply(SELECT (SELECT id, 
                                      CASE WHEN NAME<>'' THEN ',' ELSE '' END + NAME  AS NAME, 
                                      CASE WHEN car<>'' THEN ',' ELSE '' END + car AS car 
                               FROM   test 
                               WHERE  test.id = t.id 
                               FOR xml path(''), type) AS M) A 
    GROUP  BY id, 
              RIGHT(A.NAME, Len(A.NAME) - 1), 
              RIGHT(A.car, Len(A.car) - 1) 
    

    注意:这里Group by也可以替换distinct为,因为我们没有使用任何aggregate函数

    • 0

相关问题

  • 死锁的主要原因是什么,可以预防吗?

  • 我在索引上放了多少“填充”?

  • 是否有开发人员遵循数据库更改的“最佳实践”类型流程?

  • 如何确定是否需要或需要索引

  • 从 SQL Server 2008 降级到 2005

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve