Select distinct count usage divided by month - sql

I do have a table license_Usage which works like a log of the usage of licenses in a day
ID User license date
1 1 A 22/1/2015
2 1 A 23/1/2015
3 1 B 23/1/2015
4 1 A 24/1/2015
5 2 A 22/2/2015
6 2 A 23/2/2015
7 1 B 23/2/2015
Where I want it to return the count of licenses of the day of the month with most usage of licenses the result should look like:
User Jan Feb
1 2 1 ...
2 0 2
I know I can get the total of licenses in a month using this query:
SELECT vlu.[Userkey],
COUNT(CASE WHEN MONTH = 1 THEN 1 END) as JAN,
COUNT(CASE WHEN MONTH = 2 THEN 1 END) as FEB,
COUNT(CASE WHEN MONTH = 3 THEN 1 END) as MAR,
COUNT(CASE WHEN MONTH = 4 THEN 1 END) as APR,
COUNT(CASE WHEN MONTH = 5 THEN 1 END) as MAY,
COUNT(CASE WHEN MONTH = 6 THEN 1 END) as JUN,
COUNT(CASE WHEN MONTH = 7 THEN 1 END) as JUL,
COUNT(CASE WHEN MONTH = 8 THEN 1 END) as AUG,
COUNT(CASE WHEN MONTH = 9 THEN 1 END) as SEP,
COUNT(CASE WHEN MONTH = 10 THEN 1 END) as OCT,
COUNT(CASE WHEN MONTH = 11 THEN 1 END) as NOV,
COUNT(CASE WHEN MONTH = 12 THEN 1 END) as DEC
FROM license_usage vlu
CROSS APPLY (SELECT MONTH(vlu.EndDate)) AS CA(Month)
WHERE vlu.[EndDate] >='2015-01-01'
AND vlu.[EndDate] < '2016-01-01'
GROUP BY vlu.[Userkey]
How can I get it to return my results?
Example:
http://sqlfiddle.com/#!3/be0b4/1

Got it by using distinct on the Count (*)
select umd.pbrUserkey,
max(case when mm = 1 then cnt else 0 end) as Jan,
max(case when mm = 2 then cnt else 0 end) as Feb,
max(case when mm = 3 then cnt else 0 end) as Mar,
max(case when mm = 4 then cnt else 0 end) as Apr,
max(case when mm = 5 then cnt else 0 end) as May
from (select vluk.pbrUserkey, month(vluk.EndDate) as mm, day(vluk.EndDate) as dd,
count(distinct vluk.idPackage) as cnt
from [license_usage] as vluk
where vluk.[EndDate] >= '2015-01-01' AND vluk.[EndDate] < '2016-01-01'
group by vluk.Userkey, month(vluk.EndDate), day(vluk.EndDate)
) umd
group by umd.Userkey;

If I understand correctly, you want the maximum by day usage per month for each user. The basic data you want is:
select UserKey, month(license_usage) as mm, day(license_usage) as dd,
count(distinct license) as cnt
from license_usage vlu
where vlu.EndDate] >= '2015-01-01' and vlu.EndDate < '2016-01-01'
group by UserKey, month(license_usage), day(license_usage);
Then you can pivot this in several ways, such as using conditional aggregation:
select UserKey,
max(case when mm = 1 then cnt else 0 end) as Jan,
. . .
from (select UserKey, month(license_usage) as mm, day(license_usage) as dd,
count(distinct license) as cnt
from license_usage vlu
where vlu.EndDate] >= '2015-01-01' AND vlu.EndDate < '2016-01-01'
group by UserKey, month(license_usage), day(license_usage)
) umd
group by UserKey;
CROSS APPLY is an interesting approach, but I can't think of a simpler way to get this information.

Related

SQL - Calculate Customer's Percentage of Total Orders by Month

I'm practicing SQL on this site: https://www.w3schools.com/sql/trysqlserver.asp?filename=trysql_func_sqlserver_substring
, and am trying to calculate the % of total monthly orders by customer ID. So for example, if customer 10 had 3 orders in January, and there were 33 orders total in January, then customer 10's result in January would be 3/33 = 9.09%. I want each row to be a customer ID, and a column for each month.
Basically, I want to convert this:
Into this:
I can get the totals by month, but am having trouble getting the percentages.
I'm using this code:
SELECT d.CustomerID,
SUM(CASE WHEN Month = 01 THEN NumOrders ELSE 0 END) AS Jan,
SUM(CASE WHEN Month = 02 THEN NumOrders ELSE 0 END) AS Feb,
SUM(CASE WHEN Month = 03 THEN NumOrders ELSE 0 END) AS Mar,
SUM(CASE WHEN Month = 04 THEN NumOrders ELSE 0 END) AS Apr,
SUM(CASE WHEN Month = 05 THEN NumOrders ELSE 0 END) AS May,
SUM(CASE WHEN Month = 06 THEN NumOrders ELSE 0 END) AS Jun,
SUM(CASE WHEN Month = 07 THEN NumOrders ELSE 0 END) AS Jul,
SUM(CASE WHEN Month = 08 THEN NumOrders ELSE 0 END) AS Aug,
SUM(CASE WHEN Month = 09 THEN NumOrders ELSE 0 END) AS Sep,
SUM(CASE WHEN Month = 10 THEN NumOrders ELSE 0 END) AS Oct,
SUM(CASE WHEN Month = 11 THEN NumOrders ELSE 0 END) AS Nov,
SUM(CASE WHEN Month = 12 THEN NumOrders ELSE 0 END) AS [Dec],
SUM(NumOrders) AS Total
FROM(
SELECT CustomerID,
DATEPART(mm,OrderDate) AS Month,
COUNT(OrderID) AS NumOrders
FROM Orders
GROUP BY CustomerID,
DATEPART(mm,OrderDate)
) d
GROUP BY d.CustomerID
WITH ROLLUP
I've tried using this code like this to calculate the percentages, but am not getting it to work out.
SUM(CASE WHEN Month = 01 THEN NumOrders ELSE 0 END) / CAST( SUM(NumOrders) OVER (PARTITION BY Month) AS FLOAT) AS JanPct,
This is pretty basic in Excel, and seems like it should be in SQL too, so I feel like I'm missing something obvious.
Try this
Create table #tmp (CustId INT, Jan int, Feb Int, March int)
insert into #tmp VALUES
(10,4,3,5),
(11,3,1,7),
(12,6,2,6),
(13,5,4,4);
Select * from #tmp
select CustId,
CEILING(CAST(Jan As FLOAT)/CAST(SUM(Jan) OVER() AS FLOAT)*100) As Jan,
CEILING(CAST(Feb As FLOAT)/CAST(SUM(Feb) OVER() AS FLOAT)*100) As Feb,
CEILING(CAST(March As FLOAT)/CAST(SUM(March) OVER() AS FLOAT)*100) As March
from #tmp
drop table #tmp
if you want % symbol, convert to varchar and append %
Eg:
CONVERT(VARCHAR(5),CEILING(CAST(Jan As FLOAT)/CAST(SUM(Jan) OVER() AS FLOAT)*100))+'%'
I wasn't able to make rollup work with PIVOT, so here is the long solution.
DECLARE #t table(OrderId INT identity(1,1), OrderDate date, CustomerID INT)
INSERT #t values('2017-01-01', 1),('2017-01-01', 1),('2017-02-01', 1),('2017-01-01', 2)
;WITH CTE as
(
SELECT DISTINCT
CAST(ROUND(count(*) over(partition by CustomerID, Month(OrderDate))*100./ count(*)
over(partition by month(OrderDate)), 0) as INT) Pct,
Month(OrderDate) Mon,
CustomerID
FROM #t
)
SELECT
CustomerID,
SUM(CASE WHEN Mon = 1 THEN Pct ELSE 0 END) AS Jan,
SUM(CASE WHEN Mon = 2 THEN Pct ELSE 0 END) AS Feb,
SUM(CASE WHEN Mon = 3 THEN Pct ELSE 0 END) AS Mar,
SUM(CASE WHEN Mon = 4 THEN Pct ELSE 0 END) AS Apr,
SUM(CASE WHEN Mon = 5 THEN Pct ELSE 0 END) AS May,
SUM(CASE WHEN Mon = 6 THEN Pct ELSE 0 END) AS Jun,
SUM(CASE WHEN Mon = 7 THEN Pct ELSE 0 END) AS Jul,
SUM(CASE WHEN Mon = 8 THEN Pct ELSE 0 END) AS Aug,
SUM(CASE WHEN Mon = 9 THEN Pct ELSE 0 END) AS Sep,
SUM(CASE WHEN Mon = 10 THEN Pct ELSE 0 END) AS Oct,
SUM(CASE WHEN Mon = 11 THEN Pct ELSE 0 END) AS Nov,
SUM(CASE WHEN Mon = 12 THEN Pct ELSE 0 END) AS [Dec]
FROM CTE
GROUP BY ROLLUP (CustomerID)
Just Use below code instead of selecting COUNT(OrderID) AS NumOrders in your below subquery
CONVERT(numeric(10,2), count(Orderid) * 100.0/ (select count(Orderid) from [Orders])) as NumOrders

Cohort Analysis in SQL while recounting users

I'm trying to create a cohort query using SQL.
Usually with cohort analysis we look at users and check if a user who performed a specific action at a specific time and count if that user performs the same action over time.
WITH by_week
AS (SELECT
user_id,
TD_DATE_TRUNC('week', login_time) AS login_week
FROM logins
GROUP BY 1, 2),
with_first_week
AS (SELECT
user_id,
login_week,
FIRST_VALUE(login_week) OVER (PARTITION BY user_id ORDER BY login_week) AS first_week
FROM by_week),
with_week_number
AS (SELECT
user_id,
login_week,
first_week,
(login_week - first_week) / (24 * 60 * 60 * 7) AS week_number
FROM with_first_week)
SELECT
TD_TIME_FORMAT(first_week, 'yyyy-MM-dd') AS first_week,
SUM(CASE WHEN week_number = 1 THEN 1 ELSE 0 END) AS week_1,
SUM(CASE WHEN week_number = 2 THEN 1 ELSE 0 END) AS week_2,
SUM(CASE WHEN week_number = 3 THEN 1 ELSE 0 END) AS week_3,
SUM(CASE WHEN week_number = 4 THEN 1 ELSE 0 END) AS week_4,
SUM(CASE WHEN week_number = 5 THEN 1 ELSE 0 END) AS week_5,
SUM(CASE WHEN week_number = 6 THEN 1 ELSE 0 END) AS week_6,
SUM(CASE WHEN week_number = 7 THEN 1 ELSE 0 END) AS week_7,
SUM(CASE WHEN week_number = 8 THEN 1 ELSE 0 END) AS week_8,
SUM(CASE WHEN week_number = 9 THEN 1 ELSE 0 END) AS week_9
FROM with_week_number
GROUP BY 1
ORDER BY 1
But let say now I don't care that much about first time/user-level analysis and I only want to see if my login action increases over time (i.e I want to add up logins of the first cohort during week 2 with logins of the second cohort in week 1). Is there a simple/elegant way to do this?
Edit:
Giving an example below
WeekStart Week1 Week2 Week 3
2017/05/03 66 **53** **49**
2017/05/10 (**53**+74) (**49**+70) **65**
2017/05/17 (**49**+ 70 + 45) (**65** + 80) etc.
I think you need to group by login_week instead of first_week so you count all logins during the given week in every row, not by cohort, and then you have to use >= instead of = so it will sum up this week's cohort with all older cohorts in any given row.
WITH
by_week AS (
SELECT
user_id,
TD_DATE_TRUNC('week', login_time) AS login_week
FROM logins
GROUP BY 1, 2
)
,with_first_week AS (
SELECT
user_id,
login_week,
FIRST_VALUE(login_week) OVER (PARTITION BY user_id ORDER BY login_week) AS first_week
FROM by_week
)
,with_week_number AS (
SELECT
user_id,
login_week,
first_week,
(login_week - first_week) / (24 * 60 * 60 * 7) AS week_number
FROM with_first_week
)
SELECT
TD_TIME_FORMAT(login_week, 'yyyy-MM-dd') AS login_week,
SUM(CASE WHEN week_number>= 1 THEN 1 ELSE 0 END) AS week_1,
SUM(CASE WHEN week_number>= 2 THEN 1 ELSE 0 END) AS week_2,
SUM(CASE WHEN week_number>= 3 THEN 1 ELSE 0 END) AS week_3,
SUM(CASE WHEN week_number>= 4 THEN 1 ELSE 0 END) AS week_4,
SUM(CASE WHEN week_number>= 5 THEN 1 ELSE 0 END) AS week_5,
SUM(CASE WHEN week_number>= 6 THEN 1 ELSE 0 END) AS week_6,
SUM(CASE WHEN week_number>= 7 THEN 1 ELSE 0 END) AS week_7,
SUM(CASE WHEN week_number>= 8 THEN 1 ELSE 0 END) AS week_8,
SUM(CASE WHEN week_number>= 9 THEN 1 ELSE 0 END) AS week_9
FROM with_week_number
GROUP BY 1
ORDER BY 1;

Is it possible to Count by diffrent condition in one query?

I have a Shipments table which basicly contains Shipments data with dates
id is integer
dateshipped is date
id dateshipped
1 1-JAN-16
2 1-JAN-16
3 3-FEB-16
4 9-FEB-16
I want to write a query which count all shipments based on Months.
What I should get is:
Jan Feb March....
2 2 0
I know I can do it by having query for each column, get only relevent rows for this specific month and just count them.
As follows:
Select (Select count(*)
from Shipments
Where EXTRACT(YEAR FROM dateshipped)::int=2016 and EXTRACT(MONTH FROM dateshipped)::int=1 )as JAN,
(Select count(*)
from Shipments
Where EXTRACT(YEAR FROM dateshipped)::int=2016 and EXTRACT(MONTH FROM dateshipped)::int=2 )as FEB
This works however its too much of the same code...
I am wondring if it is possible to do it with a single FROM statment and each column get it's own relevent rows for count.
Something like:
Select COL1,COL2,COL3...
from Shipments
Where EXTRACT(YEAR FROM dateshipped)::int=2016;
and have something like:
COL1 = count only JAN records
COL2 = count only FEB records
....
maybe there is something with Parations on months or any other solution?
You need a pivot query to accomplish this:
SELECT SUM(CASE WHEN EXTRACT(MONTH FROM dateshipped)::int = 1 THEN 1 ELSE 0 END) AS Jan,
SUM(CASE WHEN EXTRACT(MONTH FROM dateshipped)::int = 2 THEN 1 ELSE 0 END) AS Feb,
SUM(CASE WHEN EXTRACT(MONTH FROM dateshipped)::int = 3 THEN 1 ELSE 0 END) AS Mar,
SUM(CASE WHEN EXTRACT(MONTH FROM dateshipped)::int = 4 THEN 1 ELSE 0 END) AS Apr,
SUM(CASE WHEN EXTRACT(MONTH FROM dateshipped)::int = 5 THEN 1 ELSE 0 END) AS May,
SUM(CASE WHEN EXTRACT(MONTH FROM dateshipped)::int = 6 THEN 1 ELSE 0 END) AS Jun,
SUM(CASE WHEN EXTRACT(MONTH FROM dateshipped)::int = 7 THEN 1 ELSE 0 END) AS Jul,
SUM(CASE WHEN EXTRACT(MONTH FROM dateshipped)::int = 8 THEN 1 ELSE 0 END) AS Aug,
SUM(CASE WHEN EXTRACT(MONTH FROM dateshipped)::int = 9 THEN 1 ELSE 0 END) AS Sep,
SUM(CASE WHEN EXTRACT(MONTH FROM dateshipped)::int = 10 THEN 1 ELSE 0 END) AS Oct,
SUM(CASE WHEN EXTRACT(MONTH FROM dateshipped)::int = 11 THEN 1 ELSE 0 END) AS Nov,
SUM(CASE WHEN EXTRACT(MONTH FROM dateshipped)::int = 12 THEN 1 ELSE 0 END) AS Dec
FROM Shipments
WHERE EXTRACT(YEAR FROM dateshipped)::int=2016
Since 9.4 you can use FILTER
SELECT
count(*) AS total,
count(*) FILTER (WHERE Extract(MONTH FROM dateshipped)::int=1) AS JAN,
count(*) FILTER (WHERE Extract(MONTH FROM dateshipped)::int=2) AS FEB,
...
FROM Shipments
WHERE Extract(YEAR FROM dateshipped)::int=2016;
Try case with sum function:
Select
sum(case when extract(MONTH from dateshipped)=1 then 1 else 0 end) as jan,
sum(case when extract(MONTH from dateshipped)=2 then 1 else 0 end) as feb,
sum(case when extract(MONTH from dateshipped)=3 then 1 else 0 end) as march
.....
.....
from Shipments
Where EXTRACT(YEAR FROM dateshipped)::int=2016;

SQL Server 2005 GROUP BY and COUNT query for each month

I have a SQL Server 2005 table named Rentals:
RentalID
Book
Date
I want to return, using a query, for each book, how many rentals were in each month for a given year.
The results should look something like this:
+--------------------------------+-----+-----+-----+
| Book | Jan | Feb | Mar |
+--------------------------------+-----+-----+-----+
| Isaac Asimov - Foundation | 2 | 5 | 3 |
| H.G. Wells - War of the Worlds | 4 | 3 | 1 |
| Frank Herbert - Dune | 7 | 4 | 6 |
+--------------------------------+-----+-----+-----+
My query so far:
SELECT
Book,
(SELECT COUNT(*) FROM Rentals WHERE month(Date)=1 AND year(Date)=2011),
(SELECT COUNT(*) FROM Rentals WHERE month(Date)=2 AND year(Date)=2011),
(SELECT COUNT(*) FROM Rentals WHERE month(Date)=3 AND year(Date)=2011),
(SELECT COUNT(*) FROM Rentals WHERE month(Date)=4 AND year(Date)=2011),
(SELECT COUNT(*) FROM Rentals WHERE month(Date)=5 AND year(Date)=2011),
(SELECT COUNT(*) FROM Rentals WHERE month(Date)=6 AND year(Date)=2011),
(SELECT COUNT(*) FROM Rentals WHERE month(Date)=7 AND year(Date)=2011),
(SELECT COUNT(*) FROM Rentals WHERE month(Date)=8 AND year(Date)=2011),
(SELECT COUNT(*) FROM Rentals WHERE month(Date)=9 AND year(Date)=2011),
(SELECT COUNT(*) FROM Rentals WHERE month(Date)=10 AND year(Date)=2011),
(SELECT COUNT(*) FROM Rentals WHERE month(Date)=11 AND year(Date)=2011),
(SELECT COUNT(*) FROM Rentals WHERE month(Date)=12 AND year(Date)=2011)
FROM Rentals
GROUP BY Book
This can be written much simpler by using a CASE expression inside of an aggregate function. This process is called PIVOT:
select book,
sum(case when month(Date) = 1 then 1 else 0 end) Jan,
sum(case when month(Date) = 2 then 1 else 0 end) Feb,
sum(case when month(Date) = 3 then 1 else 0 end) Mar,
sum(case when month(Date) = 4 then 1 else 0 end) Apr,
sum(case when month(Date) = 5 then 1 else 0 end) May,
sum(case when month(Date) = 6 then 1 else 0 end) Jun,
sum(case when month(Date) = 7 then 1 else 0 end) Jul,
sum(case when month(Date) = 8 then 1 else 0 end) Aug,
sum(case when month(Date) = 9 then 1 else 0 end) Sep,
sum(case when month(Date) = 10 then 1 else 0 end) Oct,
sum(case when month(Date) = 11 then 1 else 0 end) Nov,
sum(case when month(Date) = 12 then 1 else 0 end) Dec
from Rentals
where year(date) = 2011
group by book;
See SQL Fiddle with Demo. Instead of querying the table multiple times for each column, you use conditional aggregation to get the count for each book during the month and year.
If you use pivot the code is much easier to maintain,
SELECT
BOOK,
[1] as Jan ,
[2] as Feb,
[3] as Mar,
[4] as Apr,
[5] as May,
[6] as Jun,
[7] as Jul,
[8] as Aug,
[9] as Sep,
[10] as Oct,
[11] as Nov,
[12] as Dec
FROM
(
SELECT
BOOK ,
DATEPART(MONTH,[DATE]) AS PER
FROM
Rentals
WHERE
DATEPART(YEAR,[DATE]) = 2014
) AS P PIVOT
(
COUNT(PER) FOR PER IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])
) AS DATA
Simple.

Count no of month appear in Single column in SQL

Here in one column dates are in random order. I want to build query where it will count all the months how many times it appears.
SQL Table :-
EMP_table_date
10/26/2014
10/26/2014
10/24/2014
11/26/2014
11/26/2014
11/23/2014
12/26/2014
12/26/2014
Expected Output : -
Oct | Nov | Dec
3 | 3 | 2
Note:
Only single column mentioned above
Using Oracle 11G Database
select count(case when extract(month from emp_table_date) = 1 then 1 end) as jan,
count(case when extract(month from emp_table_date) = 2 then 1 end) as feb,
count(case when extract(month from emp_table_date) = 3 then 1 end) as mar,
count(case when extract(month from emp_table_date) = 4 then 1 end) as april,
count(case when extract(month from emp_table_date) = 5 then 1 end) as may,
count(case when extract(month from emp_table_date) = 6 then 1 end) as jun,
count(case when extract(month from emp_table_date) = 7 then 1 end) as jul,
count(case when extract(month from emp_table_date) = 8 then 1 end) as aug,
count(case when extract(month from emp_table_date) = 9 then 1 end) as sep,
count(case when extract(month from emp_table_date) = 10 then 1 end) as oct,
count(case when extract(month from emp_table_date) = 11 then 1 end) as nov,
count(case when extract(month from emp_table_date) = 12 then 1 end) as december
from emp_table;
SELECT TRUNC(emp_table_date,'MON') as "MON",
COUNT(emp_table_date) as "CNT"
FROM emp_table
GROUP BY TRUNC(emp_table_date,'MON');
This will return the count of each month in rows. Use standard PIVOT to convert it into columns.