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
;
Related
I have a table like this
Date County Location
2020-01-01 abc west
2020-01-02 abc north
2020-02-01 xzy west
2020-02-02 xzy east
2020-02-03 xyz east
Can we group and count so it can become
County jan feb
abc 2
xyz 3
Location jan feb
west 1
north 1
west 1
east 2
Thank you
Try this as the base query and then write a pivot query based on this query result as shown in the demo link.
For your reference FROM - Using PIVOT and UNPIVOT.
Select country
, FORMAT([date], 'MMMM') as Month
, count(*) as Tot
from YourTable
group by country, FORMAT([date], 'MMMM')
Pivot query needed an aggregate function. Here is the complete query.
create table YourTable
([Date] Date
, Country varchar(20)
, Location varchar(20))
insert into YourTable values
('2020-01-01', 'abc', 'west'),
('2020-01-02', 'abc', 'north'),
('2020-02-01', 'xzy', 'west'),
('2020-02-02', 'xzy', 'east'),
('2020-02-03', 'xyz', 'east')
Select * into #temp from(
Select country
, FORMAT([date], 'MMMM') as Month
, count(*) as Tot
from YourTable
group by country, FORMAT([date], 'MMMM')
)a
--Select * from #temp
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.Month)
FROM #temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT country, ' + #cols + ' from
(
select Country
, Month
, Tot
from #temp
) x
pivot
(
max(Tot)
for Month in (' + #cols + ')
) p '
execute(#query)
Live db<>fiddle demo.
Use conditional aggregation along with grouping sets:
select county, location,
sum(case when date >= '2020-01-01' and date < '2020-02-01' then 1 end) as jan,
sum(case when date >= '2020-02-01' and date < '2020-03-01' then 1 end) as feb
from t
group by grouping sets ( (country), (location) );
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)
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.
I have a query
SELECT mua.Id,tnk.Plate,Cast(mua.Tarih as Date) as M_Date
FROM Muayene mua
LEFT JOIN Tanker tnk on (tnk.OID=mua.TankerId)
ORDER BY mua.Id DESC
But I need result to be
Id Plate 2011 2012 2013 2014 2015 2016 2017
5 34VM7969 2011-08-02
4 34VM7969 2016-08-19
3 34VM7969 2017-03-19
1 34VM7969 2014-08-08
How can I change my query for this result ?
You'll need to get the year for each one of your date values and then PIVOT on those values. You can use a couple of different functions in SQL Server to get this.
DatePart - the syntax would be DatePart(year, yourDate)
Year - syntax being Year(yourDate)
Either one of these will return the year for each date, you'll then place the years in your PIVOT as the new columns.
select plate, [2011], [2012], [2013], [2014], [2015], [2016], [2017]
from
(
SELECT tnk.Plate,
Cast(mua.Tarih as Date) as M_Date,
year(mua.Tarih) yr
FROM Muayene mua
LEFT JOIN Tanker tnk
on (tnk.OID=mua.TankerId)
) d
pivot
(
max(m_date)
for yr in ([2011], [2012], [2013], [2014], [2015], [2016], [2017])
) piv;
See Demo. You'll notice that in this query that I removed the column mua.Id. This is because when you pivot data you will group by each column in your query, since these values are distinct you'll return different rows. By removing the column from your query, you will return a result:
| PLATE | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 |
|----------|------------|--------|--------|------------|------------|------------|------------|
| 34VM7969 | 2011-08-02 | (null) | (null) | 2014-08-08 | 2015-02-21 | 2016-08-19 | 2017-03-09 |
Finally, if you are going to have an unknown number of dates, then I'd suggest 2 things - use a Calendar table and then dynamic SQL.
Then Calendar table is just a list of dates that you can use for querying similar to:
create table calendar
(
date datetime
);
insert into calendar
select '2011-01-01' union all
select '2012-01-01' union all
select '2013-01-01' union all
select '2014-01-01' union all
select '2015-01-01' union all
select '2016-01-01' union all
select '2016-01-01' union all
select '2017-01-01' union all
select '2018-01-01'
You'd then create a list of the years in a sql string and execute that string, similar to:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(year(date))
from calendar
group by year(date)
order by year(date)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT plate, ' + #cols + '
from
(
select plate,
m_Date = convert(varchar(10), m_date, 120),
year(m_date) yr
from yourquery
) x
pivot
(
max(m_date)
for yr in (' + #cols + ')
) p '
execute sp_executesql #query;
See Demo
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