SQL metric rows to date columns- - sql

Suppose I have the following table:
Client ContainerID Year Month NumberOfViews
bar 116025 2019 1 2
dandy 2753 2020 1 3
dandy 2753 2020 2 2
dandy 4247 2020 1 1
demo 20037 2019 1 1
I want it to transform to the following table:
Client ContainerID Jan-2019 Jan-2020 Feb-2020
bar 116025 2 0 0
dandy 2753 0 3 2
dandy 4247 0 1 0
demo 20037 1 0 0
meaning: year + month cell rows turns into columns and the value for 'NumberOfViews' goes to the right date column.

There you go.
Replace your table name:
WITH cte AS(
SELECT Client
,ContainerId
,NumberOfViews
,FORMAT(CAST(CAST([Year] AS CHAR(4)) + '-' + CAST([Month] AS CHAR(2)) + '-01' AS DATE), 'MMM-yyyy') AS [DateCol]
FROM dbo.Test)
SELECT Client
,ContainerId
,ISNULL([Jan-2019], 0) AS [Jan-2019]
,ISNULL([Jan-2020], 0) AS [Jan-2020]
,ISNULL([Feb-2020], 0) AS [Feb-2020]
FROM cte
PIVOT
(
SUM(NumberOfViews)
FOR DateCol IN ([Jan-2019], [Jan-2020], [Feb-2020])
) AS PivotTable;

I think you need a Dynamic PIVOT as the month-year is not static. Please try this below logic-
Please use your original table name where ever you found "your_table" in the script.
DECLARE #cols AS NVARCHAR(MAX),
#sqlCommand AS NVARCHAR(MAX);
SELECT #cols =
STUFF((SELECT ( '],[' + A.YM)
FROM
(
SELECT
CASE Month
WHEN 1 THEN 'Jan-' WHEN 2 THEN 'Feb-' WHEN 3 THEN 'Mar-'
WHEN 4 THEN 'Apr-' WHEN 5 THEN 'May-' WHEN 6 THEN 'Hun-'
WHEN 7 THEN 'Jul-' WHEN 8 THEN 'Aug-' WHEN 9 THEN 'Sep-'
WHEN 10 THEN 'Oct-' WHEN 11 THEN 'Nov-' WHEN 12 THEN 'Dec-'
END
+ CAST(Year AS VARCHAR) YM
FROM your_table
) A
ORDER BY A.YM
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')+']'
FROM your_table
SET #sqlCommand=
N'SELECT Client,ContainerID,'+SUBSTRING(#cols,2,LEN(#cols))+'
FROM
(
SELECT Client,ContainerID,YM,NumberOfViews
FROM
(
SELECT Client,ContainerID,NumberOfViews,
CASE Month
WHEN 1 THEN ''Jan-'' WHEN 2 THEN ''Feb-'' WHEN 3 THEN ''Mar-''
WHEN 4 THEN ''Apr-'' WHEN 5 THEN ''May-'' WHEN 6 THEN ''Hun-''
WHEN 7 THEN ''Jul-'' WHEN 8 THEN ''Aug-'' WHEN 9 THEN ''Sep-''
WHEN 10 THEN ''Oct-'' WHEN 11 THEN ''Nov-'' WHEN 12 THEN ''Dec-''
END
+ CAST(Year AS VARCHAR) YM
FROM your_table
)A
) AS P
PIVOT
(
SUM(NumberOfViews)
FOR YM IN('+SUBSTRING(#cols,2,LEN(#cols))+')
) PVT'
--PRINT #sqlCommand
EXEC (#sqlCommand)

Related

How to convert columns to rows in SQL Server without pivot and value is dynamic

I have some values in rows like :
Month | Product | SalesQty
-------+---------+---------
Jan-17 | ABC | 3
Feb-17 | ABC | 6
Apr-17 | ABC | 19
But i want to show the some values in columns like:
Model| Apr-17 | May-17 | Jun-17 | Jul-17
ABC 1 2 12 0
BCS 212 12 12 112
Months must be generated dynamically. Static month will not help me in this situation.
Why not Use pivot? it is simpler than other solutions like case expression:
SELECT *
FROM table
PIVOT
(
SUM(SalesQty)
FOR Month IN([Apr-17] ,[May-17], [Jun-17], [Jul-17])
) AS p;
To do it dynamically you can use the same query with dynamic sql like this:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #query AS NVARCHAR(MAX);
select #cols = STUFF((SELECT distinct ',' +QUOTENAME(CONCAT(LEFT(datename(month, Month), 3),
CAST(DATEPART(day, month) AS NVARCHAR(2))))
FROM table1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT #query = ' SELECT *
FROM
(
SELECT product, SalesQty,
CONCAT(LEFT(datename(month, Month), 3),
CAST(DATEPART(day, month) AS NVARCHAR(2))) AS Month
FROM table1
) AS t
PIVOT
(
SUM(SalesQty)
FOR Month IN( ' + #cols + ' )
) AS p';
execute(#query);
dynamic demo
If you don't want to use PIVOT then you can use CASE expression like this:
SELECT product,
SUM(CASE WHEN month = 'Jan17' THEN SalesQty ELSE 0 END) AS Jan17,
SUM(CASE WHEN month = 'Jan17' THEN SalesQty ELSE 0 END) AS Jun17,
SUM(CASE WHEN month = 'Jan17' THEN SalesQty ELSE 0 END) AS Jul17
FROM
(
SELECT product, SalesQty,
CONCAT(LEFT(datename(month, Month), 3),
CAST(DATEPART(day, month) AS NVARCHAR(2))) AS Month
FROM table1
) AS t
GROUP BY Product;
Then to do this dynamically, you just need to replace the case expression part to by dynamic in the cols names variable.

Dynamic PIVOT in SQL Server 2005

i rare use PIVOT in sql server but now requirement is something that i have to use PIVOT.
my table structure is something like
CurDate Warranty_Info
------- -------------
01/01/2009 50
01/05/2009 30
01/03/2009 220
01/01/2010 40
01/06/2010 10
01/02/2010 0
01/01/2011 10
01/05/2012 420
01/05/2013 130
now i have to show the data in this way
Month 2009 2010 2011 2012 2013
----- ---- ---- ---- ---- -----
JAN 10 0 11 32 98
FEB 20 10 21 11 44
MAR 0 224 33 77 31
UPTO
DEC
1) data should display month wise order....so jan first
2) if no data exist in any month then month name will come with 0 as value for that month.
i tried but fail. here is my sql by which i tried.
SELECT *
FROM (SELECT DateName(month,DateAdd(month,Month(CurDate),-1)) as [Month],
YEAR(CurDate) AS WarrantyYear,
Warranty_Info
FROM eod_main) AS D
PIVOT (
SUM(Warranty_Info)
FOR WarrantyYear IN (
[2009],[2010],[2011],[2012],[2013]
)
) AS P
ORDER BY DATENAME(MONTH,DATEADD(MONTH, [Month] - 1, 0))
and i tried to generate sql dynamically this way.
DECLARE #cols AS NVARCHAR(MAX)
DECLARE #PivotTableSQL NVARCHAR(MAX)
DECLARE #StartYear AS INT,
#EndYear AS INT
SET #StartYear=2009
SET #EndYear=2013
select #cols = STUFF((SELECT ',' + QUOTENAME(Year(CurDate))
from eod_main
WHERE Year(CurDate)>=#StartYear AND Year(CurDate) <=#EndYear
group by Year(CurDate)
order by Year(CurDate)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #PivotTableSQL = N'
SELECT * FROM (
SELECT Month(CurDate), YEAR(CurDate) AS WarrantyYear,Warranty_Info FROM eod_main) AS D
PIVOT (
SUM(Warranty_Info)
FOR WarrantyYear IN (
' + #cols + '
)
) AS PivotTable
'
print #PivotTableSQL
but some where i am facing problem like
1) display month name
2) order by month no
3) null value show show 0 instead of NULL
4) if no data exist for any month then month name should display with 0 value.
please guide me how to achieve it. thanks
UPDATE
DECLARE #query varchar(max)
DECLARE #StartYr INT
DECLARE #ENDYr INT
declare #years varchar(max), #yearsColumns varchar(max)
SET #StartYr=2011
SET #EndYr=2013
SELECT 1 mID, 'January' as month into #tempMonths UNION ALL
SELECT 2,'February' as month UNION ALL
SELECT 3,'March' as month UNION ALL
SELECT 4,'April' as month UNION ALL
SELECT 5,'May' as month UNION ALL
SELECT 6,'June' as month UNION ALL
SELECT 7,'July' as month UNION ALL
SELECT 8,'August' as month UNION ALL
SELECT 9,'September' as month UNION ALL
SELECT 10,'October' as month UNION ALL
SELECT 11,'November' as month UNION ALL
SELECT 12,'December' as month
SELECT #years=COALESCE(#years+',','') +'['+ cast(years as varchar(4))+']',
#yearsColumns=COALESCE(#yearsColumns+',','') +'isnull(['+ cast(years as varchar(4))+'],0)
as ['+cast(years as varchar(4))+']'
from (select distinct YEAR(CurDate) years from EOD_Main
WHERE YEAR(CurDate)>=#StartYr AND YEAR(CurDate)<=#EndYr
) as x
SET #query = 'Select months,'+#yearsColumns+' from (
select distinct mID, YEAR(CurDate) years,[MONTH] months,
isnull(Warranty_Info,0) as Warranty_Info from EOD_Main
right join #tempMonths on datename(month,CurDate ) =[month]
) as xx
PIVOT
(
SUM(xx.Warranty_Info) FOR years IN ('+#years+')
)
as pvt ORDER BY mID'
--PRINT #query
EXEC(#query)
drop table #tempMonths
Hi Find the Below Solution, i hope that it is help full to you
SELECT CAST('01/01/2009' AS date) CurDate , 50 Warranty_Info INto #temp UNION all SELECT
CAST('05/01/2009' AS date) , 30 UNION all SELECT
CAST('03/01/2009' AS date) , 220 UNION all SELECT
CAST('01/01/2010' AS date) , 40 UNION all SELECT
CAST('06/01/2010' AS date) , 10 UNION all SELECT
CAST('02/01/2010' AS date) , 0 UNION all SELECT
CAST('01/01/2011' AS date) , 10 UNION all SELECT
CAST('05/01/2012' AS date) , 420 UNION all SELECT
CAST('05/01/2013' AS date) , 130
SELECT 1 mID, 'January' as month into #tempMonths UNION ALL
SELECT 2,'February' as month UNION ALL
SELECT 3,'March' as month UNION ALL
SELECT 4,'April' as month UNION ALL
SELECT 5,'May' as month UNION ALL
SELECT 6,'June' as month UNION ALL
SELECT 7,'July' as month UNION ALL
SELECT 8,'August' as month UNION ALL
SELECT 9,'September' as month UNION ALL
SELECT 10,'October' as month UNION ALL
SELECT 11,'November' as month UNION ALL
SELECT 12,'December' as month
declare #years varchar(max), #yearsColumns varchar(max)
SELECT #years=COALESCE(#years+',','') +'['+ cast(years as varchar(4))+']',
#yearsColumns=COALESCE(#yearsColumns+',','') +'isnull(['+ cast(years as varchar(4))+'],0) as ['+cast(years as varchar(4))+']'
from (select distinct YEAR(CurDate) years from #temp) as x
print #years
DECLARE #query varchar(max)= '
select months,'+#yearsColumns+' from (
select distinct mID, YEAR(CurDate) years,[MONTH] months, isnull(Warranty_Info,0) as Warranty_Info from #temp
right join #tempMonths on datename(month,CurDate ) =[month]
) as xx
PIVOT
(
SUM(xx.Warranty_Info) FOR years IN ('+#years+')
)
as pvt ORDER BY mID'
PRINT #query
EXEC(#query)
output islike below
months 2009 2010 2011 2012 2013
--------- ----------- ----------- ----------- ----------- -----------
January 50 40 10 0 0
February 0 0 0 0 0
March 220 0 0 0 0
April 0 0 0 0 0
May 30 0 0 420 130
June 0 10 0 0 0
July 0 0 0 0 0
August 0 0 0 0 0
September 0 0 0 0 0
October 0 0 0 0 0
November 0 0 0 0 0
December 0 0 0 0 0
if it is give accurate result don't forget to make a vote.

data between two dates in column for each day

I have table worker
id name
----------- -------------------
5 Артур Петрович
6 Дмитрий Белов
7 Казарян Артур
and another table
id date amount id_worker
----------- ---------- ----------- -----------
27 2013-09-12 1500 5
28 2013-09-12 100 6
29 2013-09-12 500 5
30 2013-09-12 500 6
31 2013-09-14 1000 7
32 2013-09-15 100 5
33 2013-09-15 200 5
I want to write stored procedure which on input gets start and end dates
and on output I want to get this table if:
start date:2013-09-10
end date :2013-09-15
Name 2013-09-10 2013-09-11 2013-09-12 2013-09-13 2013-09-14 2013-09-15
_______________ __________ __________ __________ __________ __________ __________
Артур Петрович 0 0 2000 0 0 300
Дмитрий Белов 0 0 600 0 0 0
Казарян Артур 0 0 0 0 1000 0
The only way I konw to do this is using Dynamic SQL, IMO there is no risk of SQL Injection if the tables structures are known ahead
DECLARE #DateList VARCHAR(MAX), #DateListCoalesced VARCHAR(MAX)
SELECT #DateList = '', #DateListCoalesced = ''
;WITH DateLimits AS (
SELECT CAST('2013-9-10' AS DATE) AS dt
UNION ALL
SELECT DATEADD(dd, 1, dt)
FROM DateLimits s
WHERE DATEADD(dd, 1, dt) <= CAST('2013-9-15' AS DATE))
SELECT #DateList = #DateList + '[' + CAST(dt AS VARCHAR)+ '], ' ,
#DateListCoalesced = #DateListCoalesced + ' COALESCE( [' + CAST(dt AS VARCHAR)+ '] , 0) as [' + CAST(dt AS VARCHAR)+ '], '
FROM DateLimits
;SET #DateList = LEFT(#DateList, LEN(#DateList) - 1)
;SET #DateListCoalesced = LEFT(#DateListCoalesced, LEN(#DateListCoalesced) - 1)
DECLARE #query NVARCHAR(max)
SET #query = N'SELECT [Name], ' + #DateListCoalesced +'
FROM
(SELECT [Name], [Date], [Amount]
FROM WorkerAmount
INNER JOIN Worker ON WorkerAmount.id_worker = Worker.id
) p
PIVOT
(
Sum ([Amount] )
FOR [Date] IN ( '+ #DateList +' )
) AS pvt '
EXEC sp_executesql #Query
This answer uses a combination of few other questions
getting dates between range of dates
Pivots with dynamic columns in sql-server
replace null values in sql pivot

How to apply pivot in below scenarios

I have below table
Name Month Year Count
----------------------------
xxx 12 2012 24
xxx 1 2013 42
xxx 2 2013 23
yyy 12 2012 34
yyy 1 2013 12
yyy 2 2013 54
I would like to convert it into below format,
Name Dec-12 Jan-13 Feb-13
--------------------------------
xxx 24 42 23
yyy 34 12 54
How to apply pivot?
Since you are using SQL Server there are several ways that you can pivot the data from rows into columns.
If your values are limited or you have a known number of values, then you can hard-code the values with a static pivot:
select name, [Dec_12], [Jan_13], [Feb_13]
from
(
select name,
left(datename(month, dateadd(month, month, 0) -1), 3) +'_'+right(cast(year as varchar(4)), 2) MY,
[count]
from yourtable
) src
pivot
(
sum(count)
for my in ([Dec_12], [Jan_13], [Feb_13])
) piv;
See SQL Fiddle with Demo.
Now, if you have an unknown number of values, then you will need to implement dynamic SQL to generate the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(my)
from
(
select left(datename(month, dateadd(month, month, 0) -1), 3) +'_'+right(cast(year as varchar(4)), 2) my,
CAST(
CAST(year AS VARCHAR(4)) +
RIGHT('0' + CAST(month AS VARCHAR(2)), 2) +
'01'
AS DATETIME) fulldate
from yourtable
) t
group by my, fulldate
order by fulldate
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT name, ' + #cols + '
from
(
select name,
left(datename(month, dateadd(month, month, 0) -1), 3) +''_''+right(cast(year as varchar(4)), 2) MY,
[count]
from yourtable
) x
pivot
(
sum(count)
for my in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo.
This difference with this and the static version is if you need an unknown number of dates or want this to automatically update with new dates when they are available, this will return the new data without changing the code.
The result of both queries is:
| NAME | DEC_12 | JAN_13 | FEB_13 |
-----------------------------------
| xxx | 24 | 42 | 23 |
| yyy | 34 | 12 | 54 |
Try this:
WITH CTE
AS
(
SELECT
Name,
CAST(Month AS VARCHAR(2)) + '-' + CAST(Year AS VARCHAR(4)) AS MonthYear,
[Count]
FROM tablename
)
SELECT
Name,
[12-2012] AS 'Dec-12',
[1-2013] AS 'Jan-13',
[2-2013] AS 'Feb-13'
FROM CTE
PIVOT
(
MAX([Count])
FOR MonthYear IN([12-2012],
[1-2013],
[2-2013])
) AS p;
SQL Fiddle Demo
SELECT t.name
, MAX(CASE
WHEN t.month=12 AND t.year = 2012
THEN count
ELSE NULL
END) AS "Dec_12"
, MAX(CASE
WHEN t.month=1 AND t.year = 2013
THEN count
ELSE NULL
END) AS "Jan_13"
, MAX(CASE
WHEN t.month=2 AND t.year = 2013
THEN count
ELSE NULL
END) AS "Feb_13"
FROM table t
GROUP BY t.name
;

Finding grand total of day wise total monthly sale

in rows i have months. In columns i have 1 to 31 days.
I want to add a column at the end Sum of all sale sale in the 31 days of a month.
SELECT * FROM (
SELECT DATENAME(month, date) AS SaleMonth,
DATEPART(dd,date) AS SaleDay FROM EnquiryMaster
) p PIVOT
(COUNT (SaleDay) FOR
SaleDay IN ( [1],[2],[3],[4],[5],[6],[7],[8],[9],[10],
[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],
[21],[22],[23],[24],[25],[26],[27],[28],[29],[30],[31] )) AS pvt
This code displays day wise data of 31 days but doesnt display sum.
If you want this as a column to display the total by month, there unfortunately is no easy way. You can use something like this:
SELECT SaleMonth,
[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],
[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],
[21],[22],[23],[24],[25],[26],[27],[28],[29],[30],[31],
([1]+[2]+[3]+[4]+[5]+[6]+[7]+[8]+[9]+[10]+
[11]+[12]+[13]+[14]+[15]+[16]+[17]+[18]+[19]+[20]+
[21]+[22]+[23]+[24]+[25]+[26]+[27]+[28]+[29]+[30]+[31]) TotalMonth
FROM
(
SELECT DATENAME(month, date) AS SaleMonth,
DATEPART(dd,date) AS SaleDay
FROM EnquiryMaster
) p
PIVOT
(
COUNT (SaleDay)
FOR SaleDay IN ( [1],[2],[3],[4],[5],[6],[7],[8],[9],[10],
[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],
[21],[22],[23],[24],[25],[26],[27],[28],[29],[30],[31] )
) AS pvt
See SQL Fiddle with Demo
This might be a case when dynamic sql would be easier to implement to pivot the data. By using dynamic SQL, you would not have to hard-code the values.
If you were to use dynamic sql your query would be similar to this:
DECLARE #cols AS NVARCHAR(MAX),
#colsTotal AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(DATEPART(dd,date))
from EnquiryMaster
group by DATEPART(dd,date)
order by DATEPART(dd,date)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #colsTotal = STUFF((SELECT distinct '+' + QUOTENAME(DATEPART(dd,date))
from EnquiryMaster
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT SaleMonth,' + #cols + ', '+ #colsTotal+' as GrandTotal from
(
SELECT DATENAME(month, date) AS SaleMonth,
DATEPART(dd,date) AS SaleDay
FROM EnquiryMaster
) x
pivot
(
count(SaleDay)
for SaleDay in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo
Or another suggestion, I might have would be to alternate your columns headers to be the Month and then you can implement a GROUP BY with ROLLUP on each month to get a totals row.
So your new result set would look like this:
Day | Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec
-------------------------------------------------------------------------------
1 | 12 | 34 | 5 | 3 | 3 | 2 | 32 | 0 | 9 | 87 | 23 | 54
Total | ....
try BETWEEN Condition
SELECT * FROM (SELECT DATENAME(month, date) AS SaleMonth,DATEPART(dd,date) AS SaleDay FROM EnquiryMaster) p PIVOT (COUNT (SaleDay) FOR SaleDay BETWEEN 1 and 31) AS pvt
Try adding just one more column to your subselect:
SELECT * FROM (
SELECT DATENAME(month, date) AS SaleMonth,
DATEPART(dd,date) AS SaleDay,
COUNT(*) OVER (PARTITION BY DATENAME(month, date)) AS MonthlyTotal
FROM EnquiryMaster
) p PIVOT
(COUNT (SaleDay) FOR
SaleDay IN ( [1],[2],[3],[4],[5],[6],[7],[8],[9],[10],
[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],
[21],[22],[23],[24],[25],[26],[27],[28],[29],[30],[31] )) AS pvt