如何 select 在 SQL 服务器中对成分数据进行非规范化?
How to select denormalized ingredient data in SQL Server?
我正在制作产品清单并使用以下语句显示数据
SELECT _PRODNAME AS [Manufacture Product],
_BASEPRODNAME AS [Sub Product],
_PRDDEFQTY AS [Required Qty / Unit],
_PURQTY AS [Purchase Qty],
_PURRETQTY AS [Return Qty],
_ISSUEQTY AS [Issue Qty],
_DAMAGEQTY AS [Damage Qty],
_BALQTY AS [Balance Qty],
_MINESTIMATE AS [Estimate Qty],
_SALEQTY AS [Sale Qty],
_MANUDAMAGEQTY AS Damage,
_AVAILQTY AS [Avail Qty]
FROM dbo.VIEW_MANUFACTURING
此查询返回此结果:
但我的预期结果是
在我的示例数据中,香草蛋糕是主产品,奶油、鸡蛋、面粉是子产品,第 3、4、5、6、7、8 列是子产品数据,第 9、10、11 列, 12个是主要产品。
我的问题是如何单独显示这些数据,我对此一无所知。
赏金编辑
正如您在第一张图片中看到的那样,有两个制造产品 1) Manu 2) Vanila Cake
这里我们会得到香草蛋糕示例:
香草蛋糕有 3 个子产品 1) 奶油 2) 鸡蛋 3) 面粉
第 3 至 8 列与子产品相关(Required Qty / Unit Column To Balance Qty Column)
第 9 至 12 列与制造产品相关(Estimate Qty Column To Avail Qty Column)
预期结果如图 2 所示
你真的应该在应用层做这种操作。为什么? SQL 表和结果集表示 无序 集——除非您特别指定排序。您尚未指定顺序。
其次,SQL 查询的所有列都应具有相同的列数。您似乎想要不同行的不同数字。
一个部分解决方案是只将名称放在 "first" 行:
select (case when row_number() over (partition by _prodname order by _baseprodname) = 1
then _prodname
end) as [Manufacture Product],
. . .
from dbo.VIEW_MANUFACTURING
order by _prodname, _baseprodname;
要将它们放在不同的行中,您可以这样做:
select v.[Manufacture Product], v.[Sub Product], . . .
from (select vm.*,
row_number() over (partition by _prodname order by _baseprodname) as seqnum
from dbo.VIEW_MANUFACTURING vm
) vm outer apply
(values (1, _ProdName, NULL, NULL, . . .),
(2, NULL, _BaseProdName, . . .)
) v(seqnum, [Manufacture Product], [Sub Product], . . .)
where vm.seqnum = 1 or v.seqnum = 2
order by v.[Manufacture Product], v.seqnum, v.[Sub Product];
我假设这可以作为用空字符串替换的空值,并用于演示您希望在 Image2 中看到的效果。
通过Case statement
订购可能会得到结果集,请检查并告诉我
WITH cte
AS (
SELECT _PRODNAME AS [Manufacture Product]
,NULL [Sub Product]
,NULL [Required Qty / Unit]
,NULL [Purchase Qty]
,NULL [Return Qty]
,NULL [Issue Qty]
,NULL [Damage Qty]
,SUM(_BALQTY) AS [Balance Qty]
,SUM(_MINESTIMATE) AS [Estimate Qty]
,SUM(_SALEQTY) AS [Sale Qty]
,SUM(_MANUDAMAGEQTY) AS Damage
,SUM(_AVAILQTY) AS [Avail Qty]
FROM dbo.VIEW_MANUFACTURING
GROUP BY _PRODNAME
UNION ALL
SELECT NULL AS [Manufacture Product]
,_BASEPRODNAME AS [Sub Product]
,NULL AS [Required Qty / Unit]
,NULL AS [Purchase Qty]
,NULL AS [Return Qty]
,NULL AS [Issue Qty]
,NULL AS [Damage Qty]
,NULL AS [Balance Qty]
,SUM(_MINESTIMATE) AS [Estimate Qty]
,SUM(_SALEQTY) AS [Sale Qty]
,SUM(_MANUDAMAGEQTY) AS Damage
,SUM(_AVAILQTY) AS [Avail Qty]
FROM dbo.VIEW_MANUFACTURING
GROUP BY _BASEPRODNAME
)
SELECT ISNULL([Manufacture Product], '') [Manufacture Product]
,ISNULL([Sub Product], '') [Sub Product]
,ISNULL([Required Qty / Unit], '') [Required Qty / Unit]
,ISNULL([Purchase Qty], '') [Purchase Qty]
,ISNULL([Return Qty], '') [Return Qty]
,ISNULL([Issue Qty], '') [Issue Qty]
,ISNULL([Damage Qty], '') [Damage Qty]
,ISNULL([Balance Qty], '') [Balance Qty]
,ISNULL([Estimate Qty], '') [Estimate Qty]
,ISNULL([Sale Qty], '') [Sale Qty]
,ISNULL(Damage, '') Damage
,ISNULL([Avail Qty], '') [Avail Qty]
FROM cte
ORDER BY
CASE WHEN [Sub Product] is null and [Manufacture Product] is not null then [Manufacture Product] END DESC
, CASE WHEN [Manufacture Product] IS NULL THEN [Sub Product] END
在这个例子中,我使用了非 'Manu'(制造商)的独特产品。为了确保所需输出的第一行的顺序,我为该行指定了一个子顺序整数 = 1,用于列 theOrder
。我子查询 'Manu' 数据,假设您想要平均值,这是针对 'Vanilla Cake'.
的制造商产品行
I UNION ALL this with the sub product details,给它一个子订单int = 2,theOrder
。在您想要的输出中空白的列我保留 NULL。
这整件事都是子查询的,我用 case 语句来清空列,类似于您想要的输出。这是使用制造商产品名称和 theOrder
列的组合进行排序,因此应首先列出主要产品,然后是子产品。
DECLARE @temp TABLE ([Manufacture Product] varchar(100), [Sub Product] varchar(100), [Required Qty / Unit] decimal(16,2), [Purchase Qty] decimal(16,2)
, [Return Qty] decimal(16,2)
,[Issue Qty] decimal(16,2), [Damage Qty] decimal(16,2), [Balance Qty] decimal(16,2), [Estimate Qty] decimal(16,2)
,[Sale Qty] decimal(16,2), [Damage] decimal(16,2), [Avail Qty] decimal(16,2))
INSERT INTO @temp
VALUES ('manu', '2 GOOD' , 34.00, 502.00, 0.00, 0.00, 0.00, 502.00, 14.71, 0.00, 0.00, 14.71)
,('manu', 'CHOCO AL...', 34.00, 500.00, 0.00, 0.00, 0.00, 500.00, 14.71, 0.00, 0.00, 14.71)
,('Vanila Cake', 'Butter Cream', 10.00, 600.00, 0.00, 72.00, 0.00, 528.00, 52.80, 0.00, 0.00, 52.80)
,('Vanila Cake', 'Eggs' , 2.00,1000.00, 0.00, 37.00, 0.00, 963.00, 52.80, 0.00, 0.00, 52.80)
,('Vanila Cake', 'Flour' , 5.00, 0.00, 0.00, 0.00, 0.00, 500.00, 52.80, 0.00, 0.00, 52.80)
SELECT CASE WHEN theOrder = 1 THEN [Manufacture Product] ELSE '' END [Manufacture Product]
,CASE WHEN theOrder = 2 THEN [Sub Product] ELSE '' END [Sub Product]
,[Required Qty / Unit]
,[Purchase Qty]
,[Return Qty]
,[Issue Qty]
,[Damage Qty]
,[Balance Qty]
,[Estimate Qty]
,[Sale Qty]
,Damage
,[Avail Qty]
FROM (
SELECT DISTINCT
1 [theOrder]
,T.[Manufacture Product]
,'' [Sub Product]
,NULL [Required Qty / Unit]
,NULL [Purchase Qty]
,NULL [Return Qty]
,NULL [Issue Qty]
,NULL [Damage Qty]
,NULL [Balance Qty]
,(SELECT AVG(T2.[Estimate Qty]) FROM @temp T2 WHERE T2.[Manufacture Product] = 'Manu') [Estimate Qty]
,(SELECT AVG([Sale Qty]) FROM @temp T2 WHERE T2.[Manufacture Product] = 'Manu') [Sale Qty]
,(SELECT AVG([Damage]) FROM @temp T2 WHERE T2.[Manufacture Product] = 'Manu') [Damage]
,(SELECT AVG([Avail Qty]) FROM @temp T2 WHERE T2.[Manufacture Product] = 'Manu') [Avail Qty]
FROM @temp T
WHERE T.[Manufacture Product] <> 'Manu'
UNION ALL
SELECT 2 [theOrder]
,T.[Manufacture Product]
,T.[Sub Product]
,T.[Required Qty / Unit]
,T.[Purchase Qty]
,T.[Return Qty]
,T.[Issue Qty]
,T.[Damage Qty]
,T.[Balance Qty]
,NULL [Estimate Qty]
,NULL [Sale Qty]
,NULL [Damage]
,NULL [Avail Qty]
FROM @temp T
WHERE T.[Manufacture Product] <> 'Manu'
) AS dT
ORDER BY dT.[Manufacture Product], dT.theOrder, dT.[Sub Product]
这会产生类似于您所要求的输出。 NULLS 可以由报告工具处理。
仅供参考,我在 SSRS 等报告工具中更容易做到这一点。如果是 SSRS,我会为 Manufacture Product 创建一个父列。
如果我正确理解了您的要求,这里是您问题的解决方案。
SELECT CASE WHEN record_type = 'Product' THEN _PRODNAME ELSE NULL END AS [Manufacture Product]
,CASE WHEN record_type = 'Sub Product' THEN _BASEPRODNAME ELSE NULL END AS [Sub Product]
,CASE WHEN record_type = 'Sub Product' THEN _PRDDEFQTY ELSE NULL END AS [Required Qty / Unit]
,CASE WHEN record_type = 'Sub Product' THEN _PURQTY ELSE NULL END AS [Purchase Qty]
,CASE WHEN record_type = 'Sub Product' THEN _PURRETQTY ELSE NULL END AS [Return Qty]
,CASE WHEN record_type = 'Sub Product' THEN _ISSUEQTY ELSE NULL END AS [Issue Qty]
,CASE WHEN record_type = 'Sub Product' THEN _DAMAGEQTY ELSE NULL END AS [Damage Qty]
,CASE WHEN record_type = 'Sub Product' THEN _BALQTY ELSE NULL END AS [Balance Qty]
,CASE WHEN record_type = 'Product' THEN _MINESTIMATE ELSE NULL END AS [Estimate Qty]
,CASE WHEN record_type = 'Product' THEN _SALEQTY ELSE NULL END AS [Sale Qty]
,CASE WHEN record_type = 'Product' THEN _MANUDAMAGEQTY ELSE NULL END AS [Damage]
,CASE WHEN record_type = 'Product' THEN _AVAILQTY ELSE NULL END AS [Avail Qty]
FROM (
SELECT _PRODNAME
,NULL AS _BASEPRODNAME
,NULL AS _PRDDEFQTY
,NULL AS _PURQTY
,NULL AS _PURRETQTY
,NULL AS _ISSUEQTY
,NULL AS _DAMAGEQTY
,NULL AS _BALQTY
,_MINESTIMATE
,_SALEQTY
,_MANUDAMAGEQTY
,_AVAILQTY
,'Product' AS record_type
FROM (
SELECT _PRODNAME
,_BASEPRODNAME
,_PRDDEFQTY
,_PURQTY
,_PURRETQTY
,_ISSUEQTY
,_DAMAGEQTY
,_BALQTY
,_MINESTIMATE
,_SALEQTY
,_MANUDAMAGEQTY
,_AVAILQTY
,'Product' AS record_type
,ROW_NUMBER() OVER (
PARTITION BY _PRODNAME ORDER BY _PRODNAME
) r_num
FROM dbo.VIEW_MANUFACTURING
) v
WHERE r_num = 1
UNION
SELECT _PRODNAME
,_BASEPRODNAME
,_PRDDEFQTY
,_PURQTY
,_PURRETQTY
,_ISSUEQTY
,_DAMAGEQTY
,_BALQTY
,_MINESTIMATE
,_SALEQTY
,_MANUDAMAGEQTY
,_AVAILQTY
,'Sub Product' AS record_type
FROM dbo.VIEW_MANUFACTURING
) v1
ORDER BY _PRODNAME ,
_BASEPRODNAME;
输出在这里
演示
SQL
SELECT _PRODNAME AS [Manufacture Product],
NULL AS [Sub Product],
NULL AS [Required Qty / Unit],
NULL AS [Purchase Qty],
NULL AS [Return Qty],
NULL AS [Issue Qty],
NULL AS [Damage Qty],
NULL AS [Balance Qty],
_MINESTIMATE AS [Estimate Qty],
_SALEQTY AS [Sale Qty],
_MANUDAMAGEQTY AS Damage,
_AVAILQTY AS [Avail Qty]
FROM dbo.VIEW_MANUFACTURING
WHERE _PRODNAME = 'Vanila Cake'
GROUP BY _PRODNAME, _MINESTIMATE, _SALEQTY, _MANUDAMAGEQTY, _AVAILQTY
UNION ALL
SELECT NULL AS [Manufacture Product],
_BASEPRODNAME AS [Sub Product],
_PRDDEFQTY AS [Required Qty / Unit],
_PURQTY AS [Purchase Qty],
_PURRETQTY AS [Return Qty],
_ISSUEQTY AS [Issue Qty],
_DAMAGEQTY AS [Damage Qty],
_BALQTY AS [Balance Qty],
NULL AS [Estimate Qty],
NULL AS [Sale Qty],
NULL AS Damage,
NULL AS [Avail Qty]
FROM dbo.VIEW_MANUFACTURING
WHERE _PRODNAME = 'Vanila Cake';
备注
A DISTINCT
可以代替 GROUP BY
甚至简单的 SELECT TOP 1
。 GROUP BY
之所以被选中,是因为它比 DISTINCT
更快,并且会突出显示非规范化数据的任何问题。
您可以更简洁地使用 GROUPING SETS
(Demo)
SELECT [Manufacture Product] = _PRODNAME,
[Sub Product] = _BASEPRODNAME,
[Required Qty / Unit] = CASE WHEN GROUPING(_BASEPRODNAME) = 0 THEN SUM(_PRDDEFQTY) END,
[Purchase Qty] = CASE WHEN GROUPING(_BASEPRODNAME) = 0 THEN SUM(_PURQTY) END,
[Return Qty] = CASE WHEN GROUPING(_BASEPRODNAME) = 0 THEN SUM(_PURRETQTY) END,
[Issue Qty] = CASE WHEN GROUPING(_BASEPRODNAME) = 0 THEN SUM(_ISSUEQTY) END,
[Damage Qty] = CASE WHEN GROUPING(_BASEPRODNAME) = 0 THEN SUM(_DAMAGEQTY) END,
[Balance Qty] = CASE WHEN GROUPING(_BASEPRODNAME) = 0 THEN SUM(_BALQTY) END,
[Estimate Qty] = CASE WHEN GROUPING(_BASEPRODNAME) = 1 THEN SUM(_MINESTIMATE) END,
[Sale Qty] = CASE WHEN GROUPING(_BASEPRODNAME) = 1 THEN SUM(_SALEQTY) END,
Damage = CASE WHEN GROUPING(_BASEPRODNAME) = 1 THEN SUM(_MANUDAMAGEQTY) END,
[Avail Qty] = CASE WHEN GROUPING(_BASEPRODNAME) = 1 THEN SUM(_AVAILQTY) END
FROM dbo.VIEW_MANUFACTURING
GROUP BY GROUPING SETS ( ( _PRODNAME ), ( _PRODNAME, _BASEPRODNAME ) )
ORDER BY [Manufacture Product] ASC,
GROUPING(_BASEPRODNAME) DESC,
[Sub Product] ASC
如果需要,只需添加 WHERE _PRODNAME = 'Vanila Cake'
。
或者您可以使用
摆脱重复的 CASE
表达式
WITH T
AS (SELECT [Manufacture Product] = _PRODNAME,
[Sub Product] = _BASEPRODNAME,
[Required Qty / Unit] = SUM(_PRDDEFQTY),
[Purchase Qty] = SUM(_PURQTY),
[Return Qty] = SUM(_PURRETQTY),
[Issue Qty] = SUM(_ISSUEQTY),
[Damage Qty] = SUM(_DAMAGEQTY),
[Balance Qty] = SUM(_BALQTY),
[Estimate Qty] = SUM(_MINESTIMATE),
[Sale Qty] = SUM(_SALEQTY),
Damage = SUM(_MANUDAMAGEQTY),
[Avail Qty] = SUM(_AVAILQTY),
GrpFlag = GROUPING(_BASEPRODNAME)
FROM VIEW_MANUFACTURING
GROUP BY GROUPING SETS ( ( _PRODNAME ), ( _PRODNAME, _BASEPRODNAME ) ))
SELECT T.[Manufacture Product],
T.[Sub Product],
OA1.*,
OA2.*
FROM T
OUTER APPLY (SELECT [Required Qty / Unit],[Purchase Qty],[Return Qty],[Issue Qty],[Damage Qty],[Balance Qty]
WHERE GrpFlag = 0) OA1
OUTER APPLY (SELECT [Estimate Qty],[Sale Qty], Damage, [Avail Qty]
WHERE GrpFlag = 1) OA2
ORDER BY [Manufacture Product] ASC,
GrpFlag DESC,
[Sub Product] ASC
我正在制作产品清单并使用以下语句显示数据
SELECT _PRODNAME AS [Manufacture Product],
_BASEPRODNAME AS [Sub Product],
_PRDDEFQTY AS [Required Qty / Unit],
_PURQTY AS [Purchase Qty],
_PURRETQTY AS [Return Qty],
_ISSUEQTY AS [Issue Qty],
_DAMAGEQTY AS [Damage Qty],
_BALQTY AS [Balance Qty],
_MINESTIMATE AS [Estimate Qty],
_SALEQTY AS [Sale Qty],
_MANUDAMAGEQTY AS Damage,
_AVAILQTY AS [Avail Qty]
FROM dbo.VIEW_MANUFACTURING
此查询返回此结果:
但我的预期结果是
在我的示例数据中,香草蛋糕是主产品,奶油、鸡蛋、面粉是子产品,第 3、4、5、6、7、8 列是子产品数据,第 9、10、11 列, 12个是主要产品。
我的问题是如何单独显示这些数据,我对此一无所知。
赏金编辑
正如您在第一张图片中看到的那样,有两个制造产品 1) Manu 2) Vanila Cake
这里我们会得到香草蛋糕示例:
香草蛋糕有 3 个子产品 1) 奶油 2) 鸡蛋 3) 面粉
第 3 至 8 列与子产品相关(Required Qty / Unit Column To Balance Qty Column)
第 9 至 12 列与制造产品相关(Estimate Qty Column To Avail Qty Column)
预期结果如图 2 所示
你真的应该在应用层做这种操作。为什么? SQL 表和结果集表示 无序 集——除非您特别指定排序。您尚未指定顺序。
其次,SQL 查询的所有列都应具有相同的列数。您似乎想要不同行的不同数字。
一个部分解决方案是只将名称放在 "first" 行:
select (case when row_number() over (partition by _prodname order by _baseprodname) = 1
then _prodname
end) as [Manufacture Product],
. . .
from dbo.VIEW_MANUFACTURING
order by _prodname, _baseprodname;
要将它们放在不同的行中,您可以这样做:
select v.[Manufacture Product], v.[Sub Product], . . .
from (select vm.*,
row_number() over (partition by _prodname order by _baseprodname) as seqnum
from dbo.VIEW_MANUFACTURING vm
) vm outer apply
(values (1, _ProdName, NULL, NULL, . . .),
(2, NULL, _BaseProdName, . . .)
) v(seqnum, [Manufacture Product], [Sub Product], . . .)
where vm.seqnum = 1 or v.seqnum = 2
order by v.[Manufacture Product], v.seqnum, v.[Sub Product];
我假设这可以作为用空字符串替换的空值,并用于演示您希望在 Image2 中看到的效果。
通过Case statement
订购可能会得到结果集,请检查并告诉我
WITH cte
AS (
SELECT _PRODNAME AS [Manufacture Product]
,NULL [Sub Product]
,NULL [Required Qty / Unit]
,NULL [Purchase Qty]
,NULL [Return Qty]
,NULL [Issue Qty]
,NULL [Damage Qty]
,SUM(_BALQTY) AS [Balance Qty]
,SUM(_MINESTIMATE) AS [Estimate Qty]
,SUM(_SALEQTY) AS [Sale Qty]
,SUM(_MANUDAMAGEQTY) AS Damage
,SUM(_AVAILQTY) AS [Avail Qty]
FROM dbo.VIEW_MANUFACTURING
GROUP BY _PRODNAME
UNION ALL
SELECT NULL AS [Manufacture Product]
,_BASEPRODNAME AS [Sub Product]
,NULL AS [Required Qty / Unit]
,NULL AS [Purchase Qty]
,NULL AS [Return Qty]
,NULL AS [Issue Qty]
,NULL AS [Damage Qty]
,NULL AS [Balance Qty]
,SUM(_MINESTIMATE) AS [Estimate Qty]
,SUM(_SALEQTY) AS [Sale Qty]
,SUM(_MANUDAMAGEQTY) AS Damage
,SUM(_AVAILQTY) AS [Avail Qty]
FROM dbo.VIEW_MANUFACTURING
GROUP BY _BASEPRODNAME
)
SELECT ISNULL([Manufacture Product], '') [Manufacture Product]
,ISNULL([Sub Product], '') [Sub Product]
,ISNULL([Required Qty / Unit], '') [Required Qty / Unit]
,ISNULL([Purchase Qty], '') [Purchase Qty]
,ISNULL([Return Qty], '') [Return Qty]
,ISNULL([Issue Qty], '') [Issue Qty]
,ISNULL([Damage Qty], '') [Damage Qty]
,ISNULL([Balance Qty], '') [Balance Qty]
,ISNULL([Estimate Qty], '') [Estimate Qty]
,ISNULL([Sale Qty], '') [Sale Qty]
,ISNULL(Damage, '') Damage
,ISNULL([Avail Qty], '') [Avail Qty]
FROM cte
ORDER BY
CASE WHEN [Sub Product] is null and [Manufacture Product] is not null then [Manufacture Product] END DESC
, CASE WHEN [Manufacture Product] IS NULL THEN [Sub Product] END
在这个例子中,我使用了非 'Manu'(制造商)的独特产品。为了确保所需输出的第一行的顺序,我为该行指定了一个子顺序整数 = 1,用于列 theOrder
。我子查询 'Manu' 数据,假设您想要平均值,这是针对 'Vanilla Cake'.
I UNION ALL this with the sub product details,给它一个子订单int = 2,theOrder
。在您想要的输出中空白的列我保留 NULL。
这整件事都是子查询的,我用 case 语句来清空列,类似于您想要的输出。这是使用制造商产品名称和 theOrder
列的组合进行排序,因此应首先列出主要产品,然后是子产品。
DECLARE @temp TABLE ([Manufacture Product] varchar(100), [Sub Product] varchar(100), [Required Qty / Unit] decimal(16,2), [Purchase Qty] decimal(16,2)
, [Return Qty] decimal(16,2)
,[Issue Qty] decimal(16,2), [Damage Qty] decimal(16,2), [Balance Qty] decimal(16,2), [Estimate Qty] decimal(16,2)
,[Sale Qty] decimal(16,2), [Damage] decimal(16,2), [Avail Qty] decimal(16,2))
INSERT INTO @temp
VALUES ('manu', '2 GOOD' , 34.00, 502.00, 0.00, 0.00, 0.00, 502.00, 14.71, 0.00, 0.00, 14.71)
,('manu', 'CHOCO AL...', 34.00, 500.00, 0.00, 0.00, 0.00, 500.00, 14.71, 0.00, 0.00, 14.71)
,('Vanila Cake', 'Butter Cream', 10.00, 600.00, 0.00, 72.00, 0.00, 528.00, 52.80, 0.00, 0.00, 52.80)
,('Vanila Cake', 'Eggs' , 2.00,1000.00, 0.00, 37.00, 0.00, 963.00, 52.80, 0.00, 0.00, 52.80)
,('Vanila Cake', 'Flour' , 5.00, 0.00, 0.00, 0.00, 0.00, 500.00, 52.80, 0.00, 0.00, 52.80)
SELECT CASE WHEN theOrder = 1 THEN [Manufacture Product] ELSE '' END [Manufacture Product]
,CASE WHEN theOrder = 2 THEN [Sub Product] ELSE '' END [Sub Product]
,[Required Qty / Unit]
,[Purchase Qty]
,[Return Qty]
,[Issue Qty]
,[Damage Qty]
,[Balance Qty]
,[Estimate Qty]
,[Sale Qty]
,Damage
,[Avail Qty]
FROM (
SELECT DISTINCT
1 [theOrder]
,T.[Manufacture Product]
,'' [Sub Product]
,NULL [Required Qty / Unit]
,NULL [Purchase Qty]
,NULL [Return Qty]
,NULL [Issue Qty]
,NULL [Damage Qty]
,NULL [Balance Qty]
,(SELECT AVG(T2.[Estimate Qty]) FROM @temp T2 WHERE T2.[Manufacture Product] = 'Manu') [Estimate Qty]
,(SELECT AVG([Sale Qty]) FROM @temp T2 WHERE T2.[Manufacture Product] = 'Manu') [Sale Qty]
,(SELECT AVG([Damage]) FROM @temp T2 WHERE T2.[Manufacture Product] = 'Manu') [Damage]
,(SELECT AVG([Avail Qty]) FROM @temp T2 WHERE T2.[Manufacture Product] = 'Manu') [Avail Qty]
FROM @temp T
WHERE T.[Manufacture Product] <> 'Manu'
UNION ALL
SELECT 2 [theOrder]
,T.[Manufacture Product]
,T.[Sub Product]
,T.[Required Qty / Unit]
,T.[Purchase Qty]
,T.[Return Qty]
,T.[Issue Qty]
,T.[Damage Qty]
,T.[Balance Qty]
,NULL [Estimate Qty]
,NULL [Sale Qty]
,NULL [Damage]
,NULL [Avail Qty]
FROM @temp T
WHERE T.[Manufacture Product] <> 'Manu'
) AS dT
ORDER BY dT.[Manufacture Product], dT.theOrder, dT.[Sub Product]
这会产生类似于您所要求的输出。 NULLS 可以由报告工具处理。
仅供参考,我在 SSRS 等报告工具中更容易做到这一点。如果是 SSRS,我会为 Manufacture Product 创建一个父列。
如果我正确理解了您的要求,这里是您问题的解决方案。
SELECT CASE WHEN record_type = 'Product' THEN _PRODNAME ELSE NULL END AS [Manufacture Product]
,CASE WHEN record_type = 'Sub Product' THEN _BASEPRODNAME ELSE NULL END AS [Sub Product]
,CASE WHEN record_type = 'Sub Product' THEN _PRDDEFQTY ELSE NULL END AS [Required Qty / Unit]
,CASE WHEN record_type = 'Sub Product' THEN _PURQTY ELSE NULL END AS [Purchase Qty]
,CASE WHEN record_type = 'Sub Product' THEN _PURRETQTY ELSE NULL END AS [Return Qty]
,CASE WHEN record_type = 'Sub Product' THEN _ISSUEQTY ELSE NULL END AS [Issue Qty]
,CASE WHEN record_type = 'Sub Product' THEN _DAMAGEQTY ELSE NULL END AS [Damage Qty]
,CASE WHEN record_type = 'Sub Product' THEN _BALQTY ELSE NULL END AS [Balance Qty]
,CASE WHEN record_type = 'Product' THEN _MINESTIMATE ELSE NULL END AS [Estimate Qty]
,CASE WHEN record_type = 'Product' THEN _SALEQTY ELSE NULL END AS [Sale Qty]
,CASE WHEN record_type = 'Product' THEN _MANUDAMAGEQTY ELSE NULL END AS [Damage]
,CASE WHEN record_type = 'Product' THEN _AVAILQTY ELSE NULL END AS [Avail Qty]
FROM (
SELECT _PRODNAME
,NULL AS _BASEPRODNAME
,NULL AS _PRDDEFQTY
,NULL AS _PURQTY
,NULL AS _PURRETQTY
,NULL AS _ISSUEQTY
,NULL AS _DAMAGEQTY
,NULL AS _BALQTY
,_MINESTIMATE
,_SALEQTY
,_MANUDAMAGEQTY
,_AVAILQTY
,'Product' AS record_type
FROM (
SELECT _PRODNAME
,_BASEPRODNAME
,_PRDDEFQTY
,_PURQTY
,_PURRETQTY
,_ISSUEQTY
,_DAMAGEQTY
,_BALQTY
,_MINESTIMATE
,_SALEQTY
,_MANUDAMAGEQTY
,_AVAILQTY
,'Product' AS record_type
,ROW_NUMBER() OVER (
PARTITION BY _PRODNAME ORDER BY _PRODNAME
) r_num
FROM dbo.VIEW_MANUFACTURING
) v
WHERE r_num = 1
UNION
SELECT _PRODNAME
,_BASEPRODNAME
,_PRDDEFQTY
,_PURQTY
,_PURRETQTY
,_ISSUEQTY
,_DAMAGEQTY
,_BALQTY
,_MINESTIMATE
,_SALEQTY
,_MANUDAMAGEQTY
,_AVAILQTY
,'Sub Product' AS record_type
FROM dbo.VIEW_MANUFACTURING
) v1
ORDER BY _PRODNAME ,
_BASEPRODNAME;
输出在这里
演示
SQL
SELECT _PRODNAME AS [Manufacture Product],
NULL AS [Sub Product],
NULL AS [Required Qty / Unit],
NULL AS [Purchase Qty],
NULL AS [Return Qty],
NULL AS [Issue Qty],
NULL AS [Damage Qty],
NULL AS [Balance Qty],
_MINESTIMATE AS [Estimate Qty],
_SALEQTY AS [Sale Qty],
_MANUDAMAGEQTY AS Damage,
_AVAILQTY AS [Avail Qty]
FROM dbo.VIEW_MANUFACTURING
WHERE _PRODNAME = 'Vanila Cake'
GROUP BY _PRODNAME, _MINESTIMATE, _SALEQTY, _MANUDAMAGEQTY, _AVAILQTY
UNION ALL
SELECT NULL AS [Manufacture Product],
_BASEPRODNAME AS [Sub Product],
_PRDDEFQTY AS [Required Qty / Unit],
_PURQTY AS [Purchase Qty],
_PURRETQTY AS [Return Qty],
_ISSUEQTY AS [Issue Qty],
_DAMAGEQTY AS [Damage Qty],
_BALQTY AS [Balance Qty],
NULL AS [Estimate Qty],
NULL AS [Sale Qty],
NULL AS Damage,
NULL AS [Avail Qty]
FROM dbo.VIEW_MANUFACTURING
WHERE _PRODNAME = 'Vanila Cake';
备注
A DISTINCT
可以代替 GROUP BY
甚至简单的 SELECT TOP 1
。 GROUP BY
之所以被选中,是因为它比 DISTINCT
更快,并且会突出显示非规范化数据的任何问题。
您可以更简洁地使用 GROUPING SETS
(Demo)
SELECT [Manufacture Product] = _PRODNAME,
[Sub Product] = _BASEPRODNAME,
[Required Qty / Unit] = CASE WHEN GROUPING(_BASEPRODNAME) = 0 THEN SUM(_PRDDEFQTY) END,
[Purchase Qty] = CASE WHEN GROUPING(_BASEPRODNAME) = 0 THEN SUM(_PURQTY) END,
[Return Qty] = CASE WHEN GROUPING(_BASEPRODNAME) = 0 THEN SUM(_PURRETQTY) END,
[Issue Qty] = CASE WHEN GROUPING(_BASEPRODNAME) = 0 THEN SUM(_ISSUEQTY) END,
[Damage Qty] = CASE WHEN GROUPING(_BASEPRODNAME) = 0 THEN SUM(_DAMAGEQTY) END,
[Balance Qty] = CASE WHEN GROUPING(_BASEPRODNAME) = 0 THEN SUM(_BALQTY) END,
[Estimate Qty] = CASE WHEN GROUPING(_BASEPRODNAME) = 1 THEN SUM(_MINESTIMATE) END,
[Sale Qty] = CASE WHEN GROUPING(_BASEPRODNAME) = 1 THEN SUM(_SALEQTY) END,
Damage = CASE WHEN GROUPING(_BASEPRODNAME) = 1 THEN SUM(_MANUDAMAGEQTY) END,
[Avail Qty] = CASE WHEN GROUPING(_BASEPRODNAME) = 1 THEN SUM(_AVAILQTY) END
FROM dbo.VIEW_MANUFACTURING
GROUP BY GROUPING SETS ( ( _PRODNAME ), ( _PRODNAME, _BASEPRODNAME ) )
ORDER BY [Manufacture Product] ASC,
GROUPING(_BASEPRODNAME) DESC,
[Sub Product] ASC
如果需要,只需添加 WHERE _PRODNAME = 'Vanila Cake'
。
或者您可以使用
摆脱重复的CASE
表达式
WITH T
AS (SELECT [Manufacture Product] = _PRODNAME,
[Sub Product] = _BASEPRODNAME,
[Required Qty / Unit] = SUM(_PRDDEFQTY),
[Purchase Qty] = SUM(_PURQTY),
[Return Qty] = SUM(_PURRETQTY),
[Issue Qty] = SUM(_ISSUEQTY),
[Damage Qty] = SUM(_DAMAGEQTY),
[Balance Qty] = SUM(_BALQTY),
[Estimate Qty] = SUM(_MINESTIMATE),
[Sale Qty] = SUM(_SALEQTY),
Damage = SUM(_MANUDAMAGEQTY),
[Avail Qty] = SUM(_AVAILQTY),
GrpFlag = GROUPING(_BASEPRODNAME)
FROM VIEW_MANUFACTURING
GROUP BY GROUPING SETS ( ( _PRODNAME ), ( _PRODNAME, _BASEPRODNAME ) ))
SELECT T.[Manufacture Product],
T.[Sub Product],
OA1.*,
OA2.*
FROM T
OUTER APPLY (SELECT [Required Qty / Unit],[Purchase Qty],[Return Qty],[Issue Qty],[Damage Qty],[Balance Qty]
WHERE GrpFlag = 0) OA1
OUTER APPLY (SELECT [Estimate Qty],[Sale Qty], Damage, [Avail Qty]
WHERE GrpFlag = 1) OA2
ORDER BY [Manufacture Product] ASC,
GrpFlag DESC,
[Sub Product] ASC