如何在SQL Server中选择非规范化的成分数据?

时间:2017-11-05 12:28:06

标签: sql sql-server sql-server-2008 select denormalized

我正在制作产品库存并使用以下声明显示数据

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 

此查询返回此结果:

enter image description here

但我的预期结果是

enter image description here

在我的样本数据中,Vanilla Cake是主要产品,Butter Cream,Eggs,Flour是子产品,3,4,5,6,7,8列是子产品数据,第9,10,11,12列是主要产品。

我的问题是如何单独显示这些数据,我对此一无所知。

编辑赏金

正如您在第一张图片中看到的,有两种制造产品1)Manu 2)Vanila Cake

这里我们将获得vanila蛋糕示例:

Vanila蛋糕有3个副产品1)黄油奶油2)鸡蛋3)面粉

第3至第8列与子产品(所需数量/单位列平衡数量列)相关

第9至12栏与制造产品相关(估算数量列到可用数量列)

预期结果显示在图2中

6 个答案:

答案 0 :(得分:5)

您应该在应用程序层中进行此类操作。为什么? SQL表和结果集表示无序集 - 除非您专门指定排序。您尚未指定订购。

其次,SQL查询的所有列应具有相同的列数。您似乎想要为不同的行使用不同的数字。

一个部分解决方案是仅将名称放在“第一”行:

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];

答案 1 :(得分:3)

在这个例子中,我选择了不属于“Manu”的独特产品。 (制造商)。为了确保所需输出的第一行的排序,我为行theOrder赋予行一个整数= 1的子顺序。我查询了Manu'数据,假设您需要平均值,这是针对'香草蛋糕的制造商产品行。

我将所有这些与子产品详细信息联合起来,给它一个子序列int = 2,theOrder。在您想要的输出中被删空的列我留下了NULL。

这整个事情都是subqueried,我做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,我会为制造产品制作一个父列。

答案 2 :(得分:2)

我认为这可以作为空值替换为空字符串,并用于演示如何在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

答案 3 :(得分:2)

如果我正确理解您的要求,这是您的问题的解决方案。

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;

输出在这里

Result

答案 4 :(得分:2)

<强>演示

http://rextester.com/AWO40913

<强> 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';

备注

可以使用DISTINCT代替GROUP BY甚至简单的SELECT TOP 1GROUP BY被选中,因为它比DISTINCT更快,并且会突出显示非规范化数据的任何问题。

答案 5 :(得分:2)

您可以使用GROUPING SETSDemo

更简洁地完成此操作
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