Pergunta

Considere uma tabela de banco de dados com nomes, com três linhas:

Peter
Paul
Mary

Existe uma maneira fácil de transformar isso em uma única seqüência de Peter, Paul, Mary?

Foi útil?

Solução

Se você estiver em SQL Server 2017 ou Azure, consulte Mathieu Renda resposta .

Eu tive um problema semelhante quando eu estava tentando unir duas tabelas com relacionamentos um-para-muitos. Em SQL 2005 Achei que o método XML PATH pode lidar com a concatenação das linhas com muita facilidade.

Se houver uma tabela chamada STUDENTS

SubjectID       StudentName
----------      -------------
1               Mary
1               John
1               Sam
2               Alaina
2               Edward

Resultado eu esperava era:

SubjectID       StudentName
----------      -------------
1               Mary, John, Sam
2               Alaina, Edward

Eu usei o seguinte T-SQL:

SELECT Main.SubjectID,
       LEFT(Main.Students,Len(Main.Students)-1) As "Students"
FROM
    (
        SELECT DISTINCT ST2.SubjectID, 
            (
                SELECT ST1.StudentName + ',' AS [text()]
                FROM dbo.Students ST1
                WHERE ST1.SubjectID = ST2.SubjectID
                ORDER BY ST1.SubjectID
                FOR XML PATH ('')
            ) [Students]
        FROM dbo.Students ST2
    ) [Main]

Você pode fazer a mesma coisa de uma forma mais compacta se você pode concat as vírgulas no início e no uso substring para ignorar o primeiro, assim você não precisa fazer uma sub-consulta:

SELECT DISTINCT ST2.SubjectID, 
    SUBSTRING(
        (
            SELECT ','+ST1.StudentName  AS [text()]
            FROM dbo.Students ST1
            WHERE ST1.SubjectID = ST2.SubjectID
            ORDER BY ST1.SubjectID
            FOR XML PATH ('')
        ), 2, 1000) [Students]
FROM dbo.Students ST2

Outras dicas

Esta resposta pode retornar para obter resultados consistentes, use um dos pARA XML métodos PATH detalhados em outras respostas.

Use COALESCE:

DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(@Names + ', ', '') + Name 
FROM People

Apenas alguma explicação (uma vez que esta resposta parece obter vistas relativamente regulares):

  • Coalesce é realmente apenas uma fraude útil que realiza duas coisas:

1) Não há necessidade de @Names initialize com um valor de cadeia vazia.

2) Não há necessidade de retirar um separador extra no final.

  • A solução acima irá dar resultados incorretos se uma linha tem um NULL valor Nome (se houver um NULL , o NULL fará @Names NULL , depois dessa linha e a próxima linha vai começar de novo como uma cadeia vazia novamente facilmente corrigido com uma das duas soluções:.
DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(@Names + ', ', '') + Name
FROM People
WHERE Name IS NOT NULL

ou

DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(@Names + ', ', '') + 
    ISNULL(Name, 'N/A')
FROM People

Dependendo do que o comportamento que você quer (a primeira opção apenas filtros NULL s para fora, a segunda opção mantém-los na lista com um marcador mensagem [substituir 'N / A' com o que é apropriado para você]).

Um método ainda não mostrado através do comando XML data() em MS SQL Server é:

Suponha tabela chamada NameList com uma coluna chamada FName,

SELECT FName + ', ' AS 'data()' 
FROM NameList 
FOR XML PATH('')

retornos:

"Peter, Paul, Mary, "

Apenas a vírgula extra deve ser tratada.

Editar: Como adotado a partir @ comentário de NReilingh, você pode usar o seguinte método para remover a vírgula no final. Assumindo que os mesmos nomes de tabelas e colunas:

STUFF(REPLACE((SELECT '#!' + LTRIM(RTRIM(FName)) AS 'data()' FROM NameList
FOR XML PATH('')),' #!',', '), 1, 2, '') as Brands

SQL Server 2017+ e SQL Azure: STRING_AGG

A partir da próxima versão do SQL Server, podemos finalmente concatenate em linhas sem ter que recorrer a qualquer variável ou XML witchery.

STRING_AGG (Transact-SQL)

Sem agrupamento

SELECT STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department;

Com agrupamento:

SELECT GroupName, STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department
GROUP BY GroupName;

Com o agrupamento e sub-ordenação

SELECT GroupName, STRING_AGG(Name, ', ') WITHIN GROUP (ORDER BY Name ASC) AS Departments
FROM HumanResources.Department 
GROUP BY GroupName;

SQL Server 2005

SELECT Stuff(
  (SELECT N', ' + Name FROM Names FOR XML PATH(''),TYPE)
  .value('text()[1]','nvarchar(max)'),1,2,N'')

No SQL Server 2016

Você pode usar o PARA JSON sintaxe

i.

SELECT per.ID,
Emails = JSON_VALUE(
   REPLACE(
     (SELECT _ = em.Email FROM Email em WHERE em.Person = per.ID FOR JSON PATH)
    ,'"},{"_":"',', '),'$[0]._'
) 
FROM Person per

E o resultado se tornará

Id  Emails
1   abc@gmail.com
2   NULL
3   def@gmail.com, xyz@gmail.com

Isto irá funcionar até mesmo seus dados contém caracteres XML inválidos

o '"},{"_":"' é seguro, porque se você dados contêm '"},{"_":"', será escapou para "},{\"_\":\"

Você pode substituir ', ' com qualquer separador corda


e no SQL Server 2017, SQL Azure banco de dados

Você pode usar o novo STRING_AGG função

No MySQL existe uma função, GROUP_CONCAT () , que permite que você concatenar os valores de várias linhas. Exemplo:

SELECT 1 AS a, GROUP_CONCAT(name ORDER BY name ASC SEPARATOR ', ') AS people 
FROM users 
WHERE id IN (1,2,3) 
GROUP BY a

Use COALESCE - Saiba mais a partir daqui

Para um exemplo:

102

103

104

Em seguida, escreva o código abaixo no sql server,

Declare @Numbers AS Nvarchar(MAX) -- It must not be MAX if you have few numbers 
SELECT  @Numbers = COALESCE(@Numbers + ',', '') + Number
FROM   TableName where Number IS NOT NULL

SELECT @Numbers

Saída seria:

102,103,104

matrizes Postgres são impressionantes. Exemplo:

Criar alguns dados de teste:

postgres=# \c test
You are now connected to database "test" as user "hgimenez".
test=# create table names (name text);
CREATE TABLE                                      
test=# insert into names (name) values ('Peter'), ('Paul'), ('Mary');                                                          
INSERT 0 3
test=# select * from names;
 name  
-------
 Peter
 Paul
 Mary
(3 rows)

agregá-los em uma matriz:

test=# select array_agg(name) from names;
 array_agg     
------------------- 
 {Peter,Paul,Mary}
(1 row)

Converter a matriz para uma string delimitada por vírgulas:

test=# select array_to_string(array_agg(name), ', ') from names;
 array_to_string
-------------------
 Peter, Paul, Mary
(1 row)

Concluído

Desde o PostgreSQL 9.0 é ainda mais fácil .

Oracle 11g Release 2 oferece suporte a função LISTAGG. Documentação aqui .

COLUMN employees FORMAT A50

SELECT deptno, LISTAGG(ename, ',') WITHIN GROUP (ORDER BY ename) AS employees
FROM   emp
GROUP BY deptno;

    DEPTNO EMPLOYEES
---------- --------------------------------------------------
        10 CLARK,KING,MILLER
        20 ADAMS,FORD,JONES,SCOTT,SMITH
        30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

3 rows selected.

Aviso

Tenha cuidado para implementar esta função se não houver possibilidade da string resultante vai mais de 4000 caracteres. Ele vai lançar uma exceção. Se for esse o caso, então você precisa se quer tratar a exceção ou rolar sua própria função que impede que a corda se juntou de ir mais de 4000 caracteres.

No SQL Server 2005 e, mais tarde, utilizar a consulta a seguir para concatenar as linhas.

DECLARE @t table
(
    Id int,
    Name varchar(10)
)
INSERT INTO @t
SELECT 1,'a' UNION ALL
SELECT 1,'b' UNION ALL
SELECT 2,'c' UNION ALL
SELECT 2,'d' 

SELECT ID,
stuff(
(
    SELECT ','+ [Name] FROM @t WHERE Id = t.Id FOR XML PATH('')
),1,1,'') 
FROM (SELECT DISTINCT ID FROM @t ) t

Eu não tenho acesso a um SQL Server em casa, por isso estou palpite sobre a sintaxe aqui, mas é mais ou menos:

DECLARE @names VARCHAR(500)

SELECT @names = @names + ' ' + Name
FROM Names

Uma solução CTE recursiva foi sugerido, mas nenhum código fornecido. O código abaixo é um exemplo de uma CTE recursiva - nota que, embora os resultados corresponder a questão, os dados não muito coincidir com a descrição dada, como eu suponho que você realmente quer fazer isso em grupos de linhas, nem todas as linhas na tabela. Mudá-lo para corresponder a todas as linhas na tabela é deixado como um exercício para o leitor.

;with basetable as 
(   SELECT id, CAST(name as varchar(max))name, 
        ROW_NUMBER() OVER(Partition By id     order by seq) rw, 
        COUNT(*) OVER (Partition By id) recs 
FROM (VALUES (1, 'Johnny', 1), (1,'M', 2), 
                  (2,'Bill', 1), (2, 'S.', 4), (2, 'Preston', 5), (2, 'Esq.', 6),
        (3, 'Ted', 1), (3,'Theodore', 2), (3,'Logan', 3),
                  (4, 'Peter', 1), (4,'Paul', 2), (4,'Mary', 3)

           )g(id, name, seq)
),
rCTE as (
    SELECT recs, id, name, rw from basetable where rw=1
    UNION ALL
    SELECT b.recs, r.ID, r.name +', '+ b.name name, r.rw+1
    FROM basetable b
         inner join rCTE r
    on b.id = r.id and b.rw = r.rw+1
)
SELECT name FROM rCTE
WHERE recs = rw and ID=4

Você precisa criar uma variável que irá realizar o seu resultado final e selecione para ele, assim.

mais fácil Solução

DECLARE @char VARCHAR(MAX);

SELECT @char = COALESCE(@char + ', ' + [column], [column]) 
FROM [table];

PRINT @char;

A partir do PostgreSQL 9.0 este é bastante simples:

select string_agg(name, ',') 
from names;

Em versões antes array_agg() 9,0 pode ser usado, como mostrado por hgmnz

No SQL Server vNext isso será construído com a função STRING_AGG, leia mais sobre ele aqui: https://msdn.microsoft.com/en-us/library/mt790580.aspx

Usando XML me ajudou na obtenção de linhas separados por vírgulas. Para a vírgula extra que pode usar a função substituir do SQL Server. Em vez de adicionar uma vírgula, o uso de 'dados ()' o AS irá concatenar as linhas com espaços, que mais tarde podem ser substituídos com vírgulas como a sintaxe escrito abaixo.

REPLACE(
        (select FName AS 'data()'  from NameList  for xml path(''))
         , ' ', ', ') 

Uma solução pronto-a-uso, sem vírgulas extras:

select substring(
        (select ', '+Name AS 'data()' from Names for xml path(''))
       ,3, 255) as "MyList"

Uma lista vazia irá resultar em valor NULL. Normalmente, você irá inserir a lista em uma variável de coluna de tabela ou programa: ajustar o comprimento máximo 255 com sua necessidade

.

(Diwakar e Jens Frandsen proporcionou boas respostas, mas a melhoria necessidade.)

SELECT STUFF((SELECT ', ' + name FROM [table] FOR XML PATH('')), 1, 2, '')

Aqui está um exemplo:

DECLARE @t TABLE (name VARCHAR(10))
INSERT INTO @t VALUES ('Peter'), ('Paul'), ('Mary')
SELECT STUFF((SELECT ', ' + name FROM @t FOR XML PATH('')), 1, 2, '')
--Peter, Paul, Mary
DECLARE @Names VARCHAR(8000)
SELECT @name = ''
SELECT @Names = @Names + ',' + Names FROM People
SELECT SUBSTRING(2, @Names, 7998)

Isso coloca a vírgula perdida no início.

No entanto, se você precisar de outras colunas, ou para CSV uma tabela filho que você precisa para quebrar isso em um usuário escalar campo definido (UDF).

Você pode usar o caminho XML como uma subconsulta correlacionada na cláusula SELECT também (mas eu teria que esperar até que eu voltar ao trabalho porque o Google não fazer coisas trabalho em casa: -)

Com as outras respostas, a pessoa que lê a resposta deve estar ciente de uma tabela de domínio específico, como veículo ou estudante. A tabela deve ser criado e preenchido com os dados para testar uma solução.

A seguir é um exemplo que usa SQL Server "INFORMATION_SCHEMA.COLUMNS" mesa. Ao utilizar esta solução, há mesas precisam ser criados ou dados acrescentou. Este exemplo cria uma vírgula lista de nomes de colunas separadas para todas as tabelas no banco de dados.

SELECT
    Table_Name
    ,STUFF((
        SELECT ',' + Column_Name
        FROM INFORMATION_SCHEMA.Columns Columns
        WHERE Tables.Table_Name = Columns.Table_Name
        ORDER BY Column_Name
        FOR XML PATH ('')), 1, 1, ''
    )Columns
FROM INFORMATION_SCHEMA.Columns Tables
GROUP BY TABLE_NAME 

Para o Oracle bancos de dados, consulte esta pergunta: Como várias linhas ser concatenados em um no Oracle sem criar um procedimento armazenado?

A melhor resposta parece ser por @Emmanuel, usando a função built-in LISTAGG (), disponível no Oracle 11g Release 2 e posterior.

SELECT question_id,
   LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM YOUR_TABLE;
GROUP BY question_id

como @ user762952 apontou, e de acordo com a documentação da Oracle http://www.oracle-base.com/articles/misc/string-aggregation-techniques.php , a função WM_CONCAT () é também uma opção. Parece estável, mas a Oracle recomenda explicitamente contra a usá-lo para qualquer SQL aplicativo, para uso em seu próprio risco.

Além disso, você terá que escrever sua própria função; o documento do Oracle acima tem um guia sobre como fazer isso.

Eu realmente gostei elegancy de resposta de Dana . Só queria torná-la completa.

DECLARE @names VARCHAR(MAX)
SET @names = ''

SELECT @names = @names + ', ' + Name FROM Names 

-- Deleting last two symbols (', ')
SET @sSql = LEFT(@sSql, LEN(@sSql) - 1)

Para evitar valores nulos você pode usar CONCAT ()

DECLARE @names VARCHAR(500)
SELECT @names = CONCAT(@names, ' ', name) 
FROM Names
select @names

Esta resposta vai exigir algum privilégio no servidor ao trabalho.

Assembléias são uma boa opção para você. Há um monte de sites que explicam como criá-la. O que eu acho que é muito bem explicado é este um

Se você quiser, eu já criou a montagem, e é possível fazer o download do DLL aqui .

Depois de ter baixado, você precisará executar o script a seguir no SQL Server:

CREATE Assembly concat_assembly 
   AUTHORIZATION dbo 
   FROM '<PATH TO Concat.dll IN SERVER>' 
   WITH PERMISSION_SET = SAFE; 
GO 

CREATE AGGREGATE dbo.concat ( 

    @Value NVARCHAR(MAX) 
  , @Delimiter NVARCHAR(4000) 

) RETURNS NVARCHAR(MAX) 
EXTERNAL Name concat_assembly.[Concat.Concat]; 
GO  

sp_configure 'clr enabled', 1;
RECONFIGURE

Observe que o caminho para a montagem pode ser acessível ao servidor. Desde que você tenha feito com sucesso todas as etapas, você pode usar a função como:

SELECT dbo.Concat(field1, ',')
FROM Table1

Espero que ajude !!!

Eu costumo usar escolha como este para cordas concatenar em SQL Server:

with lines as 
( 
  select 
    row_number() over(order by id) id, -- id is a line id
    line -- line of text.
  from
    source -- line source
), 
result_lines as 
( 
  select 
    id, 
    cast(line as nvarchar(max)) line 
  from 
    lines 
  where 
    id = 1 
  union all 
  select 
    l.id, 
    cast(r.line + N', ' + l.line as nvarchar(max))
  from 
    lines l 
    inner join 
    result_lines r 
    on 
      l.id = r.id + 1 
) 
select top 1 
  line
from
  result_lines
order by
  id desc

Se você quer lidar com valores nulos você pode fazê-lo através da adição de uma cláusula onde ou adicionar outro aglutinam em torno do primeiro.

DECLARE @Names VARCHAR(8000) 
SELECT @Names = COALESCE(COALESCE(@Names + ', ', '') + Name, @Names) FROM People

MySQL Exemplo completa:

Temos usuários que podem ter muitos dados e nós queremos ter uma saída, onde podemos ver todos os usuários Dados em uma lista:

Resultado:

___________________________
| id   |  rowList         |
|-------------------------|
| 0    | 6, 9             |
| 1    | 1,2,3,4,5,7,8,1  |
|_________________________|

Configuração de Tabela:

CREATE TABLE `Data` (
  `id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1;


INSERT INTO `Data` (`id`, `user_id`) VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 1),
(5, 1),
(6, 0),
(7, 1),
(8, 1),
(9, 0),
(10, 1);


CREATE TABLE `User` (
  `id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


INSERT INTO `User` (`id`) VALUES
(0),
(1);

Inquérito:

SELECT User.id, GROUP_CONCAT(Data.id ORDER BY Data.id) AS rowList FROM User LEFT JOIN Data ON User.id = Data.user_id GROUP BY User.id

No Oracle, é wm_concat. Eu acredito que esta função está disponível no 10g Release e superior.

Isto pode ser útil também

create table #test (id int,name varchar(10))
--use separate inserts on older versions of SQL Server
insert into #test values (1,'Peter'), (1,'Paul'), (1,'Mary'), (2,'Alex'), (3,'Jack')

DECLARE @t VARCHAR(255)
SELECT @t = ISNULL(@t + ',' + name, name) FROM #test WHERE id = 1
select @t
drop table #test

volta

Peter,Paul,Mary

Este método aplica-se ao banco de dados Teradata Aster apenas como ele utiliza a sua função nPath.

Mais uma vez, temos alunos de mesa

SubjectID       StudentName
----------      -------------
1               Mary
1               John
1               Sam
2               Alaina
2               Edward

Em seguida, com nPath é apenas único SELECT:

SELECT * FROM npath(
  ON Students
  PARTITION BY SubjectID
  ORDER BY StudentName
  MODE(nonoverlapping)
  PATTERN('A*')
  SYMBOLS(
    'true' as A
  )
  RESULT(
    FIRST(SubjectID of A) as SubjectID,
    ACCUMULATE(StudentName of A) as StudentName
  )
);

Resultado:

SubjectID       StudentName
----------      -------------
1               [John, Mary, Sam]
2               [Alaina, Edward]
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top