AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 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

Concatenação de várias colunas

  • 772

Como concatenar várias colunas em uma única linha? Por exemplo:

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

O conjunto de resultados esperado é:

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

Usando uma solução que encontrei no 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 

Existem soluções melhores? A seleção interna vem de uma união multi-tabela cara (não o 'teste' de tabela única mostrado acima). A consulta está em um TVF in-line, então não posso usar uma tabela temporária.

Além disso, se houver uma coluna em branco, os resultados produzirão vírgulas extras como

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

Existe alguma maneira de evitar isso?

sql-server sql-server-2008
  • 4 4 respostas
  • 18286 Views

4 respostas

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

    Fiz alguns testes usando um pouco mais de 6 mil linhas. Com um índice na coluna ID.

    Aqui está o que eu descobri.

    Sua consulta inicial:

    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 
    

    Este funcionou por ~ 23 minutos.

    Eu corri esta versão que é a versão que aprendi pela primeira vez. De certa forma, parece que deveria demorar mais, mas não demora.

    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
    

    Esta versão durou pouco mais de 2 minutos.

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

    Um agregado CLR quase certamente será a maneira mais rápida de fazer isso. Mas talvez você não queira usar um por qualquer motivo...

    Você diz que a fonte para isso é uma consulta cara.

    Eu materializaria isso em uma #temptabela primeiro para garantir que ela seja avaliada apenas uma vez.

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

    O plano de execução que obtenho para a consulta na pergunta primeiro faz a concatenação para cada linha na consulta externa e, em seguida, remove as duplicatas por id, SomeField_Combined1, SomeField_Combined2.

    Isso é incrivelmente um desperdício. A reescrita a seguir evita isso.

    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 
    

    No entanto, para os seguintes dados de teste (1000 ids com 2156 linhas por id para mim)

    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 
    

    Ainda encontrei a solução de Kenneth com duas XML PATHchamadas muito mais rápida e com menos recursos.

    +-----------------+--------------------+------------------------+------------------+---------------------+-------------------------+-----------------------------+
    |                 | 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                   |
    +-----------------+--------------------+------------------------+------------------+---------------------+-------------------------+-----------------------------+
    

    Para cada distinto id, #testele executa duas operações em vez de uma, mas essa operação é significativamente mais barata do que construir o XML e depois analisá-lo novamente.

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

    Como já apontado por Martin Smith, um agregado CLR é provavelmente sua melhor aposta. Novamente, armazenar seus resultados em uma tabela temporária é uma boa ideia.

    Aqui está outra possível implementação T-SQL que usa UNPIVOT/PIVOT:

    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;
    

    Ele é executado aproximadamente no mesmo tempo que a solução de Kenneth.

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

    Tente isso

    Use Righta função para remover a vírgula inicial em vez de xmlfunções

    Use caseinstruções para evitar vírgulas para espaços em branco

    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) 
    

    Nota: Aqui Group bytambém pode ser substituído por distinct, pois não estamos usando nenhuma aggregatefunção

    • 0

relate perguntas

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Quanto "Padding" coloco em meus índices?

  • Existe um processo do tipo "práticas recomendadas" para os desenvolvedores seguirem para alterações no banco de dados?

  • Como determinar se um Índice é necessário ou necessário

  • Downgrade do SQL Server 2008 para 2005

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

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

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve