web-dev-qa-db-ja.com

SQLで固定列なしでピボットする方法は?

非常に単純なテーブルのピボットに苦労しています。 Web上のすべての例とチュートリアルは私が探しているものではないので、皆さんがここで私を助けることができるかもしれません(私のT-SQLの知識はそれほど良くないと言っておく必要があります...)

状況を説明しましょう:

私はテーブルを持っています

CREATE TABLE [dbo].[Trucks](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](100) NOT NULL) 

SET IDENTITY_INSERT [dbo].[Trucks] ON 
INSERT [dbo].[Trucks] ([Id], [Name]) VALUES (1, N'AAA-BBB')
INSERT [dbo].[Trucks] ([Id], [Name]) VALUES (2, N'AAA-CCC')
INSERT [dbo].[Trucks] ([Id], [Name]) VALUES (3, N'BBB-WWW')
INSERT [dbo].[Trucks] ([Id], [Name]) VALUES (4, N'SKL-POL')
INSERT [dbo].[Trucks] ([Id], [Name]) VALUES (5, N'QAS-ZSD')
SET IDENTITY_INSERT [dbo].[Trucks] OFF 

各トラックにはコンパートメントがいくつかあります。各コンパートメントには名前と容量があります

CREATE TABLE [dbo].[Compartments](
    [Id] [int]  IDENTITY(1,1) NOT NULL,
    [TruckId] [int] NOT NULL,
    [Compartment] [nvarchar](50) NOT NULL,
    [Capacity] [bigint] NULL)
SET IDENTITY_INSERT [dbo].[Compartments] ON 
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (1, 1, N'C1', 5000)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (2, 1, N'C2', 4000)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (3, 1, N'C3', 5000)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (4, 1, N'C4', 4000)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (5, 1, N'C5', 6000)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (6, 2, N'Vak 1', 6000)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (8, 2, N'Vak 2', 6000)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (9, 2, N'Vak 3', 5000)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (10, 2, N'Vak 4', 5000)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (11, 3, N'1', 500)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (12, 3, N'2', 500)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (13, 3, N'3', 500)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (14, 3, N'4', 500)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (15, 3, N'5', 500)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (16, 3, N'6', 500)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (17, 3, N'7', 500)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (18, 3, N'8', 500)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (19, 3, N'9', 500)
INSERT [dbo].[Compartments] ([Id], [TruckId], [Compartment], [Capacity]) VALUES (20, 3, N'10', 500)
SET IDENTITY_INSERT [dbo].[Compartments] OFF 

特定のトラックのコンパートメントのリストを返すSPを(TruckIdに基づいて)作成したいと思います。これは簡単な部分です:

SELECT * FROM COMPARTMENTS WHERE TruckId = @p_TruckId

これにより、次のようなテーブルが作成されます。

Results of query

私がやりたいことは、ヘッダーとしてコンパートメント名、値として容量を持つテーブルを返すことです。

これは、値が1行だけのテーブルになります。列の数は、その特定のトラックのコンパートメントの数によって異なります。

PIVOT関数を調べましたが、そのためには事前に列数を知っておく必要があります。動的なPIVOTの例も見つけましたが、自分の状況でそれを機能させることができません。

ここで誰かが私を助けてくれますか?いいね!

編集: 私が見つけた例へのリンク

5
Mr. T.

動的SQLクエリを作成する前に、構文を正しく取得できるように、常にハードコードされたバージョンを作成する必要があります。そのため、最初に行う必要があるのは、必要なTruckId値のいずれかに対して機能するPIVOTクエリを記述することです。

静的バージョン:

あなたが必要だとしましょうTruckID = 3PIVOTのコードは次のようになります。

select Name, TruckId, [1], [2], [3], [4], [5], [6]
from
(
  select Name, TruckId, Compartment, Capacity
  from Trucks t
  inner join Compartments c
    on t.Id = c.TruckId
  where t.Id = 3  -- your truck id here
) d
pivot
(
  max(capacity)
  for compartment in ([1], [2], [3], [4], [5], [6])  -- your Compartment values here
) p;

SQLを参照Fiddleデモあり

動的バージョン:

ここでの問題は、CompartmentsにさまざまなTruckId値を設定することになるため、TruckIdおよびは、必要な結果を生成します。 PIVOT関数は機能しますが、最初にSQL文字列を各CompartmentのすべてのTruckId値と連結する必要があります。

まず、すべてのパラメーターを定義します。

DECLARE 
  @cols AS NVARCHAR(MAX),
  @query  AS NVARCHAR(MAX),
  @ParmDefinition NVARCHAR(500),
  @TruckId as int;

set @TruckId = 3;  -- this would be the value you submit via your SP
set @ParmDefinition = '@id int';

次に、 FOR XML PATH および [〜#〜] stuff [〜#〜] を使用して、新しい列ヘッダーのリストを連結します。

select @cols = STUFF((SELECT ',' + QUOTENAME(Compartment) 
                    from #Compartments
                    where TruckId = @TruckId
                    group by id, Compartment
                    order by Id
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'');

列のリストを取得したら、実行される完全なSQL文字列を作成します。これは上記の静的バージョンに似ているはずです。

set @query 
    = N'SELECT Name, TruckId, ' + @cols + N' 
        from 
        (
            select t.Name, c.TruckId, c.Compartment, c.Capacity
            from #Trucks t
            inner join #Compartments c
              on t.Id = c.TruckId
            where t.Id = @id
        ) x
        pivot 
        (
            max(Capacity)
            for Compartment in (' + @cols + N')
        ) p ';

最後のステップは、sql文字列の実行です。

exec sp_executesql @query, @ParmDefinition, @id = @TruckId;

デモを見る 。これにより、次の結果が得られます。

Name    TruckId 1   2   3   4   5   6   7   8   9   10  
------- ------- --- --- --- --- --- --- --- --- --- --- 
BBB-WWW 3       500 500 500 500 500 500 500 500 500 500 
10
Taryn