Como faço para transformar colunas SQL em linhas?
-
08-06-2019 - |
Pergunta
Estou com um problema muito simples que requer uma solução muito rápida e simples no SQL Server 2005.
Eu tenho uma tabela com x colunas.Quero poder selecionar uma linha da tabela e depois transformar as colunas em linhas.
TableA
Column1, Column2, Column3
Instrução SQL para ruturn
ResultA
Value of Column1
Value of Column2
Value of Column3
@Kevin: Fiz uma pesquisa no Google sobre o assunto, mas muitos exemplos eram excessivamente complexos para o meu exemplo, você pode ajudar mais?
@Mário:A solução que estou criando possui 10 colunas que armazenam os valores de 0 a 6 e devo calcular quantas colunas possuem o valor 3 ou mais.Então pensei em criar uma consulta para transformar isso em linhas e depois usar a tabela gerada em uma subconsulta para contar o número de linhas com Coluna >= 3
Solução
Você deve dar uma olhada na cláusula UNPIVOT.
Atualização1:GateKiller, por incrível que pareça, li um artigo (sobre algo não relacionado) sobre isso esta manhã e estou tentando refrescar minha memória onde o vi novamente, também tive alguns exemplos decentes.Isso voltará para mim, tenho certeza.
Atualização2:Encontrei: http://weblogs.sqlteam.com/jeffs/archive/2008/04/23/unpivot.aspx
Outras dicas
Eu tive que fazer isso para um projeto antes.Uma das maiores dificuldades que tive foi explicar o que estava tentando fazer para outras pessoas.Passei muito tempo tentando fazer isso em SQL, mas achei a função pivot terrivelmente inadequada.Não me lembro exatamente o motivo, mas é muito simplista para a maioria dos aplicativos e não está totalmente implementado no MS SQL 2000.Acabei escrevendo uma função pivot em .NET.Vou postar aqui na esperança de que algum dia ajude alguém.
''' <summary>
''' Pivots a data table from rows to columns
''' </summary>
''' <param name="dtOriginal">The data table to be transformed</param>
''' <param name="strKeyColumn">The name of the column that identifies each row</param>
''' <param name="strNameColumn">The name of the column with the values to be transformed from rows to columns</param>
''' <param name="strValueColumn">The name of the column with the values to pivot into the new columns</param>
''' <returns>The transformed data table</returns>
''' <remarks></remarks>
Public Shared Function PivotTable(ByVal dtOriginal As DataTable, ByVal strKeyColumn As String, ByVal strNameColumn As String, ByVal strValueColumn As String) As DataTable
Dim dtReturn As DataTable
Dim drReturn As DataRow
Dim strLastKey As String = String.Empty
Dim blnFirstRow As Boolean = True
' copy the original data table and remove the name and value columns
dtReturn = dtOriginal.Clone
dtReturn.Columns.Remove(strNameColumn)
dtReturn.Columns.Remove(strValueColumn)
' create a new row for the new data table
drReturn = dtReturn.NewRow
' Fill the new data table with data from the original table
For Each drOriginal As DataRow In dtOriginal.Rows
' Determine if a new row needs to be started
If drOriginal(strKeyColumn).ToString <> strLastKey Then
' If this is not the first row, the previous row needs to be added to the new data table
If Not blnFirstRow Then
dtReturn.Rows.Add(drReturn)
End If
blnFirstRow = False
drReturn = dtReturn.NewRow
' Add all non-pivot column values to the new row
For Each dcOriginal As DataColumn In dtOriginal.Columns
If dcOriginal.ColumnName <> strNameColumn AndAlso dcOriginal.ColumnName <> strValueColumn Then
drReturn(dcOriginal.ColumnName.ToLower) = drOriginal(dcOriginal.ColumnName.ToLower)
End If
Next
strLastKey = drOriginal(strKeyColumn).ToString
End If
' Add new columns if needed and then assign the pivot values to the proper column
If Not dtReturn.Columns.Contains(drOriginal(strNameColumn).ToString) Then
dtReturn.Columns.Add(drOriginal(strNameColumn).ToString, drOriginal(strValueColumn).GetType)
End If
drReturn(drOriginal(strNameColumn).ToString) = drOriginal(strValueColumn)
Next
' Add the final row to the new data table
dtReturn.Rows.Add(drReturn)
' Return the transformed data table
Return dtReturn
End Function
UNIÃO deveria ser seu amigo:
SELECT Column1 FROM table WHERE idColumn = 1
UNION ALL
SELECT Column2 FROM table WHERE idColumn = 1
UNION ALL
SELECT Column3 FROM table WHERE idColumn = 1
mas pode também seja seu inimigo em grandes conjuntos de resultados.
Se você tem um conjunto fixo de colunas e sabe o que são, basicamente pode fazer uma série de subseleções
(SELECT Column1 AS ResultA FROM TableA) as R1
e junte-se às subseleções.Tudo isso em uma única consulta.
Não tenho certeza da sintaxe do SQL Server para isso, mas no MySQL eu faria
SELECT IDColumn, ( IF( Column1 >= 3, 1, 0 ) + IF( Column2 >= 3, 1, 0 ) + IF( Column3 >= 3, 1, 0 ) + ... [snip ] )
AS NumberOfColumnsGreaterThanThree
FROM TableA;
EDITAR:Uma pesquisa muito (muito) breve no Google me diz que o CASE
declaração faz o que estou fazendo com o IF
declaração no MySQL.Você pode ou não aproveitar o resultado do Google que encontrei
EDIÇÃO ADICIONAL:Devo salientar também que esta não é uma resposta à sua pergunta, mas uma solução alternativa para o seu problema real.
SELECT IDColumn,
NumberOfColumnsGreaterThanThree = (CASE WHEN Column1 >= 3 THEN 1 ELSE 0 END) +
(CASE WHEN Column2 >= 3 THEN 1 ELSE 0 END) +
(Case WHEN Column3 >= 3 THEN 1 ELSE 0 END)
FROM TableA;