SQLで列を単純に行に切り替えるにはどうすればよいですか。転置する簡単なコマンドはありますか?
すなわちこの結果を変える:
Paul | John | Tim | Eric
Red 1 5 1 3
Green 8 4 3 5
Blue 2 2 9 1
これに:
Red | Green | Blue
Paul 1 8 2
John 5 4 2
Tim 1 3 9
Eric 3 5 1
このシナリオではPIVOT
は複雑すぎるようです。
このデータを変換する方法はいくつかあります。あなたの最初の投稿で、PIVOT
はこのシナリオには複雑すぎるように思われると述べましたが、SQLの UNPIVOT
とPIVOT
の両方の関数を使って簡単に適用できます。サーバ。
ただし、これらの関数にアクセスできない場合は、UNION ALL
をUNPIVOT
に、次にCASE
文を含む集約関数をPIVOT
に使用して複製できます。
テーブル作成:
CREATE TABLE yourTable([color] varchar(5), [Paul] int, [John] int, [Tim] int, [Eric] int);
INSERT INTO yourTable
([color], [Paul], [John], [Tim], [Eric])
VALUES
('Red', 1, 5, 1, 3),
('Green', 8, 4, 3, 5),
('Blue', 2, 2, 9, 1);
Union All、Aggregate、CASE Version:
select name,
sum(case when color = 'Red' then value else 0 end) Red,
sum(case when color = 'Green' then value else 0 end) Green,
sum(case when color = 'Blue' then value else 0 end) Blue
from
(
select color, Paul value, 'Paul' name
from yourTable
union all
select color, John value, 'John' name
from yourTable
union all
select color, Tim value, 'Tim' name
from yourTable
union all
select color, Eric value, 'Eric' name
from yourTable
) src
group by name
デモ付きの SQL Fiddleを参照してください
UNION ALL
は、列Paul, John, Tim, Eric
を別々の行に変換することによって、データのUNPIVOT
を実行します。次に、集約関数sum()
をcase
ステートメントと共に適用して、各color
の新しい列を取得します。
アンピボットおよびピボット静的バージョン:
SQL ServerのUNPIVOT
関数とPIVOT
関数はどちらも、この変換をはるかに簡単にします。変換したい値がすべてわかっている場合は、それらを静的バージョンにハードコーディングして結果を得ることができます。
select name, [Red], [Green], [Blue]
from
(
select color, name, value
from yourtable
unpivot
(
value for name in (Paul, John, Tim, Eric)
) unpiv
) src
pivot
(
sum(value)
for color in ([Red], [Green], [Blue])
) piv
デモ付きの SQL Fiddleを参照してください
UNPIVOT
を持つ内部クエリは、UNION ALL
と同じ機能を実行します。これは列のリストを受け取り、それを行に変換します。そしてPIVOT
は最後の列への変換を行います。
動的ピボットバージョン:
未知の数の列(この例ではPaul, John, Tim, Eric
)があり、それから未知の数の変換する色がある場合は、動的SQLを使用してリストをUNPIVOT
、次にPIVOT
に生成できます。
DECLARE @colsUnpivot AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX),
@colsPivot as NVARCHAR(MAX)
select @colsUnpivot = stuff((select ','+quotename(C.name)
from sys.columns as C
where C.object_id = object_id('yourtable') and
C.name <> 'color'
for xml path('')), 1, 1, '')
select @colsPivot = STUFF((SELECT ','
+ quotename(color)
from yourtable t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query
= 'select name, '+@colsPivot+'
from
(
select color, name, value
from yourtable
unpivot
(
value for name in ('+@colsUnpivot+')
) unpiv
) src
pivot
(
sum(value)
for color in ('+@colsPivot+')
) piv'
exec(@query)
デモ付きの SQL Fiddleを参照してください
動的バージョンは、yourtable
とsys.columns
テーブルの両方を照会して、UNPIVOT
とPIVOT
への項目のリストを生成します。これは、実行されるクエリ文字列に追加されます。動的バージョンの利点は、colors
やnames
の変更リストがある場合、実行時にそのリストが生成されることです。
3つのクエリすべてが同じ結果になります。
| NAME | RED | GREEN | BLUE |
-----------------------------
| Eric | 3 | 5 | 1 |
| John | 5 | 4 | 2 |
| Paul | 1 | 8 | 2 |
| Tim | 1 | 3 | 9 |
これには通常、すべての列と行のラベルを事前に知っておく必要があります。以下のクエリからわかるように、ラベルはすべてUNPIVOT操作と(re)PIVOT操作の両方に完全に表示されています。
MS SQL Server 2012スキーマセットアップ:
create table tbl (
color varchar(10), Paul int, John int, Tim int, Eric int);
insert tbl select
'Red' ,1 ,5 ,1 ,3 union all select
'Green' ,8 ,4 ,3 ,5 union all select
'Blue' ,2 ,2 ,9 ,1;
クエリ1:
select *
from tbl
unpivot (value for name in ([Paul],[John],[Tim],[Eric])) up
pivot (max(value) for color in ([Red],[Green],[Blue])) p
結果:
| NAME | RED | GREEN | BLUE |
-----------------------------
| Eric | 3 | 5 | 1 |
| John | 5 | 4 | 2 |
| Paul | 1 | 8 | 2 |
| Tim | 1 | 3 | 9 |
SQLで列と行を入れ替えるためのさらにいくつかのソリューションを指摘したいと思います。
最初のものは - CURSORを使うことです。専門家コミュニティにおける一般的なコンセンサスはSQL Serverのカーソルから離れることですが、それでもカーソルの使用を推奨する場合があります。とにかく、Cursorsには、行を列に変換するための別のオプションがあります。
縦方向の拡大
PIVOTと同様に、カーソルには、データセットが拡張してより多くのポリシー番号が含まれるようになるにつれて、より多くの行を追加する動的な機能があります。
水平方向の拡大
PIVOTとは異なり、カーソルはこの領域で優れています。スクリプトを変更することなく、新しく追加した文書を含めることができるためです。
掲載結果の内訳
CURSORを使用して行を列に転置することの主な制限は、一般にカーソルの使用に関連した不利な点です。それらは、かなりのパフォーマンス上のコストがかかります。これは、Cursorが各FETCH NEXT操作に対して別々の問合せを生成するためです。
行を列に転置するもう1つの解決策は、XMLを使用することです。
行を列に置き換えるためのXMLソリューションは、動的な列の制限に対処するという点で、基本的にPIVOTの最適なバージョンです。
XMLバージョンのスクリプトは、XMLパス、動的T-SQL、およびいくつかの組み込み関数(つまり、STUFF、QUOTENAME)の組み合わせを使用してこの制限に対処しています。
縦方向の拡大
PIVOTやCursorと同様に、新しく追加されたポリシーは、元のスクリプトを変更することなくXMLバージョンのスクリプトで取得できます。
水平方向の拡大
PIVOTとは異なり、新しく追加した文書はスクリプトを変更せずに表示できます。
掲載結果の内訳
IOの点では、XMLバージョンのスクリプトの統計はPIVOTとほぼ同じです。唯一の違いは、XMLにはdtTransposeテーブルの2回目のスキャンがあることですが、今回は論理読み取りデータキャッシュから行われます。
この記事では、これらの解決策(実際のT-SQLの例を含む)について、さらに詳しく説明しています。 https://www.sqlshack.com/multiple-options-to-transposing-rows-列へ/
bluefeet からのこの 解 に基づいて、動的を使用するストアドプロシージャがあります。転置テーブルを生成するためのsql。転置列(結果表のヘッダーとなる列)を除いて、すべてのフィールドが数値であることが必要です。
/****** Object: StoredProcedure [dbo].[SQLTranspose] Script Date: 11/10/2015 7:08:02 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: Paco Zarate
-- Create date: 2015-11-10
-- Description: SQLTranspose dynamically changes a table to show rows as headers. It needs that all the values are numeric except for the field using for transposing.
-- Parameters: @TableName - Table to transpose
-- @FieldNameTranspose - Column that will be the new headers
-- Usage: exec SQLTranspose <table>, <FieldToTranspose>
-- =============================================
ALTER PROCEDURE [dbo].[SQLTranspose]
-- Add the parameters for the stored procedure here
@TableName NVarchar(MAX) = '',
@FieldNameTranspose NVarchar(MAX) = ''
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE @colsUnpivot AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX),
@queryPivot AS NVARCHAR(MAX),
@colsPivot as NVARCHAR(MAX),
@columnToPivot as NVARCHAR(MAX),
@tableToPivot as NVARCHAR(MAX),
@colsResult as xml
select @tableToPivot = @TableName;
select @columnToPivot = @FieldNameTranspose
select @colsUnpivot = stuff((select ','+quotename(C.name)
from sys.columns as C
where C.object_id = object_id(@tableToPivot) and
C.name <> @columnToPivot
for xml path('')), 1, 1, '')
set @queryPivot = 'SELECT @colsResult = (SELECT '',''
+ quotename('+@columnToPivot+')
from '+@tableToPivot+' t
where '+@columnToPivot+' <> ''''
FOR XML PATH(''''), TYPE)'
exec sp_executesql @queryPivot, N'@colsResult xml out', @colsResult out
select @colsPivot = STUFF(@colsResult.value('.', 'NVARCHAR(MAX)'),1,1,'')
set @query
= 'select name, rowid, '+@colsPivot+'
from
(
select '+@columnToPivot+' , name, value, ROW_NUMBER() over (partition by '+@columnToPivot+' order by '+@columnToPivot+') as rowid
from '+@tableToPivot+'
unpivot
(
value for name in ('+@colsUnpivot+')
) unpiv
) src
pivot
(
sum(value)
for '+@columnToPivot+' in ('+@colsPivot+')
) piv
order by rowid'
exec(@query)
END
このコマンドで提供されるテーブルでそれをテストすることができます。
exec SQLTranspose 'yourTable', 'color'
最初にUnPivot
を行い、その結果をCTE
に格納し、CTE
をPivot
操作に使用します。
with cte as
(
select 'Paul' as Name, color, Paul as Value
from yourTable
union all
select 'John' as Name, color, John as Value
from yourTable
union all
select 'Tim' as Name, color, Tim as Value
from yourTable
union all
select 'Eric' as Name, color, Eric as Value
from yourTable
)
select Name, [Red], [Green], [Blue]
from
(
select *
from cte
) as src
pivot
(
max(Value)
for color IN ([Red], [Green], [Blue])
) as Dtpivot;
上記の@Paco Zarateの素晴らしい答えに追加します。複数の種類の列を持つテーブルを転置したい場合は、これを39行目の末尾に追加するので、int
列のみが転置されます。
and C.system_type_id = 56 --56 = type int
これは、変更されている完全なクエリです。
select @colsUnpivot = stuff((select ','+quotename(C.name)
from sys.columns as C
where C.object_id = object_id(@tableToPivot) and
C.name <> @columnToPivot and C.system_type_id = 56 --56 = type int
for xml path('')), 1, 1, '')
他のsystem_type_id
を見つけるには、これを実行してください:
select name, system_type_id from sys.types order by name
私は+ bluefeetの答えに基づいて分割されたテキストを転置するのに使っているコードを共有したいです。このアプローチで私はMSSQL 2005でプロシージャとして実装されています
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: ELD.
-- Create date: May, 5 2016.
-- Description: Transpose from rows to columns the user split function.
-- =============================================
CREATE PROCEDURE TransposeSplit @InputToSplit VARCHAR(8000)
,@Delimeter VARCHAR(8000) = ','
AS
BEGIN
SET NOCOUNT ON;
DECLARE @colsUnpivot AS NVARCHAR(MAX)
,@query AS NVARCHAR(MAX)
,@queryPivot AS NVARCHAR(MAX)
,@colsPivot AS NVARCHAR(MAX)
,@columnToPivot AS NVARCHAR(MAX)
,@tableToPivot AS NVARCHAR(MAX)
,@colsResult AS XML
SELECT @tableToPivot = '#tempSplitedTable'
SELECT @columnToPivot = 'col_number'
CREATE TABLE #tempSplitedTable (
col_number INT
,col_value VARCHAR(8000)
)
INSERT INTO #tempSplitedTable (
col_number
,col_value
)
SELECT ROW_NUMBER() OVER (
ORDER BY (
SELECT 100
)
) AS RowNumber
,item
FROM [DB].[ESCHEME].[fnSplit](@InputToSplit, @Delimeter)
SELECT @colsUnpivot = STUFF((
SELECT ',' + quotename(C.NAME)
FROM [tempdb].sys.columns AS C
WHERE C.object_id = object_id('tempdb..' + @tableToPivot)
AND C.NAME <> @columnToPivot
FOR XML path('')
), 1, 1, '')
SET @queryPivot = 'SELECT @colsResult = (SELECT '',''
+ quotename(' + @columnToPivot + ')
from ' + @tableToPivot + ' t
where ' + @columnToPivot + ' <> ''''
FOR XML PATH(''''), TYPE)'
EXEC sp_executesql @queryPivot
,N'@colsResult xml out'
,@colsResult OUT
SELECT @colsPivot = STUFF(@colsResult.value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET @query = 'select name, rowid, ' + @colsPivot + '
from
(
select ' + @columnToPivot + ' , name, value, ROW_NUMBER() over (partition by ' + @columnToPivot + ' order by ' + @columnToPivot + ') as rowid
from ' + @tableToPivot + '
unpivot
(
value for name in (' + @colsUnpivot + ')
) unpiv
) src
pivot
(
MAX(value)
for ' + @columnToPivot + ' in (' + @colsPivot + ')
) piv
order by rowid'
EXEC (@query)
DROP TABLE #tempSplitedTable
END
GO
私はこの解決策を、(by = SQLAuthority.com )とMSDNの分割関数( social.msdn.Microsoft.com )
売買を実行すると
DECLARE @RC int
DECLARE @InputToSplit varchar(MAX)
DECLARE @Delimeter varchar(1)
set @InputToSplit = 'hello|beautiful|world'
set @Delimeter = '|'
EXECUTE @RC = [TransposeSplit]
@InputToSplit
,@Delimeter
GO
次の結果が得られます
name rowid 1 2 3
col_value 1 hello beautiful world
この方法では、テーブルのフィールド(列)からすべてのデータをレコード(行)に変換します。
Declare @TableName [nvarchar](128)
Declare @ExecStr nvarchar(max)
Declare @Where nvarchar(max)
Set @TableName = 'myTableName'
--Enter Filtering If Exists
Set @Where = ''
--Set @ExecStr = N'Select * From '+quotename(@TableName)+@Where
--Exec(@ExecStr)
Drop Table If Exists #tmp_Col2Row
Create Table #tmp_Col2Row
(Field_Name nvarchar(128) Not Null
,Field_Value nvarchar(max) Null
)
Set @ExecStr = N' Insert Into #tmp_Col2Row (Field_Name , Field_Value) '
Select @ExecStr += (Select N'Select '''+C.name+''' ,Convert(nvarchar(max),'+quotename(C.name) + ') From ' + quotename(@TableName)+@Where+Char(10)+' Union All '
from sys.columns as C
where (C.object_id = object_id(@TableName))
for xml path(''))
Select @ExecStr = Left(@ExecStr,Len(@ExecStr)-Len(' Union All '))
--Print @ExecStr
Exec (@ExecStr)
Select * From #tmp_Col2Row
Go
私はPaco Zarateのソリューションを使用することができました、そしてそれは美しく働きます。 1行追加する必要がありました( "SET ANSI_WARNINGS ON")が、それは私がそれを使用したり呼び出したりした方法に特有のものかもしれません。私の用法に問題があります、そして私は誰かがそれを私に手伝ってくれることを望みます:
解決策は、実際のSQLテーブルでのみ機能します。一時テーブルとインメモリ(宣言された)テーブルで試してみましたが、それらでは動作しません。だから私の呼び出しコードで私は私のSQLデータベース上にテーブルを作成してからSQLTransposeを呼び出します。繰り返しますが、それは素晴らしい作品です。私が欲しいのはそれだけです。これが私の問題です:
全体的なソリューションを真に動的にするためには、SQLTransposeに送信した準備済み情報を一時的に「その場で」保存するテーブルを作成し、SQLTransposeが呼び出されたらそのテーブルを削除する必要があります。テーブルの削除は、私の最終的な実装計画に問題を引き起こしています。コードは、エンドユーザーアプリケーション(Microsoft Accessのフォーム/メニューのボタン)から実行する必要があります。このSQLプロセス(SQLテーブルの作成、SQLTransposeの呼び出し、SQLテーブルの削除)を使用すると、使用するSQLアカウントにテーブルを削除する権限がないため、エンドユーザーアプリケーションでエラーが発生します。
だから私はいくつかの可能な解決策があると思います:
SQLTransposeを一時テーブルまたは宣言されたテーブル変数で機能させる方法を見つけます。
実際のSQLテーブルを必要としない行と列の入れ替えのための別の方法を考え出してください。
エンドユーザーが使用しているSQLアカウントがテーブルを削除できるようにする適切な方法を見つけ出します。これは、私のAccessアプリケーションにコーディングされた単一の共有SQLアカウントです。許可は付与できないdboタイプの特権のようです。
私はこれのいくつかが別の、別のスレッドと質問を正当化するかもしれないことを認識します。ただし、1つの解決策が単に行と列の入れ替えを行うための異なる方法になる可能性があるので、このスレッドでここで最初の投稿をします。
編集:私はまたPacoが示唆したように、最後から6行目でsum(value)をmax(value)に置き換えました。
編集:
私は私のために働く何かを考え出した。それが最良の答えかどうかはわかりません。
ストアドプロシージャを実行するために使用される読み取り専用のユーザーアカウントを持っているため、データベースからレポート出力が生成されます。作成したSQLTranspose関数は「正当な」テーブル(宣言されたテーブルでも一時テーブルでもない)でのみ機能するので、読み取り専用のユーザーアカウントでテーブルを作成してから後で削除する方法を考え出す必要がありました。 。
私の目的では、ユーザーアカウントがテーブルを作成することを許可されていても問題ないと考えました。それでもユーザーはテーブルを削除できませんでした。私の解決策は、ユーザーアカウントが承認されているスキーマを作成することでした。その後、そのテーブルを作成、使用、削除するたびに、指定されたスキーマでそれを参照します。
私は最初に 'sa'または 'sysadmin'アカウントからこのコマンドを発行しました。CREATE SCHEMA ro AUTHORIZATION
いつでも私が私の "tmpoutput"テーブルを参照するとき、私はこの例のようにそれを指定します:
ドロップテーブルro.tmpoutput