如何在TSQL中没有固定列的情况下进行透视?

Mr.*_* T. 8 sql-server pivot t-sql

我正在努力旋转一个非常简单的表格。网络上的所有示例和教程,都不是我要找的,所以也许你们可以在这里帮助我(我必须说我的 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 
Run Code Online (Sandbox Code Playgroud)

每辆卡车都有一定数量的隔间。每个隔间都有名称和容量

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 
Run Code Online (Sandbox Code Playgroud)

我想编写一个 SP,它返回某个卡车的隔间列表(基于 TruckId)。这是简单的部分:

SELECT * FROM COMPARTMENTS WHERE TruckId = @p_TruckId
Run Code Online (Sandbox Code Playgroud)

这给了我一个看起来像这样的表:

查询结果

我想要做的是返回一个以隔间名称作为标题和容量作为值的表。

这将是一个只有 1 行值的表格。列的数量取决于给定卡车的隔间数量。

我查看了 PIVOT 函数,但您需要提前知道列数。我还找到了一个动态 PIVOT 示例,但我似乎无法让它在我的情况下工作。

有人可以在这里帮助我吗?那很好啊!

编辑:我找到的例子的链接

Tar*_*ryn 13

在编写动态 SQL 查询之前,您应该始终编写一个硬编码的版本,以便您可以获得正确的语法。因此,您需要做的第一件事是为TruckId您需要的任何值编写一个有效的 PIVOT 查询。

静态版本:

假设您需要TruckID = 3,您的代码PIVOT将类似于以下内容:

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;
Run Code Online (Sandbox Code Playgroud)

参见 SQL Fiddle with Demo

动态版本:

现在,您的问题是您将拥有Compartment任何TruckIds的各种值,因此您需要编写一些可以接受任何值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';
Run Code Online (Sandbox Code Playgroud)

接下来,您将使用FOR XML PATHSTUFF连接新列标题的列表:

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,'');
Run Code Online (Sandbox Code Playgroud)

获得列列表后,您将创建将要执行的完整 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 ';
Run Code Online (Sandbox Code Playgroud)

最后一步是执行sql字符串:

exec sp_executesql @query, @ParmDefinition, @id = @TruckId;
Run Code Online (Sandbox Code Playgroud)

查看演示。这给出了以下结果:

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 
Run Code Online (Sandbox Code Playgroud)

  • FWIW,SQL Server 2017 支持`STRING_AGG`,无需`STUFF`/`FOR XML` hack。 (3认同)