Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pivot dynamically with date as column

I have a table with product id's and names, and another table with the stock of these products on certain dates. Such as Item1 had 6 stock on 1-1-2014 and 8 stock on 2-1-2014. I'm trying to show these in a stored procedure so that it looks like a calendar, showing all the dates in a month and the stock available in the cells. What is the best way to show this?

For example:

Name  | 1-1-2014 | 2-1-2014 | 3-1-2014 | 4-1-2014
Item1 |     6    |     8    |          |    6
Item2 |          |     2    |     1    |

Original tables - Names

 ID |   Name 
  1 |  Item1
  2 |  Item2

Original tables - Stockdates

 ID | NameID  | Stock |    Date 
  1 |    1    |   8   |  2-1-2014    
  2 |    2    |   2   |  4-1-2014 
like image 340
Craphex Avatar asked Dec 11 '14 11:12

Craphex


People also ask

How do I get a pivot table to recognize a date?

Select a cell inside your Pivot Table. On the Analyze tab, in the Filter group, click on the Insert Timeline button. If you have more than one date field in your source data, choose the date field you want to use as your slider.

How do I create a dynamic pivot table in SQL?

You can also create a dynamic pivot query, which uses a dynamic columns for pivot table, means you do not need to pass hard coded column names that you want to display in your pivot table. Dynamic pivot query will fetch a value for column names from table and creates a dynamic columns name list for pivot table.


2 Answers

Here is your sample table

SELECT * INTO #Names
FROM
(
SELECT 1 ID,'ITEM1' NAME 
UNION ALL
SELECT 2 ID,'ITEM2' NAME 
)TAB

SELECT * INTO #Stockdates
FROM
(      
SELECT 1 ID,1 NAMEID,8 STOCK,'2-1-2014 ' [DATE]
UNION ALL
SELECT 2 ID,2 NAMEID,2 STOCK,'4-1-2014 ' [DATE]
)TAB

Put the join data to a temperory table

SELECT N.NAME,S.[DATE],S.STOCK 
INTO #TABLE
FROM #NAMES N
JOIN #Stockdates S ON N.ID=S.NAMEID

Get the columns for pivot

DECLARE @cols NVARCHAR (MAX)

SELECT @cols = COALESCE (@cols + ',[' + CONVERT(NVARCHAR, [DATE], 106) + ']', 
               '[' + CONVERT(NVARCHAR, [DATE], 106) + ']')
               FROM    (SELECT DISTINCT [DATE] FROM #TABLE) PV  
               ORDER BY [DATE]

Now pivot it

DECLARE @query NVARCHAR(MAX)
SET @query = '           
              SELECT * FROM 
             (
                 SELECT * FROM #TABLE
             ) x
             PIVOT 
             (
                 SUM(STOCK)
                 FOR [DATE] IN (' + @cols + ')
            ) p      

            '     
EXEC SP_EXECUTESQL @query

And your result is here

enter image description here

like image 199
Sarath KS Avatar answered Oct 01 '22 20:10

Sarath KS


Step 1 - Fill the gaps (maybe not required)

If your stocks table does not contain stock from every day for every product then you have to get all the dates in a month from somewhere else. You can generate them with a recursive CTE: (variable declarations are omitted)

with dates as
(
   select @startdate as [date]
   union ALL
   select   [date] + 1 
   from dates
   where    [date] < @enddate
)
select  @strDays = COALESCE(@strDays + ', ['+ convert(varchar(8), [date],112) + ']', '['+ convert(varchar(8), [date],112) + ']') 
from    dates;

You can use your preferred date format but it's important to maintain it in all queries.

Step 2 - Bring data to a normal form. You can chose to store it in a temporary table or you can use a CTE again and combine this step with step 3.

Join dates (from above) with products (full) and with stock (left) so you obtain a table like this:

date
product_id    
items

For products and dates where stock is not available you display 0. isnull will do the trick. Make sure the date column is converted to varchar in the same format as in CTE above.

Step 3 - pivot the table (obtained at step 2) by date column in a dynamic query.

I can give you more details but not right now. You can see something similar in another response: Spread distinct values over different columns

like image 36
B0Andrew Avatar answered Oct 01 '22 20:10

B0Andrew