125

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このシナリオには複雑すぎるようです。

4

9 に答える 9

164

このデータを変換する方法はいくつかあります。元の投稿では、このシナリオには複雑すぎるように思われると述べましたが、SQLServerのとPIVOT関数の両方を使用して非常に簡単に適用できます。UNPIVOTPIVOT

ただし、これらの関数にアクセスできない場合は、 toを使用して複製し、次に次のステートメントを使用UNION ALLUNPIVOTて集約関数を複製できます。CASEPIVOT

テーブルの作成:

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バージョン:

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 FiddlewithDemoを参照してください

は、列を個別の行に変換することにより、データのUNION ALLを実行します。次に、ステートメントを使用して集計関数を適用し、各の新しい列を取得します。UNPIVOTPaul, John, Tim, Ericsum()casecolor

UnpivotおよびPivotStaticバージョン:

UNPIVOTSQL Serverのと関数の両方により、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 FiddlewithDemoを参照してください

を使用した内部クエリ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 FiddlewithDemoを参照してください

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 |
于 2012-11-14T10:33:34.497 に答える
26

これには通常、すべての列と行のラベルを事前に知っておく必要があります。以下のクエリでわかるように、ラベルはすべて、UNPIVOT操作と(再)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 |

その他の注意事項:

  1. テーブル名を指定すると、 local-name()を使用してsys.columnsまたはFORXMLトリックからすべての列名を判別できます。
  2. FOR XMLを使用して、個別の色(または1つの列の値)のリストを作成することもできます。
  3. 上記を動的SQLバッチに組み合わせて、任意のテーブルを処理できます。
于 2012-11-14T02:58:55.903 に答える
11

SQLで列と行を転置するためのいくつかの解決策を指摘したいと思います。

1つ目は、CURSORを使用することです。専門家コミュニティの一般的なコンセンサスはSQLServerカーソルから離れることですが、カーソルの使用が推奨される場合もあります。とにかく、カーソルは行を列に転置する別のオプションを提供します。

  • 垂直方向の拡張

    PIVOTと同様に、カーソルには、データセットが拡張されてより多くのポリシー番号が含まれるようになると、より多くの行を追加する動的な機能があります。

  • 水平方向の拡張

    PIVOTとは異なり、カーソルは、スクリプトを変更せずに、新しく追加されたドキュメントを含めるように拡張できるため、この領域で優れています。

  • パフォーマンスの内訳

    CURSORを使用して行を列に転置することの主な制限は、一般にカーソルの使用に関連する欠点です。カーソルにはかなりのパフォーマンスコストがかかります。これは、カーソルがFETCHNEXT操作ごとに個別のクエリを生成するためです。

行を列に転置する別の解決策は、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-transposed-rows-into-columns/

于 2017-06-28T08:36:32.857 に答える
10

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'
于 2015-11-11T02:21:47.590 に答える
2

私は最初に実行し、結果をin操作UnPivotに保存しCTEて使用しています。CTEPivot

デモ

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;
于 2017-08-02T10:32:28.110 に答える
2

上記の@PacoZarateのすばらしい答えに加えて、複数のタイプの列を持つテーブルを転置する場合は、これを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
于 2018-01-19T18:25:38.237 に答える
2

このようにして、テーブル内のFilelds(列)からレコード(行)にすべてのデータを変換します。

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
于 2018-09-16T19:00:52.030 に答える
1

+bluefeetの回答に基づいて分割されたテキストを転置するために使用しているコードを共有するのが好きです。このアプローチでは、MSSQL2005のプロシージャとして実装されています

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
于 2016-05-03T15:33:59.203 に答える
0

Paco Zarateのソリューションを使用することができ、それは美しく機能します。1行追加する必要がありましたが( "SET ANSI_WARNINGS ON")、それは私がそれを使用または呼び出した方法に固有のものである可能性があります。私の使用法に問題があり、誰かがそれを手伝ってくれることを願っています:

このソリューションは、実際のSQLテーブルでのみ機能します。一時テーブルとメモリ内(宣言済み)テーブルで試してみましたが、それらでは機能しません。したがって、呼び出しコードでは、SQLデータベースにテーブルを作成してから、SQLTransposeを呼び出します。繰り返しますが、それはうまく機能します。それは私が欲しいものです。これが私の問題です:

ソリューション全体を真に動的にするには、SQLTransposeに送信する準備済みの情報を「オンザフライ」で一時的に保存するテーブルを作成し、SQLTransposeが呼び出されたらそのテーブルを削除する必要があります。テーブルの削除は、私の最終的な実装計画に問題をもたらしています。コードは、エンドユーザーアプリケーション(Microsoft Accessフォーム/メニューのボタン)から実行する必要があります。このSQLプロセスを使用すると(SQLテーブルを作成し、SQLTransposeを呼び出し、SQLテーブルを削除します)、使用するSQLアカウントにはテーブルを削除する権限がないため、エンドユーザーアプリケーションでエラーが発生します。

だから私はいくつかの可能な解決策があると思います:

  1. SQLTransposeを一時テーブルまたは宣言されたテーブル変数で機能させる方法を見つけてください。

  2. 実際のSQLテーブルを必要としない行と列の転置のための別の方法を考え出します。

  3. エンドユーザーが使用するSQLアカウントがテーブルを削除できるようにする適切な方法を見つけます。これは、Accessアプリケーションにコード化された単一の共有SQLアカウントです。許可は付与できないdboタイプの特権のようです。

私は、これのいくつかが別の別のスレッドと質問を正当化するかもしれないことを認識しています。ただし、1つの解決策が行と列の転置を行うための単純に異なる方法である可能性があるため、このスレッドで最初の投稿を行います。

編集:Pacoが提案したように、最後から6行目でsum(value)をmax(value)に置き換えました。

編集:

私は自分のために働く何かを見つけました。それがベストアンサーか どうかはわかりません。

階層化されたプロシージャを実行するために使用される読み取り専用のユーザーアカウントを持っているため、データベースからレポート出力を生成します。私が作成したSQLTranspose関数は、「正当な」テーブル(宣言されたテーブルでも一時的なテーブルでもない)でのみ機能するため、読み取り専用ユーザーアカウントでテーブルを作成(および後で削除)する方法を理解する必要がありました。 。

私の目的では、ユーザーアカウントにテーブルの作成を許可しても問題ないと考えました。ただし、ユーザーはまだテーブルを削除できませんでした。私の解決策は、ユーザーアカウントが承認されるスキーマを作成することでした。次に、そのテーブルを作成、使用、または削除するたびに、指定されたスキーマでテーブルを参照します。

私は最初に「sa」または「sysadmin」アカウントからこのコマンドを発行しました:CREATE SCHEMA ro AUTHORIZATION

「tmpoutput」テーブルを参照するときはいつでも、次の例のように指定します。

ドロップテーブルro.tmpoutput

于 2019-04-04T12:48:01.830 に答える