回答:
このデータを変換する方法はいくつかあります。あなたのオリジナルのポストでは、と述べPIVOT
、このシナリオには複雑すぎるようだが、それは両方使用して非常に容易に適用することが可能UNPIVOT
とPIVOT
SQL Serverの機能を。
ただし、これらの関数にアクセスできない場合は、UNION ALL
to UNPIVOT
を使用してレプリケートし、CASE
ステートメントto を使用して集約関数を複製できます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);
ユニオンオール、アグリゲート、CASEバージョン:
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
UNION ALL
実行UNPIVOT
列を変換することにより、データのPaul, John, Tim, Eric
別の行に。次にsum()
、case
ステートメントを使用して集計関数を適用し、それぞれの新しい列を取得しますcolor
。
アンピボットとピボットの静的バージョン:
SQLサーバーの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
を使用した内部クエリ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)
動的バージョンの両方を照会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で列と行を転置するためのいくつかのソリューションを指摘したいと思います。
1つ目は、CURSORの使用です。専門家コミュニティにおける一般的なコンセンサスはSQL Serverカーソルから離れることですが、カーソルの使用が推奨される場合もあります。とにかく、カーソルは行を列に転置する別のオプションを提供します。
垂直拡張
PIVOTと同様に、カーソルには、データセットが拡張されてより多くのポリシー番号が含まれるようになると、行を追加する動的機能があります。
水平拡張
PIVOTとは異なり、スクリプトを変更せずに新しく追加されたドキュメントを含めるように拡張できるため、カーソルはこの領域で優れています。
パフォーマンスの内訳
CURSORを使用して行を列に転置することの主な制限は、一般にカーソルの使用に関連する欠点です-パフォーマンスコストがかなり高くなります。これは、カーソルがFETCH NEXT操作ごとに個別のクエリを生成するためです。
行を列に転置する別のソリューションは、XMLを使用することです。
行を列に転置するXMLソリューションは、基本的に、動的な列の制限に対処する点でPIVOTの最適なバージョンです。
スクリプトのXMLバージョンは、XMLパス、動的T-SQL、およびいくつかの組み込み関数(つまり、STUFF、QUOTENAME)の組み合わせを使用して、この制限に対処します。
垂直拡張
PIVOTおよびカーソルと同様に、新しく追加されたポリシーは、元のスクリプトを変更せずに、スクリプトのXMLバージョンで取得できます。
水平拡張
PIVOTとは異なり、新しく追加されたドキュメントは、スクリプトを変更せずに表示できます。
パフォーマンスの内訳
IOの点では、スクリプトのXMLバージョンの統計はPIVOTとほとんど同じです。唯一の違いは、XMLにdtTransposeテーブルの2番目のスキャンがありますが、今回は論理読み取りからのデータキャッシュです。
これらのソリューション(実際のT-SQLの例を含む)の詳細については、次の記事を参照してください。https://www.sqlshack.com/multiple-options-to-transposing-rows-into-columns/
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の回答に基づいて分割テキストを転置するために使用しているコードを共有したいと思います。このアプローチでは、MS SQL 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
このソリューションを、注文なしの行の注文方法に関する情報(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
このようにして、テーブル内のFilelds(Columns)からのすべてのデータをRecord(Row)に変換します。
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