6

In SQL Server 2008, I have a table (tblStock) with 3 columns:

  • PartCode (NVARCHAR (50))
  • StockQty (INT)
  • Location (NVARCHAR(50))

some example data below:

    PartCode    StockQty    Location
   .........   .........    .........
    A              10        WHs-A
    B              22        WHs-A
    A               1        WHs-B
    C              20        WHs-A
    D              39        WHs-F
    E               3        WHs-D
    F               7        WHs-A
    A               9        WHs-C
    D               2        WHs-A
    F              54        WHs-E

How to create procedure to get the result as below?

PartCode    WHs-A   WHs-B   WHs-C   WHs-D   WHs-E   WHs-F   Total
........    .....   .....   .....  ......   .....   .....   .....
A            10       1       9       0       0       0      20
B            22       0       0       0       0       0      22
C            20       0       0       0       0       0      20
D             2       0       0       0       0      39      41
E             0       0       0       3       0       0       3
F             7       0       0       0      54       0      61
Total        61       1       9       3      54      39     167

Your help is much appreciated, thanks.

Sarath Subramanian
  • 20,027
  • 11
  • 82
  • 86
Sokea
  • 327
  • 5
  • 19

2 Answers2

9

SAMPLE TABLE

SELECT * INTO #tblStock
FROM
(
SELECT 'A' PartCode,  10 StockQty, 'WHs-A' Location
UNION ALL
SELECT 'B',   22,  'WHs-A'
UNION ALL
SELECT 'A',   1,   'WHs-B'
UNION ALL
SELECT 'C',   20,  'WHs-A'
UNION ALL
SELECT 'D',   39,  'WHs-F'
UNION ALL
SELECT 'E',   3,   'WHs-D'
UNION ALL
SELECT 'F',   7,   'WHs-A'
UNION ALL
SELECT 'A',   9,   'WHs-C'
UNION ALL
SELECT 'D',   2,   'WHs-A'
UNION ALL
SELECT 'F',   54,  'WHs-E'
)TAB

Get the columns for dynamic pivoting and replace NULL with zero

DECLARE @cols NVARCHAR (MAX)

SELECT @cols = COALESCE (@cols + ',[' + Location + ']', '[' + Location + ']')
               FROM (SELECT DISTINCT Location FROM #tblStock) PV 
               ORDER BY Location 
-- Since we need Total in last column, we append it at last
SELECT @cols += ',[Total]'


--Varible to replace NULL with zero
DECLARE @NulltoZeroCols NVARCHAR (MAX)

SELECT @NullToZeroCols = SUBSTRING((SELECT ',ISNULL(['+Location+'],0) AS ['+Location+']' 
FROM (SELECT DISTINCT Location FROM #tblStock)TAB  
ORDER BY Location FOR XML PATH('')),2,8000) 

SELECT @NullToZeroCols += ',ISNULL([Total],0) AS [Total]'

You can use CUBE to find row and column total and replace NULL with Total for the rows generated from CUBE.

DECLARE @query NVARCHAR(MAX)
SET @query = 'SELECT PartCode,' + @NulltoZeroCols + ' FROM 
             (
                 SELECT 
                 ISNULL(CAST(PartCode AS VARCHAR(30)),''Total'')PartCode, 
                 SUM(StockQty)StockQty , 
                 ISNULL(Location,''Total'')Location              
                 FROM #tblStock
                 GROUP BY Location,PartCode
                 WITH CUBE
             ) x
             PIVOT 
             (
                 MIN(StockQty)
                 FOR Location IN (' + @cols + ')
            ) p
            ORDER BY CASE WHEN (PartCode=''Total'') THEN 1 ELSE 0 END,PartCode' 

EXEC SP_EXECUTESQL @query

RESULT

enter image description here

NOTE : If you want NULL instead of zero as values, use @cols instead of @NulltoZeroCols in dynamic pivot code

EDIT :

1. Show only Row Total

  • Do not use the code SELECT @cols += ',[Total]' and SELECT @NullToZeroCols += ',ISNULL([Total],0) AS [Total]'.
  • Use ROLLUP instead of CUBE.

enter image description here

2. Show only Column Total

  • Use the code SELECT @cols += ',[Total]' and SELECT @NullToZeroCols += ',ISNULL([Total],0) AS [Total]'.
  • Use ROLLUP instead of CUBE.
  • Change GROUP BY Location,PartCode to GROUP BY PartCode,Location.
  • Instead of ORDER BY CASE WHEN (PartCode=''Total'') THEN 1 ELSE 0 END,PartCode, use WHERE PartCode<>''TOTAL'' ORDER BY PartCode.

enter image description here

UPDATE : To bring PartName for OP

I am updating the below query to add PartName with result. Since PartName will add extra results with CUBE and to avoid confusion in AND or OR conditions, its better to join the pivoted result with the DISTINCT values in your source table.

DECLARE @query NVARCHAR(MAX)
SET @query = 'SELECT P.PartCode,T.PartName,' + @NulltoZeroCols + ' FROM 
             (                
                 SELECT 
                 ISNULL(CAST(PartCode AS VARCHAR(30)),''Total'')PartCode, 
                 SUM(StockQty)StockQty , 
                 ISNULL(Location,''Total'')Location              
                 FROM #tblStock
                 GROUP BY Location,PartCode
                 WITH CUBE                   
             ) x
             PIVOT 
             (
                 MIN(StockQty)
                 FOR Location IN (' + @cols + ')
            ) p
            LEFT JOIN
            (  
                SELECT DISTINCT PartCode,PartName
                FROM #tblStock  
            )T
            ON P.PartCode=T.PartCode
            ORDER BY CASE WHEN (P.PartCode=''Total'') THEN 1 ELSE 0 END,P.PartCode' 

EXEC SP_EXECUTESQL @query

enter image description here

Sarath Subramanian
  • 20,027
  • 11
  • 82
  • 86
  • Exactly what i need, thanks @Sarath Avanavu for your solution and explanation. – Sokea Jan 31 '15 at 01:14
  • one more thing, any solution if i want to add more column (PartName) after PartCode? [PartCode,PartName,WHs-A,..,Total] – Sokea Jan 31 '15 at 05:03
  • One partcode will have only 1 partname right? @Sokea – Sarath Subramanian Jan 31 '15 at 05:08
  • Could it be replaced the last NULL value in PartName with nothing? – Sokea Jan 31 '15 at 05:51
  • Yes. It can be done using **ISNULL(T.PartName,'') AS PartName** or if u want nothing as string, use **ISNULL(T.PartName,'Nothing') AS PartName** @Sokea – Sarath Subramanian Jan 31 '15 at 05:53
  • tried, but got error: Msg 102, Level 15, State 1, Line 1 Incorrect syntax near ','. Msg 105, Level 15, State 1, Line 22 Unclosed quotation mark after the character string ') THEN 1 ELSE 0 END,P.PartCode'. – Sokea Jan 31 '15 at 05:56
  • Thats because you have to use single quotes for two times in dynamic sql. Use **ISNULL(T.PartName,'''') AS PartName** or **ISNULL(T.PartName,''Nothing'') AS PartName**. Note : Its not double qoutes. Its 2 single qoutes together. @Sokea – Sarath Subramanian Jan 31 '15 at 06:01
  • I still have one problem with dynamic pivot in link here [http://stackoverflow.com/questions/28290971/arithmetic-operators-against-dynamic-column-in-sql-server-2008?noredirect=1#comment44937479_28290971] pls. kindly check if you have any solution idea. thanks. – Sokea Feb 03 '15 at 07:07
  • Please delete all the comments my friend so that it will be neat :) @Sokea – Sarath Subramanian Feb 03 '15 at 08:35
1

you need to use case based aggregation to pivot the data

To get the total row use union

In case the Location values are not known in advance, you need to construct a dynamic query

you can also use pivot keyword to do the same.

select partCode,
 sum( case when Location='WHs-A' then StockQty
      else 0 end
    ) as 'Whs-A',
 sum( case when Location='WHs-B' then StockQty
      else 0 end
    ) as 'Whs-B',
sum(StockQty) as 'Total'
from tblStock
group by partCode
union all
select 'Total' as 'partCode',
sum( case when Location='WHs-A' then StockQty
    else 0 end ) as 'Whs-A',
sum( case when Location='WHs-B' then StockQty
    else 0 end) as 'Whs-B',
sum(StockQty) as 'Total'
from tblStock
radar
  • 13,270
  • 2
  • 25
  • 33
  • 1
    thanks radar. anyway Location is unknown column, could you pls. provide dynamic query? – Sokea Jan 30 '15 at 05:45