如何在 sql 中创建计算枢轴
how to create calculated pivot in sql
场景
XYZ 公司销售产品。公司通过检查完成整个订单需要多长时间来衡量他们的绩效。每个订单都会经历多种状态(示例:打开、待处理、关闭)
他们计算每个状态的天数,以了解订单处于特定状态的天数。天数以两种不同的方式计算,工作日和日历日
请参考以下table:
问题
如何将这个table变成下图所示的枢轴table?还有如何添加到额外的列,这是每个工作日和工作日状态的总和。
期望的结果:
你可以这样做:
SELECT *
FROM
(
SELECT
OrderID,
OrderStatus + CountType AS StatusType,
DayCount
FROM CalendarTable
UNION ALL
SELECT
OrderID,
CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END,
DayCount
FROM CalendarTable
) AS t
PIVOT
(
MAX(DayCount)
For StatusType IN(OpenWorking,
OpenCalendar,
CloseWorking,
CloseCalendar,
PendingWorking,
PendingCalendar,
TotalWorking,
TotalCalendar)
) AS p;
这会给你:
如果不想手动记下所有状态,可以动态记下:
声明@cols 为 NVARCHAR(MAX);
DECLARE @query AS NVARCHAR(MAX);
SELECT @cols = STUFF((SELECT distinct ',' +
QUOTENAME(StatusType)
FROM
(
SELECT
OrderID,
OrderStatus + CountType AS StatusType,
DayCount
FROM CalendarTable
UNION ALL
SELECT
OrderID,
CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END,
DayCount
FROM CalendarTable
) AS t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT @query = 'SELECT *
FROM
(
SELECT
OrderID,
OrderStatus + CountType AS StatusType,
DayCount
FROM CalendarTable
UNION ALL
SELECT
OrderID,
CASE WHEN CountType = ''Working'' THEN ''TotalWorking'' ELSE ''TotalCalendar'' END,
DayCount
FROM CalendarTable
) AS t
PIVOT
(
MAX(DayCount)
For StatusType IN(' + @cols + ')' +
') p';
execute(@query);
更新:
对于列名,您可以创建一个新变量 @colnames
并用您想要的名称填充它。对于总计,您可以添加一个 WHERE
子句以仅获取活动状态和待定状态的总计。所以你的查询将是这样的:
DECLARE @cols AS NVARCHAR(MAX);
DECLARE @colnames AS NVARCHAR(MAX);
DECLARE @query AS NVARCHAR(MAX);
SELECT @cols = STUFF((SELECT distinct ',' +
QUOTENAME(StatusType)
FROM
(
SELECT
OrderID,
OrderStatus + CountType AS StatusType,
DayCount
FROM CalendarTable
UNION ALL
SELECT
OrderID,
CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END,
DayCount
FROM CalendarTable
WHERE OrderStatus IN('Active', 'Pending')
) AS t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT @colnames = STUFF((SELECT distinct ',' +
QUOTENAME(StatusType) + ' AS ' + QUOTENAME(StatusTypeName)
FROM
(
SELECT
OrderID,
OrderStatus + CountType AS StatusType,
DayCount,
OrderStatus + CASE WHEN CountType = 'Working' THEN 'WorkDays' ELSE 'CalDays' END AS StatusTypeName
FROM CalendarTable
UNION ALL
SELECT
OrderID,
CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END,
DayCount,
CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END
FROM CalendarTable
WHERE OrderStatus IN('Active', 'Pending')
) AS t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT @query = 'SELECT OrderID , ' + @colnames + '
FROM
(
SELECT
OrderID,
OrderStatus + CountType AS StatusType,
DayCount
FROM CalendarTable
UNION ALL
SELECT
OrderID,
CASE WHEN CountType = ''Working'' THEN ''TotalWorking'' ELSE ''TotalCalendar'' END,
DayCount
FROM CalendarTable
WHERE OrderStatus IN(''Active'', ''Pending'')
) AS t
PIVOT
(
SUM(DayCount)
For StatusType IN(' + @cols + ')' +
') p';
execute(@query);
这会给你:
更新
如果要在手动数据透视查询中添加 where 子句,可以这样做:
SELECT *
FROM
(
SELECT
OrderID,
OrderStatus + CountType AS StatusType,
DayCount
FROM CalendarTable
WHERE ...
UNION ALL
SELECT
OrderID,
CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END,
DayCount
FROM CalendarTable
WHERE ...
) AS t
PIVOT
(
MAX(DayCount)
For StatusType IN(OpenWorking,
OpenCalendar,
CloseWorking,
CloseCalendar,
PendingWorking,
PendingCalendar,
TotalWorking,
TotalCalendar)
) AS p;
场景
XYZ 公司销售产品。公司通过检查完成整个订单需要多长时间来衡量他们的绩效。每个订单都会经历多种状态(示例:打开、待处理、关闭)
他们计算每个状态的天数,以了解订单处于特定状态的天数。天数以两种不同的方式计算,工作日和日历日
请参考以下table:
问题
如何将这个table变成下图所示的枢轴table?还有如何添加到额外的列,这是每个工作日和工作日状态的总和。
期望的结果:
你可以这样做:
SELECT *
FROM
(
SELECT
OrderID,
OrderStatus + CountType AS StatusType,
DayCount
FROM CalendarTable
UNION ALL
SELECT
OrderID,
CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END,
DayCount
FROM CalendarTable
) AS t
PIVOT
(
MAX(DayCount)
For StatusType IN(OpenWorking,
OpenCalendar,
CloseWorking,
CloseCalendar,
PendingWorking,
PendingCalendar,
TotalWorking,
TotalCalendar)
) AS p;
这会给你:
如果不想手动记下所有状态,可以动态记下:
声明@cols 为 NVARCHAR(MAX);
DECLARE @query AS NVARCHAR(MAX);
SELECT @cols = STUFF((SELECT distinct ',' +
QUOTENAME(StatusType)
FROM
(
SELECT
OrderID,
OrderStatus + CountType AS StatusType,
DayCount
FROM CalendarTable
UNION ALL
SELECT
OrderID,
CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END,
DayCount
FROM CalendarTable
) AS t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT @query = 'SELECT *
FROM
(
SELECT
OrderID,
OrderStatus + CountType AS StatusType,
DayCount
FROM CalendarTable
UNION ALL
SELECT
OrderID,
CASE WHEN CountType = ''Working'' THEN ''TotalWorking'' ELSE ''TotalCalendar'' END,
DayCount
FROM CalendarTable
) AS t
PIVOT
(
MAX(DayCount)
For StatusType IN(' + @cols + ')' +
') p';
execute(@query);
更新:
对于列名,您可以创建一个新变量 @colnames
并用您想要的名称填充它。对于总计,您可以添加一个 WHERE
子句以仅获取活动状态和待定状态的总计。所以你的查询将是这样的:
DECLARE @cols AS NVARCHAR(MAX);
DECLARE @colnames AS NVARCHAR(MAX);
DECLARE @query AS NVARCHAR(MAX);
SELECT @cols = STUFF((SELECT distinct ',' +
QUOTENAME(StatusType)
FROM
(
SELECT
OrderID,
OrderStatus + CountType AS StatusType,
DayCount
FROM CalendarTable
UNION ALL
SELECT
OrderID,
CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END,
DayCount
FROM CalendarTable
WHERE OrderStatus IN('Active', 'Pending')
) AS t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT @colnames = STUFF((SELECT distinct ',' +
QUOTENAME(StatusType) + ' AS ' + QUOTENAME(StatusTypeName)
FROM
(
SELECT
OrderID,
OrderStatus + CountType AS StatusType,
DayCount,
OrderStatus + CASE WHEN CountType = 'Working' THEN 'WorkDays' ELSE 'CalDays' END AS StatusTypeName
FROM CalendarTable
UNION ALL
SELECT
OrderID,
CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END,
DayCount,
CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END
FROM CalendarTable
WHERE OrderStatus IN('Active', 'Pending')
) AS t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT @query = 'SELECT OrderID , ' + @colnames + '
FROM
(
SELECT
OrderID,
OrderStatus + CountType AS StatusType,
DayCount
FROM CalendarTable
UNION ALL
SELECT
OrderID,
CASE WHEN CountType = ''Working'' THEN ''TotalWorking'' ELSE ''TotalCalendar'' END,
DayCount
FROM CalendarTable
WHERE OrderStatus IN(''Active'', ''Pending'')
) AS t
PIVOT
(
SUM(DayCount)
For StatusType IN(' + @cols + ')' +
') p';
execute(@query);
这会给你:
更新
如果要在手动数据透视查询中添加 where 子句,可以这样做:
SELECT *
FROM
(
SELECT
OrderID,
OrderStatus + CountType AS StatusType,
DayCount
FROM CalendarTable
WHERE ...
UNION ALL
SELECT
OrderID,
CASE WHEN CountType = 'Working' THEN 'TotalWorking' ELSE 'TotalCalendar' END,
DayCount
FROM CalendarTable
WHERE ...
) AS t
PIVOT
(
MAX(DayCount)
For StatusType IN(OpenWorking,
OpenCalendar,
CloseWorking,
CloseCalendar,
PendingWorking,
PendingCalendar,
TotalWorking,
TotalCalendar)
) AS p;