Count records by Grouping a period - sql

I have a table that stores per day if a user worked or if he was on vacation based on a value.
Example table, Value = 1 -> WorkDay, Value = 2 -> Vacation:
User | Day | Value
--------|------------|-------
user-1 | 2021-01-01 | 1
user-1 | 2021-01-02 | 1
user-1 | 2021-01-03 | 1
user-1 | 2021-01-04 | 1
user-1 | 2021-01-05 | 2
user-1 | 2021-01-06 | 2
...
I'll like to convert this table to this (Using the simple example above):
User | Year | Month | WorkDay | Vacation
--------|------|-------|---------|---------
user-1 | 2021 | 01 | 4 | 2
...
I tried using group by, subqueries and case, but the whole thing become a mess.
SELECT
YEAR(Day),
MONTH(DAY),
User,
...
From Table1
Group By YEAR(Day), MONTH(DAY), User

Just use conditional aggregation:
select year(day), month(day),
sum(case when value = 1 then 1 else 0 end) as workday,
sum(case when value = 2 then 1 else 0 end) as vacation
from table1
group by year(day), month(day)

You can use conditional aggregation as below:
select User,
year(day),
month(day),
sum(case when value = 1 then 1 else 0 end) as WorkDay,
sum(case when value = 2 then 1 else 0 end) as Vacation
from table1
group by User, year(day), month(day)
DB-Fiddle:
Schema and insert statements:
create table table1([User] varchar(50), Day date, Value int);
insert into table1 values('user-1' , '2021-01-01' , 1);
insert into table1 values('user-1' , '2021-01-02' , 1);
insert into table1 values('user-1' , '2021-01-03' , 1);
insert into table1 values('user-1' , '2021-01-04' , 1);
insert into table1 values('user-1' , '2021-01-05' , 2);
insert into table1 values('user-1' , '2021-01-06' , 2);
Query:
select [User] as "user",
year(day) as "year",
month(day) as "month",
sum(case when value = 1 then 1 else 0 end) as WorkDay,
sum(case when value = 2 then 1 else 0 end) as Vacation
from table1
group by [User], year(day), month(day)
GO
Output:
user
year
month
WorkDay
Vacation
user-1
2021
1
4
2
db<fiddle here

Related

SQL Count Rows GROUP BY Month Name

I have a table and it has the following structure:
DEVICE_ID | DATE | STATUS
------------------------------------------
1 | 2021/01/05 | accepted
2 | 2021/01/23 | success
3 | 2021/02/07 | success
4 | 2021/03/11 | accepted
5 | 2021/03/20 | unsuccess
6 | 2021/03/26 | success
I want to calculate no of records in 2021 by status and GROUP BY month name like this :
MONTH | ACCEPTED | SUCCESS | UNSUCCESS
------------------------------------------------
January | 1 | 1 | 0
February | 0 | 1 | 0
March | 1 | 1 | 1
April | 0 | 0 | 0
May | 0 | 0 | 0
June | 0 | 0 | 0
July | 0 | 0 | 0
August | 0 | 0 | 0
September | 0 | 0 | 0
October | 0 | 0 | 0
November | 0 | 0 | 0
December | 0 | 0 | 0
Please help me to solve this issue
Explanation - because you want the full month list you need to be able to have all 12 months somewhere in the data. Then you want the custom status columns pivoted to display as you asked.
You should next time at least or tell us what you tried. It helps us figure out how youre thinking about it and how we can help you get past whatever roadblocks youve encountered.
IF OBJECT_ID('TempDb..#tmp') IS NOT NULL DROP TABLE #tmp;
IF OBJECT_ID('TempDb..#tmp2') IS NOT NULL DROP TABLE #tmp2;
CREATE TABLE #tmp
(
Device_ID INT
, Date VARCHAR(12)
, Status VARCHAR(15)
)
;
CREATE TABLE #tmp2
(
MOnthName VARCHAR(25)
)
;
INSERT INTO #tmp2
(MonthName)
VALUES
('January'),
('February'),
('March'),
('April'),
('May'),
('June'),
('July'),
('August'),
('September'),
('October'),
('November'),
('December')
;
INSERT INTO #tmp
(
Device_ID
, Date
, Status
)
VALUES
(1,'2021/01/05','accepted'),
(2,'2021/01/23','success'),
(3,'2021/02/07','success'),
(4,'2021/03/11','accepted'),
(5,'2021/03/20','unsuccess'),
(6,'2021/03/26','success')
;
SELECT
MOnthName
, success
, accepted
, unsuccess
FROM
(
SELECT
tt.MonthName
, Status
FROM
#tmp2 tt
LEFT JOIN #tmp t ON tt.MOnthName = DATENAME(month, CAST(Date AS DATE))
GROUP BY
tt.MonthName
, Status
) AS SourceTable
PIVOT
(
COUNT(Status) FOR Status IN ([accepted], [success], [unsuccess])
) AS PivotTable
ORDER BY
CASE
WHEN MonthName ='January' THEN 1
WHEN MonthName ='February' THEN 2
WHEN MonthName ='March' THEN 3
WHEN MonthName ='April' THEN 4
WHEN MonthName ='May' THEN 5
WHEN MonthName ='June' THEN 6
WHEN MonthName ='July' THEN 7
WHEN MonthName ='August' THEN 8
WHEN MonthName ='September' THEN 9
WHEN MonthName ='October' THEN 10
WHEN MonthName ='November' THEN 11
WHEN MonthName ='December' THEN 12
END
create table yourtable(DEVICE_ID int, DATE date, STATUS varchar(50));
insert into yourtable values(1, '2021/01/05' , 'accepted');
insert into yourtable values(2, '2021/01/23' , 'success');
insert into yourtable values(3, '2021/02/07' , 'success');
insert into yourtable values(4, '2021/03/11' , 'accepted');
insert into yourtable values(5, '2021/03/20' , 'unsuccess');
insert into yourtable values(6, '2021/03/26' , 'success');
Query:
;WITH months(MonthNumber) AS
(
SELECT 0
UNION ALL
SELECT MonthNumber+1
FROM months
WHERE MonthNumber < 11
)
SELECT DATENAME(MONTH,DATEADD(MONTH,MonthNumber,'01-01-2021')) AS [month],
coalesce(sum(case when status='ACCEPTED' then 1 end),0) ACCEPTED,
coalesce(sum(case when status='SUCCESS' then 1 end),0) SUCCESS,
coalesce(sum(case when status='UNSUCCESS' then 1 end),0) UNSUCCESS
FROM months m left join yourtable y
on m.monthnumber=month(y.[date])-1
group by monthnumber
Output:
month
ACCEPTED
SUCCESS
UNSUCCESS
January
1
1
0
February
0
1
0
March
1
1
1
April
0
0
0
May
0
0
0
June
0
0
0
July
0
0
0
August
0
0
0
September
0
0
0
October
0
0
0
November
0
0
0
December
0
0
0
db<>fiddle here

Split column into multiple columns by criteria

I have a query like the following:
select
table.date,
table.shift,
sum(table.value)
from
db.table
where
table.date >= date '2020-01-01' and
table.filter = 'type'
group by
table.date,
table.shift
order by
table.date,
table.shift;
That returns data this way:
date | shift | sum(value)
-----------|-------|------------
2020-01-06 | 1 | 15
2020-01-06 | 3 | 12
2020-01-07 | 1 | 20
2020-01-07 | 2 | 38
2020-01-09 | 1 | 6
2020-01-09 | 2 | 22
2020-01-09 | 3 | 14
2020-01-10 | 1 | 17
2020-01-10 | 2 | 3
2020-01-10 | 3 | 10
I'm trying to get it like this but I don't know how:
date | 1 | 2 | 3
-----------|----|----|----
2020-01-06 | 15 | | 12
2020-01-07 | 20 | 38 |
2020-01-09 | 6 | 22 | 14
2020-01-10 | 17 | 3 | 10
No need for an addition subquery or CTE. You can pivot your dataset using conditional aggregation with slight modifications of your query: just remove shift from the group by clause, and then implement conditional logic in the sum()s:
select
date,
sum(case when shift = 1 then value end) shift1,
sum(case when shift = 2 then value end) shift2,
sum(case when shift = 3 then value end) shift3
from
db.table
where
date >= date '2020-01-01'
and filter = 'type'
group by date
order by date
Note:
there is no need to prefix the column names since a single table comes into play. I removed those
date is the name of datatype in Oracle, hence not a good choice for a column name
You can do conditional aggregation :
select t.date,
sum(case when t.shift = 1 then t.value else 0 end),
sum(case when t.shift = 2 then t.value else 0 end),
sum(case when t.shift = 3 then t.value else 0 end)
from db.table as t
where t.date >= date '2020-01-01' and
t.filter = 'type'
group by t.date;
You can use conditional aggregation
with cte as
(
select
table.date,
table.shift,
sum(table.value) as val
from
db.table
where
table.date >= date '2020-01-01' and
table.filter = 'type'
group by
table.date,
table.shift
order by
table.date,
table.shift
)
select date, max(case when shift=1 then val end) as 1,
max(case when shift=1 then val end) as 2,
max(case when shift=1 then val end) as 3
from cte
group by date
You can use PIVOT for this as follows:
SELECT
*
FROM ( SELECT
table.date,
table.shift,
table.value
from
db.table
where
table.date >= date '2020-01-01' and
table.FILTER = 'type' )
PIVOT
( SUM ( VALUE ) FOR SHIFT IN ( 1,2,3 ))
ORDER BY date;
Cheers!!

SQL intersect two timestamp pairs and group up by hours

I have A little problem.
I have A table lets call it "events" with columns like: type, (1 or 0) , timestamp start , timestamp end.
I want to group them by hours (60 minutes periods)
Into 4 columns each calculating
How many minutes per hour there was no either type 1 or type 0 event.
How many minutes per hour there was an event type 1 and in the same time there was not event of type 2.
How many minutes per hour there was an event type 2 and in the same time there was no event of type 1
How many minutes per hour there was an event 2 and event 1 at the same time.
Result should look like this:
hour 00 10 01 11
12 10 20 20 10
13 5 15 25 15
Each row should always sum to 60 minutes.
Is it possible to do it in SQL? I need it in vertica so I can use verticas functions too.
Interesting Question! Here is a query which gets you what you need. I mocked up the following table and some dummy data, and then showed the results from the query at the end. As you required - the totals always add up to 60 minutes within each hour.
SETUP:
create table public.time_event_test(event_timestamp timestamptz, event_type int);
insert into public.time_event_test(event_timestamp,event_type) select getutcdate() as event_timestamp, 1 as event_type;
insert into public.time_event_test(event_timestamp,event_type) select TIMESTAMPADD('minute',5,getutcdate()) as event_timestamp, 1 as event_type;
insert into public.time_event_test(event_timestamp,event_type) select TIMESTAMPADD('minute',1,getutcdate()) as event_timestamp, 1 as event_type;
insert into public.time_event_test(event_timestamp,event_type) select TIMESTAMPADD('minute',1,getutcdate()) as event_timestamp, 2 as event_type;
insert into public.time_event_test(event_timestamp,event_type) select TIMESTAMPADD('minute',3,getutcdate()) as event_timestamp, 2 as event_type;
insert into public.time_event_test(event_timestamp,event_type) select TIMESTAMPADD('minute',6,getutcdate()) as event_timestamp, 2 as event_type;
insert into public.time_event_test(event_timestamp,event_type) select TIMESTAMPADD('minute',90,getutcdate()) as event_timestamp, 2 as event_type;
QUERY:
select date_trunc('hour',dat) as hr
, 60 - sum(case when event_type1 = 1 or event_type2 = 1 then 1 else 0 end) as type_00
, sum(case when event_type1 = 0 and event_type2 = 1 then 1 else 0 end) as type_01
, sum(case when event_type1 = 1 and event_type2 = 0 then 1 else 0 end) as type_10
, sum(case when event_type1 = 1 and event_type2 = 1 then 1 else 0 end) as type_11
from (
select date_trunc('minute',event_timestamp) as dat
, max(case when event_type = 1 then 1 else 0 end) as event_type1
, max(case when event_type = 2 then 1 else 0 end) as event_type2
from public.time_event_test
group by 1
) x
group by 1 order by 1;
RESULTS:
hr | type_00 | type_01 | type_10 | type_11
------------------------+---------+---------+---------+---------
2016-12-21 01:00:00+00 | 52 | 3 | 2 | 3
2016-12-21 02:00:00+00 | 59 | 1 | 0 | 0

Count and pivot a table by date

I would like to identify the returning customers from an Oracle(11g) table like this:
CustID | Date
-------|----------
XC321 | 2016-04-28
AV626 | 2016-05-18
DX970 | 2016-06-23
XC321 | 2016-05-28
XC321 | 2016-06-02
So I can see which customers returned within various windows, for example within 10, 20, 30, 40 or 50 days. For example:
CustID | 10_day | 20_day | 30_day | 40_day | 50_day
-------|--------|--------|--------|--------|--------
XC321 | | | 1 | |
XC321 | | | | 1 |
I would even accept a result like this:
CustID | Date | days_from_last_visit
-------|------------|---------------------
XC321 | 2016-05-28 | 30
XC321 | 2016-06-02 | 5
I guess it would use a partition by windowing clause with unbounded following and preceding clauses... but I cannot find any suitable examples.
Any ideas...?
Thanks
No need for window functions here, you can simply do it with conditional aggregation using CASE EXPRESSION :
SELECT t.custID,
COUNT(CASE WHEN (last_visit- t.date) <= 10 THEN 1 END) as 10_day,
COUNT(CASE WHEN (last_visit- t.date) between 11 and 20 THEN 1 END) as 20_day,
COUNT(CASE WHEN (last_visit- t.date) between 21 and 30 THEN 1 END) as 30_day,
.....
FROM (SELECT s.custID,
LEAD(s.date) OVER(PARTITION BY s.custID ORDER BY s.date DESC) as last_visit
FROM YourTable s) t
GROUP BY t.custID
Oracle Setup:
CREATE TABLE customers ( CustID, Activity_Date ) AS
SELECT 'XC321', DATE '2016-04-28' FROM DUAL UNION ALL
SELECT 'AV626', DATE '2016-05-18' FROM DUAL UNION ALL
SELECT 'DX970', DATE '2016-06-23' FROM DUAL UNION ALL
SELECT 'XC321', DATE '2016-05-28' FROM DUAL UNION ALL
SELECT 'XC321', DATE '2016-06-02' FROM DUAL;
Query:
SELECT *
FROM (
SELECT CustID,
Activity_Date AS First_Date,
COUNT(1) OVER ( PARTITION BY CustID
ORDER BY Activity_Date
RANGE BETWEEN CURRENT ROW AND INTERVAL '10' DAY FOLLOWING )
- 1 AS "10_Day",
COUNT(1) OVER ( PARTITION BY CustID
ORDER BY Activity_Date
RANGE BETWEEN CURRENT ROW AND INTERVAL '20' DAY FOLLOWING )
- 1 AS "20_Day",
COUNT(1) OVER ( PARTITION BY CustID
ORDER BY Activity_Date
RANGE BETWEEN CURRENT ROW AND INTERVAL '30' DAY FOLLOWING )
- 1 AS "30_Day",
COUNT(1) OVER ( PARTITION BY CustID
ORDER BY Activity_Date
RANGE BETWEEN CURRENT ROW AND INTERVAL '40' DAY FOLLOWING )
- 1 AS "40_Day",
COUNT(1) OVER ( PARTITION BY CustID
ORDER BY Activity_Date
RANGE BETWEEN CURRENT ROW AND INTERVAL '50' DAY FOLLOWING )
- 1 AS "50_Day",
ROW_NUMBER() OVER ( PARTITION BY CustID ORDER BY Activity_Date ) AS rn
FROM Customers
)
WHERE rn = 1;
Output
USTID FIRST_DATE 10_Day 20_Day 30_Day 40_Day 50_Day RN
------ ------------------- ---------- ---------- ---------- ---------- ---------- ----------
AV626 2016-05-18 00:00:00 0 0 0 0 0 1
DX970 2016-06-23 00:00:00 0 0 0 0 0 1
XC321 2016-04-28 00:00:00 0 0 1 2 2 1
Here is an answer that works for me, I have based it on your answers above, thanks for contributions from MT0 and Sagi:
SELECT CustID,
visit_date,
Prev_Visit ,
COUNT( CASE WHEN (Days_between_visits) <=10 THEN 1 END) AS "0-10_day" ,
COUNT( CASE WHEN (Days_between_visits) BETWEEN 11 AND 20 THEN 1 END) AS "11-20_day" ,
COUNT( CASE WHEN (Days_between_visits) BETWEEN 21 AND 30 THEN 1 END) AS "21-30_day" ,
COUNT( CASE WHEN (Days_between_visits) BETWEEN 31 AND 40 THEN 1 END) AS "31-40_day" ,
COUNT( CASE WHEN (Days_between_visits) BETWEEN 41 AND 50 THEN 1 END) AS "41-50_day" ,
COUNT( CASE WHEN (Days_between_visits) >50 THEN 1 END) AS "51+_day"
FROM
(SELECT CustID,
visit_date,
Lead(T1.visit_date) over (partition BY T1.CustID order by T1.visit_date DESC) AS Prev_visit,
visit_date - Lead(T1.visit_date) over (
partition BY T1.CustID order by T1.visit_date DESC) AS Days_between_visits
FROM T1
) T2
WHERE Days_between_visits >0
GROUP BY T2.CustID ,
T2.visit_date ,
T2.Prev_visit ,
T2.Days_between_visits;
This returns:
CUSTID | VISIT_DATE | PREV_VISIT | DAYS_BETWEEN_VISIT | 0-10_DAY | 11-20_DAY | 21-30_DAY | 31-40_DAY | 41-50_DAY | 51+DAY
XC321 | 2016-05-28 | 2016-04-28 | 30 | | | 1 | | |
XC321 | 2016-06-02 | 2016-05-28 | 5 | 1 | | | | |

How to count sql from one column, and display it in two column

I have a table like this:
idrecord | date
----------------------------------------------
INC-20140308102029 | 2014-03-08 00:00:00.000
INC-20140308102840 | 2014-03-06 00:00:00.000
INC-20140310164404 | 2014-03-10 00:00:00.000
INC-20140311075714 | 2014-03-09 00:00:00.000
NRM-20140310130512 | 2014-04-02 00:00:00.000
NRM-20140311134720 | 2014-03-11 00:00:00.000
USF-20140317212232 | 2014-03-17 00:00:00.000
USF-20140321075402 | 2014-03-18 00:00:00.000
USF-20140321083137 | 2014-03-21 00:00:00.000
how to count this table and display result like this:
month | INC | NRM | USF
march | 4 | 1 | 3
April | 0 | 1 | 0
Thank you
You'd use case to count 1 or zero depending on the string matching or not. Use sum to count.
select
extract(month from thedate) as whichmonth,
sum( case when idrecord like 'INC%' then 1 else 0 end) as inc,
sum( case when idrecord like 'NRM%' then 1 else 0 end) as nrm,
sum( case when idrecord like 'USF%' then 1 else 0 end) as usf
from mytable
group by extract(month from thedate);
The function to extract the month from the date may vary from dbms to dbms. Look the appropriate function up in Google, if extract doesn't work for you.
Don't use the name date for a column. Date is a reserved word in SQL.
Try this
SELECT convert(char(3), date, 0) AS Month,
SUM(Case when LEFT(idrecord,3) = 'INC' then 1 else 0 end) as 'INC',
SUM(Case when LEFT(idrecord,3) = 'NRM' then 1 else 0 end) as 'NRM',
SUM(Case when LEFT(idrecord,3) = 'USF' then 1 else 0 end) as 'USF'
FROM Table1
Group By convert(char(3), date, 0)
Fiddle Demo
or:
SELECT datename(mm, date) AS Month,
SUM(Case when LEFT(idrecord,3) = 'INC' then 1 else 0 end) as 'INC',
SUM(Case when LEFT(idrecord,3) = 'NRM' then 1 else 0 end) as 'NRM',
SUM(Case when LEFT(idrecord,3) = 'USF' then 1 else 0 end) as 'USF'
FROM Table1
Group By datename(mm, date)
Fiddle Demo
Output:
month | INC | NRM | USF
march | 4 | 1 | 3
April | 0 | 1 | 0
try this one
select month (date) as month,
count( case when idrecord like 'INC%' then 1 else 0 end) as inc,
count( case when idrecord like 'NRM%' then 1 else 0 end) as nrm,
count( case when idrecord like 'USF%' then 1 else 0 end) as usf
from table
group by month;