Group by and getting min and date - sql

Declare #dCurrentDate Date = CONVERT (DATE, GETDATE())
Declare #dNextDate Date = DATEADD(month,1,CONVERT (DATE, GETDATE()))
I created a table which contains 10 employee Ids and name as #tbl
then i wrote a query to select min and max date of employee for next 30 days.
SELECT
c.fkEmployeeId as Employee,
e.FullName,c.calendarday
FROM
EmployeeCalendar c
JOIN Employee e
ON (e.pkEmployeeId=c.fkEmployeeId)
GROUP BY c.fkEmployeeId,
e.FullName,
c.calendarday
HAVING
c.CalendarDay BETWEEN #dCurrentDate AND #dNextDate
AND c.fkEmployeeId IN (
SELECT
EmployeeId
FROM
#tbl
)
i am getting result
Id | Name | Date
76 Muhammad 2014-03-05
76 Muhammad 2014-03-12
594 Khan 2014-02-14
but i need
Id | Name | min_date | max_date
76 muhammad 2014-03-05 2014-03-12
594 khan 2014-02-14 2014-02-14
how can i get this.

SELECT id, MIN(record_date), MAX(record_date) FROM table GROUP BY id

SELECT id,
MIN(record_date) AS min_record_date,
MAN(record_date) AS max_record_date
FROM table
GROUP BY id;

try this one....
DECLARE #dCurrentDate DATE = CONVERT (DATE, GETDATE ())
DECLARE #dNextDate
DATE = DATEADD (MONTH, 1, CONVERT (DATE, GETDATE ()))
SELECT
c.fkEmployeeId AS Employee,
e.FullName, MIN(c.calendarday),MAX(c.calendarday)
FROM
EmployeeCalendar c
JOIN Employee e
ON (e.pkEmployeeId = c.fkEmployeeId)
GROUP BY c.fkEmployeeId
HAVING c.CalendarDay BETWEEN #dCurrentDate
AND #dNextDate
AND c.fkEmployeeId IN
(SELECT
EmployeeId
FROM
#tbl)

Try this.
SELECT id,Name
MIN(record_date) AS min_date,
MAN(record_date) AS max_date
FROM table
GROUP BY id,Name
ORDER BY id

Related

SQL active users by month

I would like to know the number of active users by month, I use SQL Server 2017.
I have an AuditLog table like:
- UserID: int
- DateTime: datetime
- AuditType: int
UserID DateTime AuditType
------------------------------
1 2022-01-01 1
1 2022-01-15 4
1 2022-02-20 3
2 2022-01-10 8
2 2022-03-10 1
3 2022-03-20 1
If someone has at least one entry in a given month then he/she is treated as active.
I would like to have a result like:
Date Count
2022-01 2
2022-02 1
2022-03 2
I think you can combine the function Month(datetime) in the GROUP BY with the Count function SELECT COUNT(UserID)
SELECT (CAST(YEAR(C.DATE)AS CHAR(4))+'-'+CAST(MONTH(C.DATE)AS CHAR(2)))YEAR_MONTH,COUNT(C.USER_ID)CNTT
FROM AUDITLOG AS C
GROUP BY (CAST(YEAR(C.DATE)AS CHAR(4))+'-'+CAST(MONTH(C.DATE)AS CHAR(2)))
ORDER BY (CAST(YEAR(C.DATE)AS CHAR(4))+'-'+CAST(MONTH(C.DATE)AS CHAR(2)));
Here is solutions,
Select [Date],count(1) as Count From (
select Cast(cast(d.DateTime as date) as varchar(7)) as [Date],UserId
from AuditLog d
Group by Cast(cast(d.DateTime as date) as varchar(7)),UserId
) as q1 Group by [Date]
Order by 1
Hope, it will works.
GROUP DATE (Year and Month) either combine or separate and count distinct userId
SELECT CONVERT(VARCHAR(7), [DateTime], 126)[Date], COUNT(DISTINCT UserID)[Count]
FROM AuditLog
GROUP BY CONVERT(VARCHAR(7), [DateTime], 126)

How to Calculate Employment Experience with Special Case

I am trying to calculate the years of experience
Let's say someone has multiple employments as follow:
startdate enddate
2007-08-27 2008-05-09
2007-08-27 2008-05-09
2012-01-01 2018-07-31
2013-01-06 2019-03-25
What would be the correct sql syntax to just select startdate, enddate which will be later pluged in a function to calculate the number of days for each employment?
Here's my expected result:
1st row: startdate 8/27/2007, enddate 5/9/2008
2nd row: startdate 1/1/2012, enddate 3/25/2019
The second employment took place during the same time for the 1st employment so, it will not be counted. The fourth employment started before the third employment ended so we should use the startdate for the third employment and enddate for the fourth employment
Use Distinct to remove dplicated records. Then you can use this query:
Select totaldays / 365 from
(Select Sum(
DATEDIFF(day, srartdate, enddate )
) As totaldays
)
This query returns the dates without overlapping:
select
v.startdate startdate,
min(vv.enddate) enddate
from view_appemployment v
inner join view_appemployment vv
on v.startdate <= vv.enddate
and not exists (
select * from view_appemployment vvv
where vv.enddate >= vvv.startdate and vv.enddate < vvv.enddate
)
where not exists (
select * from view_appemployment vvvv
where v.startdate > vvvv.startdate and v.startdate <= vvvv.enddate
)
group by v.startdate
See the demo
Results:
startdate | enddate
------------------ | ------------------
27/08/2007 00:00:00 | 09/05/2008 00:00:00
01/01/2012 00:00:00 | 25/03/2019 00:00:00
This is a Gaps & Islands in Sequences problem, the following query should do what you want:
CREATE TABLE #emp (empid int, startdate date,enddate date)
INSERT INTO #emp VALUES
(1,'2007-08-27','2008-05-09'),
(1,'2007-08-27','2008-05-09'),
(1,'2012-01-01','2018-07-31'),
(1,'2013-01-06','2019-03-25')
;WITH starts AS (
SELECT em.*,
(CASE WHEN EXISTS (SELECT 1
FROM #emp em2
WHERE em2.EmpID = em.EmpID AND
em2.StartDate < em.StartDate AND
em2.EndDate >= em.StartDate
) THEN 0 ELSE 1 END) AS [IsStart]
FROM #emp em )
SELECT EmpID
,MIN(StartDate) AS [StartDate]
,MAX(EndDate) AS [EndDate]
FROM (SELECT s.*, SUM(isstart) OVER (PARTITION BY EmpID ORDER BY StartDate) AS [grp]
FROM starts s
) s
GROUP BY EmpID, grp
ORDER BY EmpID
Please try this:
SELECT concat(id," row: start date ",date_format(start_date,'%d/%m/%y')," end date ",date_format(end_date,'%d/%m/%y'))as dateinfo FROM `dates`

SQL Display custom records

I have two dates, example 01-Feb-2018 and 02-Feb-2018
I want to display some new users added in hour basis, like,
Date Total
---------------------------------------------
01-Feb-2018 12:00:00 AM | 25
01-Feb-2018 01:00:00 AM | 50
..................
.................
............
02-Feb-2018 11:00:00 PM | 45
I want the result like this in SQL.
Appreciate if you suggest an idea.
One relatively simple method:
select cast(date as date) as dte, datepart(hour, date) as hh, count(*)
from t
group by cast(date as date), datepart(hour, date)
order by dte, hh;
This doesn't do exactly what you want because it has two separate columns. You can actually combine them as:
select dateadd(hour, datepart(hour, date), cast(date as date)) as yyyymmddhh, count(*)
from t
group by cast(date as date), datepart(hour, date)
order by yyyymmddhh;
You can get the desired output like followng.
DECLARE #MyTable TABLE ([Date] DateTime, [Name] VARCHAR(100) )
INSERT INTO #MyTable
SELECT * FROM
( VALUES
('2018-02-07 15:20:27.487','A')
,('2018-02-07 15:22:27.487','B')
,('2018-02-07 14:22:27.487','C')
)T ([Date] , [Name] )
SELECT DateWithHour, COUNT(*) AS Total
FROM
(
SELECT convert(char(14),[Date],121)+'00:00' AS DateWithHour,[Name]
FROM #MyTable
) T
GROUP BY DateWithHour
ORDER BY DateWithHour
Following is the output
DateWithHour Total
------------------- ----------
2018-02-07 14:00:00 1
2018-02-07 15:00:00 2

How to duplicate data in sql with conditions

I havea table as table_A . table_A includes these columns
-CountryName
-Min_Date
-Max_Date
-Number
I want to duplicate data with seperating by months. For example
Argentina | 2015-01-04 | 2015-04-07 | 100
England | 2015-02-08 | 2015-03-11 | 90
I want to see a table as this (Monthly seperated)
Argentina | 01-2015 | 27 //(days to end of the min_date's month)
Argentina | 02-2015 | 29 //(days full month)
Argentina | 03-2015 | 31 //(days full month)
Argentina | 04-2015 | 7 //(days from start of the max_date's month)
England | 02-2015 | 21 //(days)
England | 03-2015 | 11 //(days)
I tried too much thing to made this for each records. But now my brain is so confusing and my project is delaying.
Does anybody know how can i solve this. I tried to duplicate each rows with datediff count but it is not working
WITH cte AS (
SELECT CountryName, ISNULL(DATEDIFF(M,Min_Date ,Max_Date )+1,1) as count FROM table_A
UNION ALL
SELECT CountryName, count-1 FROM cte WHERE count>1
)
SELECT CountryName,count FROM cte
-Generate all the dates between min and max dates for each country.
-Then get the month start and month end dates for each country,year,month.
-Finally get the date differences of the month start and month end.
WITH cte AS (
SELECT Country, min_date dt,min_date,max_date FROM t
UNION ALL
SELECT Country, dateadd(dd,1,dt),min_date,max_date FROM cte WHERE dt < max_date
)
,monthends as (
SELECT country,year(dt) yr,month(dt) mth,max(dt) monthend,min(dt) monthstart
FROM cte
GROUP BY country,year(dt),month(dt))
select country
,cast(mth as varchar(2))+'-'+cast(yr as varchar(4)) yr_month
,datediff(dd,monthstart,monthend)+1 days_diff
from monthends
Sample Demo
EDIT: Another option would be to generate all the dates once (the example shown here generates 51 years of dates from 2000 to 2050) and then joining it to the table to get the days by month.
WITH cte AS (
SELECT cast('2000-01-01' as date) dt,cast('2050-12-31' as date) maxdt
UNION ALL
SELECT dateadd(dd,1,dt),maxdt FROM cte WHERE dt < maxdt
)
SELECT country,year(dt) yr,month(dt) mth, datediff(dd,min(dt),max(dt))+1 days_diff
FROM cte c
JOIN t on c.dt BETWEEN t.min_date and t.max_date
GROUP BY country,year(dt),month(dt)
OPTION (MAXRECURSION 0)
I think you have the right idea. But you need to construct the months:
WITH cte AS (
SELECT CountryName, Min_Date as dte, Min_Date, Max_Date
FROM table_A
UNION ALL
SELECT CountryName, DATEADD(month, 1, dte), Min_Date, Max_Date
FROM cte
WHERE dte < Max_date
)
SELECT CountryName, dte
FROM cte;
Getting the number of days in the month is a bit more complicated. That requires some thought.
Oh, I forgot about EOMONTH():
select countryName, dte,
(case when dte = min_date
then datediff(day, min_date, eomonth(dte)) + 1
when dte = max_date
then day(dte)
else day(eomonth(dte))
end) as days
from cte;
Using a Calendar Table makes this stuff pretty easy. RexTester: http://rextester.com/EBTIMG23993
begin
create table #enderaric (
CountryName varchar(16)
, Min_Date date
, Max_Date date
, Number int
)
insert into #enderaric values
('Argentina' ,'2015-01-04' ,'2015-04-07' ,'100')
, ('England' ,'2015-02-08' ,'2015-03-11' ,'90')
end;
-- select * from #enderaric
--*/"
declare #FromDate date;
declare #ThruDate date;
set #FromDate = '2015-01-01';
set #ThruDate = '2015-12-31';
with x as (
select top (cast(sqrt(datediff(day, #FromDate, #ThruDate)) as int) + 1)
[number]
from [master]..spt_values v
)
/* Date Range CTE */
,cal as (
select top (1+datediff(day, #FromDate, #ThruDate))
DateValue = convert(date,dateadd(day,
row_number() over (order by x.number)-1,#FromDate)
)
from x cross join x as y
order by DateValue
)
select
e.CountryName
, YearMonth = convert(char(7),left(convert(varchar(10),DateValue),7))
, [Days]=count(c.DateValue)
from #enderaric as e
inner join cal c on c.DateValue >= e.min_date
and c.DateValue <= e.max_date
group by
e.CountryName
, e.Min_Date
, e.Max_Date
, e.Number
, convert(char(7),left(convert(varchar(10),DateValue),7))
results in:
CountryName YearMonth Days
---------------- --------- -----------
Argentina 2015-01 28
Argentina 2015-02 28
Argentina 2015-03 31
Argentina 2015-04 7
England 2015-02 21
England 2015-03 11
More about calendar tables:
Aaron Bertrand - Generate a set or sequence without loops
generate-a-set-1
generate-a-set-2
generate-a-set-3
David Stein - Creating a Date Table/Dimension on SQL 2008
Michael Valentine Jones - F_TABLE_DATE

Ignore date list from CTE

CTE gives a below result
Name | StartDateTime | EndDateTime
--------------------+-------------------------+------------------------
Hair Massage | 2014-02-15 09:00:00.000 | 2014-02-15 10:00:00.000
Hair Massage | 2014-02-15 10:00:00.000 | 2014-02-15 11:00:00.000
(X)Hair Massage | 2014-02-23 09:00:00.000 | 2014-02-23 10:00:00.000
(X)Hair Cut | 2014-02-20 12:15:00.000 | 2014-02-20 13:00:00.000
Hair Cut | 2014-03-07 11:30:00.000 | 2014-03-07 12:15:00.000
Also I have Holidays
Id | StartDateTime | EndDateTime
-------------+--------------------+-------------------
1 | 20140223 00:00:00 | 20140224 23:59:00
And EventBooking
EventId | StartDateTime | EndDateTime
-------------+-------------------------+------------------------
1 | 2014-02-20 12:15:00.000 | 2014-02-20 13:00:00.000
I want to remove the dates falls under holidays and EventBooking from my CTE.
I mean remove the (X) recods from my CTE
RESULT=CTE- BookedSchedule-Holidays
with HoliDaysCte2 as
(
select StartdateTime,EndDateTime from Holidays
union all
select StartdateTime,EndDateTime from EventBooking
)
SELECT
Name,
StartDateTime,
EndDateTime
FROM CTE WHERE not exists (select 1
from HoliDaysCte2 h
where cast(a.RepeatEventDate as DATETIME) between
cast(h.startdatetime as DATETIME)
and cast(h.enddatetime as DATETIME)
)
Here is my SQL FIDDLE DEMO
Okay Assuming this is your schema
CREATE TABLE dbo.StaffSchedule
( ID INT IDENTITY(1, 1) NOT NULL,
Name Varchar(50),
StartdateTime DATETIME2 NOT NULL,
EndDateTime DATETIME2 NOT NULL
);
CREATE TABLE dbo.BookedSchedules
( ID INT IDENTITY(1, 1) NOT NULL,
StaffId INT,
StartdateTime DATETIME2 NOT NULL,
EndDateTime DATETIME2 NOT NULL
);
CREATE TABLE dbo.Holidays
( ID INT,
StartdateTime DATETIME2 NOT NULL,
EndDateTime DATETIME2 NOT NULL
);
INSERT dbo.StaffSchedule (Name, StartdateTime, EndDateTime)
VALUES
('Hair Massage','2014-02-15 09:00:00.000','2014-02-15 10:00:00.000'),
('Hair Massage','2014-02-15 10:00:00.000','2014-02-15 11:00:00.000'),
('(X)Hair Massage','2014-02-23 09:00:00.000','2014-02-23 10:00:00.000'),
('(X)Hair Cut','2014-02-20 12:15:00.000','2014-02-20 13:00:00.000'),
('Hair Cut','2014-03-07 11:30:00.000','2014-03-07 12:15:00.000');
INSERT dbo.BookedSchedules (StaffId, StartdateTime, EndDateTime)
VALUES
(1,'2014-02-20 12:15:00.000','2014-02-20 13:00:00.000');
INSERT dbo.Holidays (ID,StartdateTime, EndDateTime)
VALUES
(1,'20140223 00:00:00','20140224 23:59:00');
Does this solves your issue?
select * from StaffSchedule SS
where
not exists(
select * from NonBookingSlots NBS
where (dateadd(MICROSECOND,1,ss.StartdateTime)
between nbs.StartdateTime and nbs.EndDateTime)
or (dateadd(MICROSECOND,-1,ss.EndDateTime)
between nbs.StartdateTime and nbs.EndDateTime))
ok try this,
create one more cte,
,cte2 as
(
select * from #Holidays
union all
select BookingID,StartdateTime,EndDateTime from #EventBooking
)
then as usual
AND not exists (select 1
from cte2 h
where cast(a.RepeatEventDate as date) between cast(h.startdatetime as date) and cast(h.enddatetime as date)
)
this one is latest (datetime conversion very confusing,i just started
from #Gordon query.
AND not exists (select 1
from cte2 h
where cast(DATEADD(SECOND, DATEDIFF(SECOND, 0, StartTime), RepeatEventDate) as datetime) between cast(h.startdatetime as datetime) and cast(h.enddatetime as datetime)
)
RESULT= CTE - BookedSchedule - Holidays
Will be equal to use set theories subtract operation, in sql server you may use Except (Minus in Oracle).
select StaffId, StartdateTime,EndDateTime from StaffSchedule -- CTE
except
(select StaffId, StartdateTime,EndDateTime from BookedSchedules) -- BookedSchedule
except
(select StaffSchedule.StaffId, StaffSchedule.StartdateTime , StaffSchedule.EndDateTime
from StaffSchedule
inner join Holidays
on
cast(Holidays.StartdateTime As Date) = cast(StaffSchedule.StartdateTime As Date)
and
cast(Holidays.EndDateTime As Date) = cast(StaffSchedule.EndDateTime As Date)
) -- Holidays
;
Sqlfiddle demo
If a multi-day holiday could be inserted, like:
INSERT dbo.Holidays (StartdateTime, EndDateTime)
VALUES
('2014-03-05 00:00:00.000', '2014-03-07 23:59:00.000');
Using the query bellow to extract staff-holidays will be useful:
(select StaffSchedule.StaffId, StaffSchedule.StartdateTime , StaffSchedule.EndDateTime
from StaffSchedule
inner join Holidays
on
cast(Holidays.StartdateTime As Date) <= cast(StaffSchedule.StartdateTime As Date)
and
cast(Holidays.EndDateTime As Date) >= cast(StaffSchedule.EndDateTime As Date)
)
Please try:
select * From StaffSchedule
where ID not in(
select
ID
From StaffSchedule a inner join
(
select StartdateTime, EndDateTime From dbo.BookedSchedules
union all
select StartdateTime, EndDateTime From dbo.Holidays
)b on a.StartdateTime between b.StartdateTime and b.EndDateTime and
a.EndDateTime between b.StartdateTime and b.EndDateTime)
Chekck SQL Fiddle Demo
This will surely help you.....
WITH CTE AS (
SELECT
S.ID,
S.StaffId ,
S.StartdateTime,
S.EndDateTime,
H.StartdateTime 'HolydayStartDate' ,
H.EndDateTime AS 'HolydayDateDate',
B.StartdateTime AS 'BookedStartDate',
B.EndDateTime AS 'BookedEndDate'
FROM #StaffSchedule S
LEFT JOIN #Holidays H ON S.StartdateTime >= H.StartdateTime AND S.EndDateTime <= H.EndDateTime
LEFT JOIN #BookedSchedules B ON B.StaffId = S.StaffId AND B.StartdateTime = S.StartdateTime AND B.EndDateTime = S.EndDateTime
)
SELECT * FROM CTE
WHERE
HolydayStartDate IS NULL AND
HolydayDateDate IS NULL AND
BookedStartDate IS NULL AND
BookedEndDate IS NULL
To check for overlapping periods you need to do:
where p1.StartdateTime < p2.enddatetime
and p1.enddatetime > p2.startdatetime
Depending on your needs it might be >=/<= instead of >/<.
Based on your fiddle:
with NonBookingSlots as
(
select StartdateTime,EndDateTime from Holidays
union all
select StartdateTime,EndDateTime from BookedSchedules
)
SELECT
*
FROM StaffSchedule as ss
WHERE StaffId=1
AND not exists (select *
from NonBookingSlots h
where h.StartdateTime < ss.enddatetime
and h.enddatetime > ss.startdatetime
)
I think this will work for you -
SELECT ss.* FROM StaffSchedule ss
LEFT JOIN BookedSchedules bs
ON (ss.StartdateTime BETWEEN bs.StartdateTime AND bs.EndDateTime)
AND (ss.EndDateTime BETWEEN bs.StartdateTime AND bs.EndDateTime)
LEFT JOIN Holidays h
ON (ss.StartdateTime BETWEEN h.StartdateTime AND h.EndDateTime)
AND (ss.EndDateTime BETWEEN h.StartdateTime AND h.EndDateTime)
WHERE bs.ID IS NULL AND h.StartdateTime IS NULL AND h.EndDateTime IS NULL
SQL Fiddle