terça-feira, 16 de abril de 2013

Converter várias linhas em uma string/varchar SQL Server

A dica da vez é sobre como converter várias linhas de um resultado de select por exemplo em uma linha só, separado por exemplo do ponto e virgula ";", no SQL Server.

O script é bem simples e pode ser transformado numa função que retorne string (varchar) para ser usada em outras queries.

No exemplo abaixo criei uma tabela de pessoas, com Id, Nome e Idade. No cenário quero recuperar numa única coluna e linha quais as pessoas com idade > 20, separadas por ponto e virgula.

Vamos lá?

--// Cria tabela de exemplo  num banco de dados do SQL Server
Create table Pessoas (Id Int Identity(1,1), Nome Varchar(100), Idade int)

--// Insere alguns valores na tabela para teste
Insert into Pessoas Values ('Andre', 20)
Insert into Pessoas Values ('Beatriz', 22)
Insert into Pessoas Values ('Carlos', 23)

--// Declara variável que receberá a lista (pode ser maior)
Declare @Result Varchar(200)

--// Inicia variável vazia por que varchar não concatena com null
Set @Result = ''

--// Seleciona cada registro que atende o select e concatena com a variável
Select @Result = @Result + Nome + '; '
  From Pessoas
 Where Idade > 20

--// Se houve resultado, retira o último caractere (;)
If @Result <> ''
Begin
   --// Retira o último caractere ";"
   Set @Result = SUBSTRING(@Result, 1, len(@Result)-1)
End

--// Retorna
Select @Result as Pessoas

No exemplo implementei ainda uma regra para tirar o ponto e virgula do final da variável. O resultado é o seguinte:
Resultado


Coisa simples, mas evita alguns loops, seja com cursor ou tabela temporária, que é a primeira coisa que pensamos diante de uma necessidade como essa.

Simples né? Espero que útil também!

8 comentários:

  1. É possível utilizar a função COALESCE (Transact-SQL) do próprio SQL Server. Aplicando ao seu exemplo ficaria algo assim:
    ...
    Select @Result = COALESCE (@Result + '; ', '') + Nome
    ...

    Dessa forma evitamos o "foreach" que será efetuado concatenando Nome à variável @Result a cada iteração. O COALESCE otimiza esse processo e proporciona performance quando lidamos com um volume maior de dados.
    Fica aí minha dica e agradecimento pela sua dica também Mario... me ajudou bastante. Hugs!!

    ResponderExcluir
    Respostas
    1. Jonatas, obrigado pela sua visita, pelo seu comentário e pela alternativa. Um abraço.

      Excluir
  2. Este comentário foi removido pelo autor.

    ResponderExcluir
  3. Pessoal, é possível fazer o contrario? transformar uma string separada por ponto e virgula em linhas?

    ResponderExcluir
    Respostas
    1. No SQL 2016 pode usar a função STRING_SPLIT (https://msdn.microsoft.com/en-us/library/mt684588.aspx), se for versão antes disso, procure por funções prontas que você possa criar no seu banco e que façam isso, tipo assim:

      CREATE FUNCTION [dbo].[fnSplitVarchar](@String VARCHAR(8000), @Delimiter CHAR(1)) RETURNS @Results TABLE (Item VARCHAR(2000), Linha INT) AS
      BEGIN
      DECLARE @Index INT
      DECLARE @Slice VARCHAR(5000)
      DECLARE @Linha INT

      SET @Index = 1
      SET @Linha = 0

      WHILE @Index != 0
      BEGIN
      SELECT @Index = CHARINDEX(@Delimiter, @String)

      IF @Index != 0
      BEGIN
      SET @Slice = LEFT(@String, @Index - 1)
      END
      ELSE
      BEGIN
      SET @Slice = @String
      END

      SET @Linha = @Linha + 1
      INSERT INTO @Results (Item, Linha)
      VALUES (@Slice, @Linha)

      SET @String = RIGHT(@String, LEN(@String) - @Index)

      IF LEN(@String) = 0
      BEGIN
      BREAK
      END
      END
      RETURN
      END

      ------------------------------------------------

      Daí é só usar assim que o resultado virá em tabela:

      select * from dbo.fnSplitVarchar('a,b,c', ',') where Item = 'a'

      Excluir