SQL Count Rows GROUP BY Month Name - sql

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

Related

Count records by Grouping a period

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

SQL Gaps/Islands Question - Determine if someone has worked for X years without a Y days break

Working on problem for a company in Japan. The government has some rules such as... If you are on a work visa:
You cannot work for more than 3 years at a company without taking 30 days off
You cannot work for the same staffing company for more than 5 years without taking 6 months off
So we want to figure out if anyone will be violating either rule in the next 30/60/90 days.
Sample data (list of contracts):
if object_id('tempdb..#sampleDates') is not null drop table #sampleDates
create table #sampleDates (UserId int, CompanyID int, WorkPeriodStart datetime, WorkPeriodEnd datetime)
insert #sampleDates (UserId, CompanyID, WorkPeriodStart, WorkPeriodEnd) values (27809, 972, '2019-10-10', '2020-10-10')
insert #sampleDates (UserId, CompanyID, WorkPeriodStart, WorkPeriodEnd) values (27853, 484, '2019-10-10', '2020-10-10')
insert #sampleDates (UserId, CompanyID, WorkPeriodStart, WorkPeriodEnd) values (27856, 172, '2019-10-10', '2020-10-10')
insert #sampleDates (UserId, CompanyID, WorkPeriodStart, WorkPeriodEnd) values (27857, 1234, '2015-01-01', '2015-12-31')
insert #sampleDates (UserId, CompanyID, WorkPeriodStart, WorkPeriodEnd) values (27857, 1234, '2016-01-01', '2017-02-28')
insert #sampleDates (UserId, CompanyID, WorkPeriodStart, WorkPeriodEnd) values (27857, 1234, '2017-01-01', '2017-12-31')
insert #sampleDates (UserId, CompanyID, WorkPeriodStart, WorkPeriodEnd) values (27857, 1234, '2018-01-01', '2018-12-31')
insert #sampleDates (UserId, CompanyID, WorkPeriodStart, WorkPeriodEnd) values (27857, 1234, '2019-01-01', '2020-01-31')
insert #sampleDates (UserId, CompanyID, WorkPeriodStart, WorkPeriodEnd) values (27857, 1234, '2020-01-01', '2020-12-31')
insert #sampleDates (UserId, CompanyID, WorkPeriodStart, WorkPeriodEnd) values (27897, 179, '2019-10-10', '2020-10-10')
My first issue is possibly overlapping dates. I am close to a solution on that already, but until I know how to solve the Working X years/ Y Days off issue, I'm not sure what the output of my cte or temp table should look like.
I don't expect anyone to do the work for me, but I want to find an article that can tell me:
How can I determine if someone has taken any breaks in the time period, and for how long (gaps between date ranges)?
How can I figure if they will have worked for 3/5 years without a 30/180 days break in the next 30/60/90 days?
This seemed so simple until I started coding the procedure.
Thanks for any help in advance.
EDIT:
For what it's worth, here's my second working attempt at eliminating overlapping dates (first version used a dense_rank approach and it worked until I screwed something up, went with something simple):
;with CJ as (
select UserId, CompanyID, WorkPeriodStart, WorkPeriodEnd from #sampleDates c
)
select
c.CompanyID,
c.WorkPeriodStart,
min(t1.WorkPeriodEnd) as EndDate
from CJ c
inner join CJ t1 on c.WorkPeriodStart <= t1.WorkPeriodEnd and c.UserId = t1.UserId and c.CompanyID = t1.CompanyID
and not exists(select * from CJ t2 where t1.UserId = t2.UserId and t1.CompanyID = t2.CompanyID and t1.WorkPeriodEnd >= t2.WorkPeriodStart AND t1.WorkPeriodEnd < t2.WorkPeriodEnd)
where not exists(select * from CJ c2 where c.UserId = c2.UserId and c.CompanyID = c2.CompanyID and c.WorkPeriodStart > c2.WorkPeriodStart AND c.WorkPeriodStart <= c2.WorkPeriodEnd)
group by c.UserId, c.CompanyID, c.WorkPeriodStart
order by c.UserId, c.WorkPeriodStart
Disclaimer: This is an incomplete answer.
I can continue later, but this shows how to compute the islands. Then identifying the offender ones shouldn't be that complicated.
See augmented example. I added user 27897 that has three islands: 0, 1, and 2. See below:
create table t (UserId int, CompanyID int, WorkPeriodStart date, WorkPeriodEnd date);
insert t (UserId, CompanyID, WorkPeriodStart, WorkPeriodEnd) values
(27809, 972, '2019-10-10', '2020-10-10'),
(27853, 484, '2019-10-10', '2020-10-10'),
(27856, 172, '2019-10-10', '2020-10-10'),
(27857, 1234, '2015-01-01', '2015-12-31'),
(27857, 1234, '2016-01-01', '2017-02-28'),
(27857, 1234, '2017-01-01', '2017-12-31'),
(27857, 1234, '2018-01-01', '2018-12-31'),
(27857, 1234, '2019-01-01', '2020-01-31'),
(27857, 1234, '2020-01-01', '2020-12-31'),
(27897, 179, '2015-05-28', '2015-09-30'),
(27897, 179, '2017-03-11', '2017-04-30'),
(27897, 188, '2017-02-20', '2017-07-07'),
(27897, 179, '2019-10-10', '2020-10-10');
With this data, the query that computes the island for each row can look like:
select *,
sum(hop) over(partition by UserId order by WorkPeriodStart) as island
from (
select *,
case when WorkPeriodStart > dateadd(day, 1, max(WorkPeriodEnd)
over(partition by UserId
order by WorkPeriodStart
rows between unbounded preceding and 1 preceding))
then 1 else 0 end as hop
from t
) x
order by UserId, WorkPeriodStart
Result:
UserId CompanyID WorkPeriodStart WorkPeriodEnd hop island
------ --------- --------------- ------------- --- ------
27809 972 2019-10-10 2020-10-10 0 0
27853 484 2019-10-10 2020-10-10 0 0
27856 172 2019-10-10 2020-10-10 0 0
27857 1234 2015-01-01 2015-12-31 0 0
27857 1234 2016-01-01 2017-02-28 0 0
27857 1234 2017-01-01 2017-12-31 0 0
27857 1234 2018-01-01 2018-12-31 0 0
27857 1234 2019-01-01 2020-01-31 0 0
27857 1234 2020-01-01 2020-12-31 0 0
27897 179 2015-05-28 2015-09-30 0 0
27897 188 2017-02-20 2017-07-07 1 1
27897 179 2017-03-11 2017-04-30 0 1
27897 179 2019-10-10 2020-10-10 1 2
Now, we can augment this query to get the "worked days" for each island, and the "days off" before each island, by doing:
select *,
datediff(day, s, e) + 1 as worked,
datediff(day, lag(e) over(partition by UserId order by island), s) as prev_days_off
from (
select UserId, island, min(WorkPeriodStart) as s, max(WorkPeriodEnd) as e
from (
select *,
sum(hop) over(partition by UserId order by WorkPeriodStart) as island
from (
select *,
case when WorkPeriodStart > dateadd(day, 1, max(WorkPeriodEnd)
over(partition by UserId
order by WorkPeriodStart
rows between unbounded preceding and 1 preceding))
then 1 else 0 end as hop
from t
) x
) y
group by UserId, island
) x
order by UserId, island
Result:
UserId island s e worked prev_days_off
------ ------ ---------- ---------- ------ -------------
27809 0 2019-10-10 2020-10-10 367 <null>
27853 0 2019-10-10 2020-10-10 367 <null>
27856 0 2019-10-10 2020-10-10 367 <null>
27857 0 2015-01-01 2020-12-31 2192 <null>
27897 0 2015-05-28 2015-09-30 126 <null>
27897 1 2017-02-20 2017-07-07 138 509
27897 2 2019-10-10 2020-10-10 367 825
This result is much close to what you need. That data is actually useful to filter rows according to your criteria.
This script merges any overlapping work periods and then calculates the total days worked within the previous 3 and 5 year periods. Then takes this value and determines if this is more than the maximum working days allowed within that period by UserId and CompanyId for the 3 year limit, and just by UserId for the 5 year limit. (Is this a correct interpretation of the rules in your question?)
From this it then simply adds on 30, 60 and 90 days to that total, to see if that larger value would be over the respective limits. Given the different grouping rules, this would be cleaner as 2 queries (no duplication of UserId for 5 year rule) but the result is still a flag against any offending UserId.
In the example below you can see UserId = 27857 only violating the 5 year rule at present, but then also violating the 3 year rule should they stay on for another 60 days. In addition, UserId = 27858 is currently okay, but will violate the 5 year rule in 60 days.
I have made some assumptions about how you define a year and whether or not your WorkPeriodEnd values are inclusive or not, so do check that your required logic is properly applied.
Script
if object_id('tempdb..#sampleDates') is not null drop table #sampleDates
create table #sampleDates (UserId int, CompanyId int, WorkPeriodStart datetime, WorkPeriodEnd datetime)
insert #sampleDates values
(27809, 972, '2019-10-10', '2020-10-10')
,(27853, 484, '2019-10-10', '2020-10-10')
,(27856, 172, '2019-10-10', '2020-10-10')
,(27857, 1234, '2015-01-01', '2015-12-31')
,(27857, 1234, '2016-01-01', '2017-02-28')
,(27857, 1234, '2017-01-01', '2017-12-31')
,(27857, 1234, '2018-01-01', '2018-12-31')
,(27857, 1234, '2019-01-01', '2020-01-31')
,(27857, 1234, '2020-01-01', '2020-05-31')
,(27858, 1234, '2015-01-01', '2015-12-31')
,(27858, 1234, '2016-01-01', '2017-02-28')
,(27858, 1234, '2017-01-01', '2017-12-31')
,(27858, 1234, '2018-01-01', '2018-12-31')
,(27858, 1234, '2019-09-01', '2020-01-31')
,(27858, 1234, '2020-01-01', '2020-08-31')
,(27859, 12345, '2015-01-01', '2015-12-31')
,(27859, 12346, '2016-01-01', '2017-02-28')
,(27859, 12347, '2017-01-01', '2017-12-31')
,(27859, 12348, '2018-01-01', '2018-12-31')
,(27859, 12349, '2019-01-01', '2020-01-31')
,(27859, 12340, '2020-01-01', '2020-12-31')
,(27897, 179, '2019-10-10', '2020-10-10')
;
declare #3YearsAgo date = dateadd(year,-3,getdate());
declare #3YearWorkingDays int = (365*3)-30;
declare #5YearsAgo date = dateadd(year,-5,getdate());
declare #5YearWorkingDays int = (365*5)-(365/2);
with p as
(
select UserId
,CompanyId
,min(WorkPeriodStart) as WorkPeriodStart
,max(WorkPeriodEnd) as WorkPeriodEnd
from(select l.*,
sum(case when dateadd(day,1,l.PrevEnd) < l.WorkPeriodStart then 1 else 0 end) over (partition by l.UserId, l.CompanyId order by l.WorkPeriodStart rows unbounded preceding) as grp
from(select d.*,
lag(d.WorkPeriodEnd) over (partition by d.UserId, d.CompanyId order by d.WorkPeriodEnd) as PrevEnd
from #sampleDates as d
) as l
) as g
group by grp
,UserId
,CompanyId
)
,d as
(
select UserId
,CompanyId
,sum(case when #3YearsAgo < WorkPeriodEnd
then datediff(day
,case when #3YearsAgo between WorkPeriodStart and WorkPeriodEnd then #3YearsAgo else WorkPeriodStart end
,WorkPeriodEnd
)
else 0
end
) as WorkingDays3YearsToToday
,sum(case when #5YearsAgo < WorkPeriodEnd
then datediff(day
,case when #5YearsAgo between WorkPeriodStart and WorkPeriodEnd then #5YearsAgo else WorkPeriodStart end
,WorkPeriodEnd
)
else 0
end
) as WorkingDays5YearsToToday
from p
group by UserId
,CompanyId
)
select UserId
,CompanyId
,#3YearWorkingDays as Limit3Year
,#5YearWorkingDays as Limit5Year
,WorkingDays3YearsToToday
,WorkingDays5YearsToToday
,case when WorkingDays3YearsToToday > #3YearWorkingDays then 1 else 0 end as Violation3YearNow
,case when sum(WorkingDays5YearsToToday) over (partition by UserId) > #5YearWorkingDays then 1 else 0 end as Violation5YearNow
,case when WorkingDays3YearsToToday + 30 > #3YearWorkingDays then 1 else 0 end as Violation3Year30Day
,case when sum(WorkingDays5YearsToToday) over (partition by UserId) + 30 > #5YearWorkingDays then 1 else 0 end as Violation5Year30Day
,case when WorkingDays3YearsToToday + 60 > #3YearWorkingDays then 1 else 0 end as Violation3Year60Day
,case when sum(WorkingDays5YearsToToday) over (partition by UserId) + 60 > #5YearWorkingDays then 1 else 0 end as Violation5Year60Day
,case when WorkingDays3YearsToToday + 90 > #3YearWorkingDays then 1 else 0 end as Violation3Year90Day
,case when sum(WorkingDays5YearsToToday) over (partition by UserId) + 90 > #5YearWorkingDays then 1 else 0 end as Violation5Year90Day
from d
order by UserId
,CompanyId;
Output
+--------+-----------+------------+------------+--------------------------+--------------------------+-------------------+-------------------+---------------------+---------------------+---------------------+---------------------+---------------------+---------------------+
| UserId | CompanyId | Limit3Year | Limit5Year | WorkingDays3YearsToToday | WorkingDays5YearsToToday | Violation3YearNow | Violation5YearNow | Violation3Year30Day | Violation5Year30Day | Violation3Year60Day | Violation5Year60Day | Violation3Year90Day | Violation5Year90Day |
+--------+-----------+------------+------------+--------------------------+--------------------------+-------------------+-------------------+---------------------+---------------------+---------------------+---------------------+---------------------+---------------------+
| 27809 | 972 | 1065 | 1643 | 366 | 366 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 27853 | 484 | 1065 | 1643 | 366 | 366 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 27856 | 172 | 1065 | 1643 | 366 | 366 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 27857 | 1234 | 1065 | 1643 | 1029 | 1760 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 |
| 27858 | 1234 | 1065 | 1643 | 877 | 1608 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
| 27859 | 12340 | 1065 | 1643 | 365 | 365 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 |
| 27859 | 12345 | 1065 | 1643 | 0 | 147 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 |
| 27859 | 12346 | 1065 | 1643 | 0 | 424 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 |
| 27859 | 12347 | 1065 | 1643 | 147 | 364 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 |
| 27859 | 12348 | 1065 | 1643 | 364 | 364 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 |
| 27859 | 12349 | 1065 | 1643 | 395 | 395 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 |
| 27897 | 179 | 1065 | 1643 | 366 | 366 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+--------+-----------+------------+------------+--------------------------+--------------------------+-------------------+-------------------+---------------------+---------------------+---------------------+---------------------+---------------------+---------------------+
Here is what I ended up with.
<UselessExplanation>
The issues I kept facing were:
How can I handle any and all date range overlaps and determine just the days within the contract date ranges.
The client is STILL using SQL 2008, so I need some old(er) school tsql.
Ensure that the break times (times between contracts) is accurately calculated.
So I chose to come up with my own solution which is probably dumb, being that it needs to generate a record in memory for every Workday/Candidate combination. I do not see the contracts table going beyond the 5-10k record range. Only reason I'm going this direction.
I created a calendar table with every date in it from 1/1/1980 - 12/31/2050
I then left joined the contract ranges against the calendar table by CandidateId. These will be the dates worked.
Any dates in the calendar table that do not match a date within a contract range is a Break Day.
</UselessExplanation>
Calendar table
if object_id('CalendarTable') is not null drop table CalendarTable
go
create table CalendarTable (pk int identity, CalendarDate date )
declare #StartDate date = cast('1980-01-01' as date)
declare #EndDate date = cast('2050-12-31' as date)
while #StartDate <= #EndDate
begin
insert into CalendarTable ( CalendarDate ) values ( #StartDate )
set #StartDate = dateadd(dd, 1, #StartDate)
end
go
Query for 5 year violations (working 5 years without a 6 month cool off period)
declare #enddate date = dateadd(dd, 30, getdate())
declare #beginDate date = dateadd(dd, -180, dateadd(year, -5, getdate()))
select poss.CandidateId,
min(work.CalendarDate) as FirstWorkDate,
count(work.CandidateId) as workedDays,
sum(case when work.CandidateId is null then 1 else 0 end) as breakDays,
case when count(work.CandidateId) > (365*5) and sum(case when work.CandidateId is null then 1 else 0 end) < (365/2) then 1 else 0 end as Year5Violation,
case when count(work.CandidateId) > (365*5) and sum(case when work.CandidateId is null then 1 else 0 end) < (365/2) then DATEADD(year, 5, min(work.CalendarDate)) else null end as ViolationDate
from
(
select cand.CandidateId, cal.CalendarDate
from CalendarTable cal
join (select distinct c.CandidateId from contracts c where c.WorkPeriodStart is not null and c.WorkPeriodEnd is not null and c.Deleted = 0) cand on 1 = 1
where cal.CalendarDate between #beginDate and #enddate
) as poss
left join
(
select distinct c.CandidateId, cal.CalendarDate
from contracts c
join CalendarTable cal on cal.CalendarDate between c.WorkPeriodStart and c.WorkPeriodEnd
where c.WorkPeriodStart is not null and c.WorkPeriodEnd is not null and c.Deleted = 0
) as work on work.CandidateId = poss.CandidateId and work.CalendarDate = poss.CalendarDate
group by poss.CandidateId

How to make a query of the max month/year of a group of columns

I don't really know how to make a MySQL query where I get all the data(Columns) of the most present month + year.
Being a little bit more specific I need to group the columns (description, data1, data2, data3) and only get the one that has the highest month + year.
The table contains data like this one:
description data1 data2 data3 year month adddate
desc1 0 7 1 2019 5 2019-05-23
desc2 0 7 1 2019 5 2019-05-23
desc3 1 7 1 2019 5 2019-05-23
desc4 0 2 1 2018 12 2019-05-23
I've tried using max on the month, year and adddate.
select description, data1, data2, data3, max(year) as year, max(month) as month, max(adddate) as adddate
from tabledata
group by description, data1, data2, data3
But with this I'm getting that the max register is desc2 with the month 12 which is not correct since the month is desc2 is 6.
You need to get the max value of an expression like:
100 * year + month
or
12 * year + month
So do this:
select *
from tabledata
where 100 * year + month = (
select max(100 * year + month)
from tabledata
)
See the demo.
Results:
> description | data1 | data2 | data3 | year | month | adddate
> :---------- | ----: | ----: | ----: | ---: | ----: | :---------
> desc1 | 0 | 7 | 1 | 2019 | 5 | 23/05/2019
> desc2 | 0 | 7 | 1 | 2019 | 5 | 23/05/2019
> desc3 | 1 | 7 | 1 | 2019 | 5 | 23/05/2019
You need to find the max combination of year and month (MaxYearMonth) and then join to your table again to find all the rows that match that MaxYearMonth. Here is the solution for SQL Server...
IF OBJECT_ID('tempdb.dbo.#tabledata', 'U') IS NOT NULL DROP TABLE #tabledata;
CREATE TABLE #tabledata
(
description VARCHAR(10)
, data1 INT
, data2 INT
, data3 INT
, year INT
, month INT
, adddate DATE
);
INSERT INTO #tabledata VALUES ('desc1', 0, 7, 1, 2019, 5, '2019-05-23')
INSERT INTO #tabledata VALUES ('desc2', 0, 7, 1, 2019, 5, '2019-05-23')
INSERT INTO #tabledata VALUES ('desc3', 1, 7, 1, 2019, 5, '2019-05-23')
INSERT INTO #tabledata VALUES ('desc4', 0, 2, 1, 2018, 12, '2019-05-23')
select a.description, a.data1, a.data2, a.data3
from #tabledata a
inner join
(
select max(convert(char(4), year) + right('0' + convert(varchar(2), month), 2)) as [MaxYearMonth]
from #tabledata
) b on convert(char(4), a.year) + right('0' + convert(varchar(2), a.month), 2) = b.MaxYearMonth
Yields this...
description data1 data2 data3
----------- ----------- ----------- -----------
desc1 0 7 1
desc2 0 7 1
desc3 1 7 1
Click here to see it in action.
You mention "mysql" so if you are really using mySQL then the syntax will likely need to change a bit.

TSQL Check if Month and Year fields are expired

i have a table with Month and Year fields as integer, eg:
Month | Year
------------
10 | 17
------------
11 | 17
------------
12 | 17
------------
1 | 18
------------
(Year 17 is for 2017 and Year 18 is for 2018)
I want add into a query a calculated field for check if the date is expired
SELECT [Year], [Month],
CASE WHEN
([Year]+2000) < DATEPART(Year, GetDate()) OR
(([Year]+2000) = DATEPART(Year, GetDate()) AND [Month] < DATEPART(Month, GetDate()))
THEN 1 ELSE 0 END AS IsExpired
FROM test
the output is
Month | Year | IsExpired
------------------------
10 | 17 | 1
------------------------
11 | 17 | 1
------------------------
12 | 17 | 1
------------------------
1 | 18 | 1
------------------------
the expected output is (because current GetDate() is 2017-11-29):
Month | Year | IsExpired
------------------------
10 | 17 | 1
------------------------
11 | 17 | 0
------------------------
12 | 17 | 0
------------------------
1 | 18 | 0
------------------------
see live on http://sqlfiddle.com/#!6/8c807/2
what i'm doing wrong?
Convert your values to dates:
WITH IntDates AS (
SELECT *
FROM (VALUES (10,17),(11,17),(12,17),(1,18)) AS D ([Month], [Year])),
Dates AS(
SELECT *,
DATEADD(YEAR, [Year], DATEADD(MONTH, [Month], '20000101')) AS DateValue
FROM IntDates)
SELECT *,
CASE WHEN DateValue < GETDATE() THEN 1 ELSE 0 END AS Expired
FROM Dates;
If you were using the date datatype this becomes a lot simpler.
create table test2
(
ExpirationDate date
)
--have to do a bunch of string manipulation to turn this into viable dates.
--and this of course is after switching the columns posted in your sql fiddle.
insert test2
select convert(char(4), [Year] + 2000) + right('0' + convert(varchar(2), [Month]), 2) + '01'
from Test
select case when ExpirationDate < dateadd(month, datediff(month, 0, getdate()), 0) --get beginning of the current month
then 1 else 0 end
, ExpirationDate
from test2

Get collection of integers from counter

Which would be the best way to get the number of people hired on each day of the week for 7 years from a table People that has their entry_date with a day-month-year as 01-Jun-91.
For example:
2000 2001 2002 etc..
SUN 2 0 1
MON 0 0 2
Do I have to create a counter for each day of each year? Like Sun2000, Sun2001 etc?
You need to join each day of the week with your entry_date and pivot the results.
SQL Fiddle
Query:
with x(days) as (
select 'sunday' from dual union all
select 'monday' from dual union all
select 'tuesday' from dual union all
select 'wednesday' from dual union all
select 'thursday' from dual union all
select 'friday' from dual union all
select 'saturday' from dual
)
select * from (
select x.days,
extract(year from emp.entry_date) entry_year
from x left outer join emp
on x.days = to_char(emp.entry_date,'fmday')
)
pivot(count(entry_year)
for entry_year in (
2007,
2008,
2009,
2010,
2011,
2012
)
)
order by
case days when 'sunday' then 1
when'monday' then 2
when'tuesday' then 3
when'wednesday' then 4
when'thursday' then 5
when'friday' then 6
when'saturday' then 7
end
Results:
| DAYS | 2007 | 2008 | 2009 | 2010 | 2011 | 2012 |
|-----------|------|------|------|------|------|------|
| sunday | 0 | 0 | 0 | 0 | 0 | 0 |
| monday | 0 | 0 | 0 | 2 | 0 | 0 |
| tuesday | 0 | 0 | 0 | 0 | 1 | 0 |
| wednesday | 0 | 0 | 0 | 1 | 2 | 1 |
| thursday | 0 | 0 | 0 | 0 | 0 | 3 |
| friday | 0 | 0 | 0 | 0 | 0 | 0 |
| saturday | 0 | 0 | 0 | 0 | 0 | 0 |
You need to use group by on the year and entry_date to get the count of employees joined for each date.
For example:
Rem -- Assuming following table structure
create table People(id number, name varchar2(20), entry_date date);
Rem -- Following groups the results
select extract(year from entry_date) "Year", entry_date, count(id)
from People
where extract(year from entry_date) between 2008 and 2015
group by extract(year from entry_date), entry_date
order by extract(year from entry_date), entry_date;
Check this sqlfiddle to explore more.
Depending on the version of Oracle you're using (10g doesn't have the PIVOT function, for example), you might try something like the following conditional aggregation:
SELECT day_abbrev
, SUM(CASE WHEN year_num = 2000 THEN person_cnt ELSE 0 END) AS "2000"
, SUM(CASE WHEN year_num = 2001 THEN person_cnt ELSE 0 END) AS "2001"
, SUM(CASE WHEN year_num = 2002 THEN person_cnt ELSE 0 END) AS "2002"
, SUM(CASE WHEN year_num = 2003 THEN person_cnt ELSE 0 END) AS "2003"
, SUM(CASE WHEN year_num = 2004 THEN person_cnt ELSE 0 END) AS "2004"
, SUM(CASE WHEN year_num = 2005 THEN person_cnt ELSE 0 END) AS "2005"
, SUM(CASE WHEN year_num = 2006 THEN person_cnt ELSE 0 END) AS "2006"
FROM (
SELECT TO_CHAR(entry_date, 'DY') AS day_abbrev
, EXTRACT(YEAR FROM entry_date) AS year_num
, COUNT(*) AS person_cnt
FROM people
GROUP BY TO_CHAR(entry_date, 'DY'), EXTRACT(YEAR FROM entry_date)
) GROUP BY day_abbrev
ORDER BY TO_CHAR(NEXT_DAY(SYSDATE, day_abbrev), 'D');