Sql server SQL如何使用小计创建输出

Sql server SQL如何使用小计创建输出,sql-server,pivot-table,subtotal,Sql Server,Pivot Table,Subtotal,我是T-SQL新手,需要帮助将excel报表转换为SQL运行。我有一个SQL表,记录每个仓库的所有每日库存事务(进出)。我需要创建一个报告,列出每个位置每个产品的当前库存水平和每个位置的数量,如下所示。换句话说,每个地方的当前库存水平 我还需要关于如何将首选输出报告(以下)作为视图插入SQL Server的帮助,以便我可以每月反复运行此报告 提前谢谢 库存日志表: PubID QTY LocationID Transaction 1 10 1 Add 1

我是T-SQL新手,需要帮助将excel报表转换为SQL运行。我有一个SQL表,记录每个仓库的所有每日库存事务(进出)。我需要创建一个报告,列出每个位置每个产品的当前库存水平和每个位置的数量,如下所示。换句话说,每个地方的当前库存水平

我还需要关于如何将首选输出报告(以下)作为视图插入SQL Server的帮助,以便我可以每月反复运行此报告

提前谢谢

库存日志表:

PubID   QTY LocationID  Transaction
1       10  1           Add
1       20  2           Add
1       30  3           Add
1       5   1           Sold
1       10  2           Sold
1       5   3           Sold
2       10  1           Add
2       10  2           Add
2       5   2           Sold
2       8   2           Sold
1       20  1           Add
1       20  2           Add
2       2   2           Sold
首选输出表:

PubID   Local_1 Local_2 Local_3 Total
1       25      30      25      80
2       5       0       0       5
Total   30      30      25      85
我在这里看到了很多类似的例子,但大多数只是增加价值,而我需要从增加的库存中减去已售出的库存,以得到每列中的总数

右侧和底部的行总计和列总计是加号,但如果没有加号更容易,则不需要加号


谢谢

下面的查询满足您的需要。我可能会有一个额外的组,可以合并成1个,但你知道的

DECLARE  @InventoryLog TABLE
(
    PubId INT,
    Qty INT,
    LocationId INT,
    [Transaction] Varchar(4)

)

DECLARE  @LocationTable TABLE
(
    Id INT,
    Name VarChar(10)

)

INSERT INTO @LocationTable
VALUES
(1, 'LOC_1'),
(2, 'LOC_2'),
(3, 'LOC_3')

INSERT INTO @InventoryLog
VALUES 
(1 ,      10,  1 ,          'Add'),
(1 ,      20,  2 ,          'Add'),
(1 ,      30,  3 ,          'Add'),
(1 ,      5 ,  1 ,          'Sold'),
(1 ,      10,  2 ,          'Sold'),
(1 ,      5 ,  3 ,          'Sold'),
(2 ,      10,  1 ,          'Add'),
(2 ,      10,  2 ,          'Add'),
(2 ,      5 ,  2 ,          'Sold'),
(2 ,      8 ,  2 ,          'Sold'),
(1 ,      20,  1 ,          'Add'),
(1 ,      20,  2 ,          'Add'),
(2 ,      2 ,  2 ,          'Sold')

SELECT PubId, 
       lT.Name LocationName, 
       CASE
            WHEN [Transaction] ='Add' Then Qty
            WHEN [Transaction] ='Sold' Then -Qty
       END as Quantity
INTO   #TempInventoryTable
FROM @InventoryLog iL
INNER JOIN @LocationTable  lT on iL.LocationId = lT.Id

SELECT * INTO #AlmostThere
FROM
(
SELECT PubId, 
       ISNULL(LOC_1,0) LOC_1,
       ISNULL(LOC_2,0) LOC_2, 
       ISNULL(LOC_3,0) LOC_3,
       SUM(ISNULL(LOC_1,0) + ISNULL(LOC_2,0) + ISNULL(LOC_3,0)) AS TOTAL
FROM #TempInventoryTable s
PIVOT 
(
    SUM(Quantity)
    FOR LocationName in (LOC_1,LOC_2,LOC_3)
) as b
GROUP BY PubId, LOC_1, LOC_2, LOC_3
) b

SELECT CAST(PubId as VARCHAR(10))PubId,
       LOC_1,
       LOC_2,
       LOC_3,
       TOTAL
FROM #AlmostThere
UNION
SELECT ISNULL(CAST(PubId AS VARCHAR(10)),'TOTAL')  PubId, 
       [LOC_1]= SUM(LOC_1),
       [LOC_2]= SUM(LOC_2),
       [LOC_3]= SUM(LOC_3),
       [TOTAL]= SUM(TOTAL)
FROM #AlmostThere
GROUP BY ROLLUP(PubId)




DROP TABLE #TempInventoryTable
DROP TABLE #AlmostThere



 PubId  LOC_1   LOC_2   LOC_3   TOTAL 

   1     25  30  25  80 

   2     10  -5   0   5

 TOTAL   35  25  25  85

如果这是关于无枢轴的聚合,则可以使用大小写表达式,如下所示:

SELECT
  ...
  Local_1 = SUM(CASE [Transaction] WHEN 'Add' THEN QTY ELSE -QTY END),
  ...
FROM ...
GROUP BY ...
PubID  QTY  LocationID
-----  ---  ----------
1      10   1
1      20   2
1      30   3
1      -5   1
1      -10  2
1      -5   3
2      10   1
2      10   2
2      -5   2
2      -8   2
1      20   1
1      20   2
2      -2   2
WITH prepared AS (
  SELECT
    PubID,
    QTY   = CASE [Transaction] WHEN 'Add' THEN QTY ELSE -QTY END,
    LocationID,
    Total = SUM(CASE [Transaction] WHEN 'Add' THEN QTY ELSE -QTY END)
            OVER (PARTITION BY PubID)
  FROM dbo.InventoryLog
)
…
PubID  LocationID  QTY
-----  ----------  ---
1      Local_1     35
2      Local_1     10
Total  Local_1     45
1      Local_2     50
2      Local_2     25
Total  Local_2     75
1      Local_3     35
Total  Local_3     35
Total  Total       155
1      Total       120
2      Total       35
但是,在PIVOT子句中,聚合函数的参数必须只是列引用,而不是表达式。您可以通过转换原始数据集来解决这一问题,使
数量
为正或负,具体取决于
交易

SELECT
  PubID,
  QTY = CASE [Transaction] WHEN 'Add' THEN QTY ELSE -QTY END,
  LocationID
FROM dbo.InventoryLog
上述查询将为您提供如下结果集:

SELECT
  ...
  Local_1 = SUM(CASE [Transaction] WHEN 'Add' THEN QTY ELSE -QTY END),
  ...
FROM ...
GROUP BY ...
PubID  QTY  LocationID
-----  ---  ----------
1      10   1
1      20   2
1      30   3
1      -5   1
1      -10  2
1      -5   3
2      10   1
2      10   2
2      -5   2
2      -8   2
1      20   1
1      20   2
2      -2   2
WITH prepared AS (
  SELECT
    PubID,
    QTY   = CASE [Transaction] WHEN 'Add' THEN QTY ELSE -QTY END,
    LocationID,
    Total = SUM(CASE [Transaction] WHEN 'Add' THEN QTY ELSE -QTY END)
            OVER (PARTITION BY PubID)
  FROM dbo.InventoryLog
)
…
PubID  LocationID  QTY
-----  ----------  ---
1      Local_1     35
2      Local_1     10
Total  Local_1     45
1      Local_2     50
2      Local_2     25
Total  Local_2     75
1      Local_3     35
Total  Local_3     35
Total  Total       155
1      Total       120
2      Total       35
现在很容易进行旋转:

WITH prepared AS (
  SELECT
    PubID,
    QTY = CASE [Transaction] WHEN 'Add' THEN QTY ELSE -QTY END,
    LocationID
  FROM dbo.InventoryLog
)
SELECT
  PubID,
  Local_1 = [1],
  Local_2 = [2],
  Local_3 = [3]
FROM prepared
PIVOT
(
  SUM(QTY)
  FOR LocationID IN ([1], [2], [3])
) AS p
;
请注意,您实际上可以事先准备名称
Local_1
Local_2
Local_3
,并避免在主选择中重命名它们。假设它们是通过将
LocationID
值附加到字符串
Local
来形成的,下面是我的意思示例:

WITH prepared AS (
  SELECT
    PubID,
    QTY  = CASE [Transaction] WHEN 'Add' THEN QTY ELSE -QTY END,
    Name = 'Local_' + CAST(LocationID AS varchar(10))
  FROM dbo.InventoryLog
)
SELECT
  PubID,
  Local_1,
  Local_2,
  Local_3
FROM prepared
PIVOT
(
  SUM(QTY)
  FOR Name IN (Local_1, Local_2, Local_3)
) AS p
;
(由于某些结果可能为空,因此需要合并。)

但是有一种替代方法,你不需要额外一次明确列出所有的位置。您可以使用以下方法返回每个
PubID
的总计以及
prepared
数据集中的详细信息:

SELECT
  ...
  Local_1 = SUM(CASE [Transaction] WHEN 'Add' THEN QTY ELSE -QTY END),
  ...
FROM ...
GROUP BY ...
PubID  QTY  LocationID
-----  ---  ----------
1      10   1
1      20   2
1      30   3
1      -5   1
1      -10  2
1      -5   3
2      10   1
2      10   2
2      -5   2
2      -8   2
1      20   1
1      20   2
2      -2   2
WITH prepared AS (
  SELECT
    PubID,
    QTY   = CASE [Transaction] WHEN 'Add' THEN QTY ELSE -QTY END,
    LocationID,
    Total = SUM(CASE [Transaction] WHEN 'Add' THEN QTY ELSE -QTY END)
            OVER (PARTITION BY PubID)
  FROM dbo.InventoryLog
)
…
PubID  LocationID  QTY
-----  ----------  ---
1      Local_1     35
2      Local_1     10
Total  Local_1     45
1      Local_2     50
2      Local_2     25
Total  Local_2     75
1      Local_3     35
Total  Local_3     35
Total  Total       155
1      Total       120
2      Total       35
这将是你的全部专栏。对于行,当您熟悉分组功能时,实际上很容易添加它:

…
SELECT
  PubID,
  Local_1 = SUM([1]),
  Local_2 = SUM([2]),
  Local_3 = SUM([3]),
  Total   = SUM(Total)
FROM prepared
PIVOT
(
  SUM(QTY)
  FOR LocationID IN ([1], [2], [3])
) AS p
GROUP BY ROLLUP(PubID)
;
就这些了。总而言之,这里有一个:


最后,您可能还希望对总和应用合并,以避免在数据中返回空值(如果有必要)。

还有另一种方法:在数据透视之前聚合数据,然后透视聚合的结果

与我的另一个建议相比,这个方法在语法上要简单得多,这也使得它更容易理解和维护

所有聚合都是在分组函数的帮助下完成的。基本查询如下:

SELECT
  PubID,
  LocationID,
  QTY = SUM(CASE [Transaction] WHEN 'Add' THEN QTY ELSE -QTY END)
FROM dbo.InventoryLog
GROUP BY CUBE(PubID, LocationID)
你可以看到与我的另一个答案相同的CASE表达式,只是这次它可以直接用作SUM的参数

使用“按多维数据集聚合”不仅可以按
(PubID,LocationID)
计算总数,还可以分别按
PubID
LocationID
计算总数。这是您问题中示例的查询结果:

PubID  LocationID  QTY
-----  ----------  ---
1      1           35
2      1           10
NULL   1           45
1      2           50
2      2           25
NULL   2           75
1      3           35
NULL   3           35
NULL   NULL        155
1      NULL        120
2      NULL        35
LocationID
中带null的行是最终结果集中的行总数,而
PubID
中带null的行是列总数。两列中均为空的行是总计

在继续进行数据透视之前,我们需要为数据透视结果准备列名。如果名称应该从
LocationID
的值派生,则以下声明将替换原始查询的SELECT子句中的
LocationID

Location = COALESCE('Local_' + CAST(LocationID AS varchar(10)), 'Total')
PubID = COALESCE(CAST(PubID AS varchar(10)), 'Total')
在同一阶段,我们还可以用
'Total'
替换
PubID
中的空值,因此这将替换SELECT子句中的
PubID

Location = COALESCE('Local_' + CAST(LocationID AS varchar(10)), 'Total')
PubID = COALESCE(CAST(PubID AS varchar(10)), 'Total')
现在,结果将如下所示:

SELECT
  ...
  Local_1 = SUM(CASE [Transaction] WHEN 'Add' THEN QTY ELSE -QTY END),
  ...
FROM ...
GROUP BY ...
PubID  QTY  LocationID
-----  ---  ----------
1      10   1
1      20   2
1      30   3
1      -5   1
1      -10  2
1      -5   3
2      10   1
2      10   2
2      -5   2
2      -8   2
1      20   1
1      20   2
2      -2   2
WITH prepared AS (
  SELECT
    PubID,
    QTY   = CASE [Transaction] WHEN 'Add' THEN QTY ELSE -QTY END,
    LocationID,
    Total = SUM(CASE [Transaction] WHEN 'Add' THEN QTY ELSE -QTY END)
            OVER (PARTITION BY PubID)
  FROM dbo.InventoryLog
)
…
PubID  LocationID  QTY
-----  ----------  ---
1      Local_1     35
2      Local_1     10
Total  Local_1     45
1      Local_2     50
2      Local_2     25
Total  Local_2     75
1      Local_3     35
Total  Local_3     35
Total  Total       155
1      Total       120
2      Total       35
在这一点上,一切都准备好应用PIVOT。根据所需格式转换上述结果集:

WITH aggregated AS (
  SELECT
    PubID    = COALESCE(CAST(PubID AS varchar(10)), 'Total'),
    Location = COALESCE('Local_' + CAST(LocationID AS varchar(10)), 'Total'),
    QTY      = SUM(CASE [Transaction] WHEN 'Add' THEN QTY ELSE -QTY END)
  FROM dbo.InventoryLog
  GROUP BY CUBE(PubID, LocationID)
)
SELECT
  PubID,
  Local_1,
  Local_2,
  Local_3,
  Total
FROM aggregated
PIVOT (
  MAX(QTY)
  FOR Location IN (Local_1, Local_2, Local_3, Total)
) AS p
;

此查询将为缺少的
(publid,LocationID)
组合返回空值。如果您想返回0,请将COALESCE应用于
aggregated

的定义中的SUM的结果。我认为这个问题可能会重复,这足以让您有理由发表一篇新文章。我见过很多类似的例子,但没有一个能做到这一点。如果你看到更多关于我是什么的问题,我愿意发表其他帖子。谢谢