SQL Group Count - sql

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) );

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.

Select sum with other table in SQL

How do I select sum with other table if I have data like below:
Table Member
MemberID Name DateJoin
M0001 John 01/01/2015
M0002 Willy 03/20/2016
M0003 Teddy 02/01/2017
etc....
Table Transaction
MemberID TransDate Total
M0002 02/01/2015 100000
M0002 02/28/2015 222000
M0001 01/01/2016 150000
M0001 01/26/2017 160000
M0002 01/25/2017 160000
M0003 02/01/2017 9000
I want the result as a sum of how many times the member transaction in shop in years 2015-2017
The result I want it's:
MemberID 2015 2016 2017
M0001 0 1 1
M0002 2 0 1
M0003 0 0 1
How many members will appear in Result although don't have transaction too.
try dynamic sql .
--load in #temp table
select MemberID , datepart (yyyy ,TransDate ) as TransDate ,COUNT(*)as cnt into #temp from [Transaction]
group by MemberID , datepart (yyyy ,TransDate )
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.TransDate)
FROM #temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT MemberID, ' + #cols + ' from
(
select MemberID
, cnt
, TransDate
from #temp
) x
pivot
(
max(cnt)
for TransDate in (' + #cols + ')
) p '
execute(#query)
drop #temp -- cleanup of #temp table
CREATE TABLE #Table1
([MemberID] varchar(5), [Name] varchar(5), [DateJoin] datetime)
;
INSERT INTO #Table1
([MemberID], [Name], [DateJoin])
VALUES
('M0001', 'John', '2015-01-01 00:00:00'),
('M0002', 'Willy', '2016-03-20 00:00:00'),
('M0003', 'Teddy', '2017-02-01 00:00:00')
;
CREATE TABLE #Table2
([MemberID] varchar(5), [TransDate] datetime, [Total] int)
;
INSERT INTO #Table2
([MemberID], [TransDate], [Total])
VALUES
('M0002', '2015-02-01 00:00:00', 100000),
('M0002', '2015-02-28 00:00:00', 222000),
('M0001', '2016-01-01 00:00:00', 150000),
('M0001', '2017-01-26 00:00:00', 160000),
('M0002', '2017-01-25 00:00:00', 160000),
('M0003', '2017-02-01 00:00:00', 9000)
;
select MemberID,[2015], [2016], [2017]
from
(
select a.MemberID,a.name,a.DateJoin,year(b.TransDate)[year],b.Total from #Table1 A join
#Table2 B on a.MemberID=b.MemberID
) src
pivot
(
count(total)
for year in ([2015], [2016], [2017])
) piv;
output
MemberID 2015 2016 2017
M0001 0 1 1
M0002 2 0 1
M0003 0 0 1
IN 2000
SELECT MEMBERID, COUNT(CASE WHEN YEAR=2015 THEN YEAR END ) AS [2015],
COUNT(CASE WHEN YEAR=2016 THEN YEAR END ) AS [2016],
COUNT(CASE WHEN YEAR=2017 THEN YEAR END ) AS [2017]
FROM (
SELECT A.MEMBERID,A.NAME,A.DATEJOIN,YEAR(B.TRANSDATE)[YEAR],B.TOTAL FROM #TABLE1 A JOIN
#TABLE2 B ON A.MEMBERID=B.MEMBERID)A
GROUP BY MEMBERID
It seems there is no information you need from table member. So select from table transaction alone and count conditionally.
select
memberid,
count(case when year(transdate) = 2015 then 1 end) as [2015],
count(case when year(transdate) = 2016 then 1 end) as [2016],
count(case when year(transdate) = 2017 then 1 end) as [2017]
from transaction
group by memberid
order by memberid;
If you want to include members that don't have any transaction, then you do need a join (an outer join that is):
select
m.memberid,
count(case when year(t.transdate) = 2015 then 1 end) as [2015],
count(case when year(t.transdate) = 2016 then 1 end) as [2016],
count(case when year(t.transdate) = 2017 then 1 end) as [2017]
from member m
left join transaction t on t.memberid = m.memberid
group by m.memberid
order by m.memberid;

How to pivot table for year in SQL?

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

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