SELECT MAX (column1date) only if column1date<columndate2 - sql

I am looking for SSMS (t-SQL) query to return info from the max updated_at row when date in column 1 = [updated_at] is < date in column 2 = [date]. Sometimes candidate_id has more [updated_at] ids, I want the query to return the latest [updated_at] if [updated_at] < [date].
So far I have following, but it only return info for max id and date in column 1 < date in column 2 not when. It doesn't return info if max [updated_at] is > [date]. Hope this make sense
SELECT
[candidate_id],
[date],
[updated_at]
[updated_by_id],
[user_id],
[summary],
[id]
FROM tmpacl1
WHERE EXISTS (
SELECT tmpacl1.id
FROM (
SELECT [candidate_id],
[date],
[updated_at],
[updated_by_id],
[user_id],
[summary],
[id]
FROM TPacl
WHERE (
updated_at = (
SELECT MAX(updated_at) AS TPMax
FROM TPacl_1
WHERE (candidate_id = TPacl.candidate_id)
)
)
AND ( [updated_at] < [date])
) AS TPacl_2
WHERE (tmpacl1.id = id) AND tmpacl1.candidate_id = candidate_id)
Thank you in advance

Related

SQLite query to find datetime difference between multiple rows

Here are my two tables' structures in SQLite
CREATE TABLE user
(
id integer PRIMARY KEY,
name TEXT
);
CREATE TABLE attendanceTable
(
id Integer,
mydate datetime,
startJob boolean
);
if startJob is 1 it implies that the employee is starting the job and if startJob is 0 it means employee is stopping the job.
attendanceTable is sorted by mydate column
I want output as worked hour by individual employees.
Input of query can be two different dates e.g. 2021-08-20 and 2021-08-22
From which I want to know "How much each person has worked?"
Output should be:
[id, name, userWorkedTime]
[1, Alice, 09:00]
[2, Bob, 07:00]
12:00 to 16:00 + 22:00 to 03:00 = 9 hours
13:00 to 17:00 + 12:00 to 15:00 = 7 hours
Input of query 2021-08-20 and 2021-08-21 - output should be:
[id, name, userWorkedTime]
[1, Alice, 09:00]
[2, Bob, 04:00]
12:00 to 16:00 + 22:00 to 03:00 = 9 hours
13:00 to 17:00 = 4 hours
It may possible that Alice starts her job at 11 PM and end her job at 3 AM on next day[So working hour would be 4 hours]
I believe that the following will accomplish the results you desire:-
WITH
/* The date selection parameters - change as necessary */
cte_selection(selection_start,selection_end) AS (SELECT '2020-08-20','2020-08-22'),
/* Extract data per shift - aka combine start and end
note that extract is 1 day befor and 1 day after actual selection criteria
as previous/subsequent days may be relevant
*/
cte_part1(userid,name,periodstart,periodend,duration) AS
(
SELECT
user.id,
name,
strftime('%s',mydate),
strftime('%s',
(
SELECT mydate
FROM attendancetable
WHERE id = at.id
AND NOT startjob
AND mydate > at.mydate
ORDER BY mydate ASC
LIMIT 1
)
) AS endjob,
(strftime('%s',
(
SELECT mydate
FROM attendancetable
WHERE id = at.id
AND NOT startjob
AND mydate > at.mydate
ORDER BY mydate ASC
LIMIT 1
)
) - strftime('%s',at.mydate)) AS duration
FROM attendancetable AS at
JOIN user ON at.id = user.id
WHERE startjob
AND mydate
BETWEEN date
(
(SELECT selection_start FROM cte_selection)
,'-1 day'
)
AND date
(
(SELECT selection_end FROM cte_selection)
,'+1 day'
)
),
/* split times if period crosses a day*/
cte_part2(userid,name,periodstart,startdate,periodend,enddate,duration,startday_duration,nextday_duration) AS
(
SELECT
userid,
name,
periodstart,
date(periodstart,'unixepoch') AS startdate,
periodend,
date(periodend,'unixepoch') AS enddate,
duration,
CASE
WHEN date(periodstart,'unixepoch') = date(periodend,'unixepoch') THEN duration
ELSE strftime('%s',date(periodstart,'unixepoch')||'24:00:00') - periodstart
END AS startday_duration,
CASE
WHEN date(periodstart,'unixepoch') = date(periodend,'unixepoch') THEN 0
ELSE periodend - strftime('%s',date(periodend,'unixepoch')||'00:00:00')
END AS nextday_duration
FROM cte_part1
),
/* generate new rows for following days */
cte_part3(userid,name,periodstart,startdate,periodend,enddate,duration,startday_duration,nextday_duration) AS
(
SELECT
userid,
name,
strftime('%s',date(periodend,'unixepoch')||'00:00:00'),
date(periodend,'unixepoch'),
periodend,
enddate,
nextday_duration,
nextday_duration,
0
FROM cte_part2
WHERE nextday_duration
),
/* combine both sets */
cte_part4 AS (SELECT * FROM cte_part2 UNION ALL SELECT * FROM cte_part3)
/* Group the final data */
SELECT *,time(sum(startday_duration),'unixepoch') AS time_worked
FROM cte_part4
WHERE startdate BETWEEN (SELECT selection_start FROM cte_selection) AND (SELECT selection_end FROM cte_selection) GROUP BY userid
;
e.g. :-
and :-
Note All results with the exception of the time_worked are arbitrary values from the underlying data. However, userid and name will be correct as they would be the same for each group. The other values will be a value from the group.
you can easily apply changes to the final query to include or exclude columns.
The full testing SQL being :-
DROP TABLE IF EXISTS user;
CREATE TABLE IF NOT EXISTS user (id integer PRIMARY KEY,name TEXT);
DROP TABLE IF EXISTS attendanceTable ;
CREATE TABLE attendanceTable(id Integer,mydate datetime,startJob boolean);
INSERT INTO user VALUES (1,'Alice'),(2,'Bob');
INSERT INTO attendanceTable VALUES
(1,'2020-08-20 12:00:00',1),
(2,'2020-08-20 13:00:00',1),
(1,'2020-08-20 16:00:00',0),
(2,'2020-08-20 17:00:00',0),
(1,'2020-08-20 22:00:00',1),
(1,'2020-08-21 03:00:00',0),
(2,'2020-08-22 12:00:00',1),
(2,'2020-08-22 15:00:00',0)
;
WITH
/* The date selection parameters - change as necessary */
cte_selection(selection_start,selection_end) AS (SELECT '2020-08-20','2020-08-22'),
/* Extract data per shift - aka combine start and end
note that extract is 1 day befor and 1 day after actual selection criteria
as previous/subsequent days may be relevant
*/
cte_part1(userid,name,periodstart,periodend,duration) AS
(
SELECT
user.id,
name,
strftime('%s',mydate),
strftime('%s',
(
SELECT mydate
FROM attendancetable
WHERE id = at.id
AND NOT startjob
AND mydate > at.mydate
ORDER BY mydate ASC
LIMIT 1
)
) AS endjob,
(strftime('%s',
(
SELECT mydate
FROM attendancetable
WHERE id = at.id
AND NOT startjob
AND mydate > at.mydate
ORDER BY mydate ASC
LIMIT 1
)
) - strftime('%s',at.mydate)) AS duration
FROM attendancetable AS at
JOIN user ON at.id = user.id
WHERE startjob
AND mydate
BETWEEN date
(
(SELECT selection_start FROM cte_selection)
,'-1 day'
)
AND date
(
(SELECT selection_end FROM cte_selection)
,'+1 day'
)
),
/* split times if period crosses a day*/
cte_part2(userid,name,periodstart,startdate,periodend,enddate,duration,startday_duration,nextday_duration) AS
(
SELECT
userid,
name,
periodstart,
date(periodstart,'unixepoch') AS startdate,
periodend,
date(periodend,'unixepoch') AS enddate,
duration,
CASE
WHEN date(periodstart,'unixepoch') = date(periodend,'unixepoch') THEN duration
ELSE strftime('%s',date(periodstart,'unixepoch')||'24:00:00') - periodstart
END AS startday_duration,
CASE
WHEN date(periodstart,'unixepoch') = date(periodend,'unixepoch') THEN 0
ELSE periodend - strftime('%s',date(periodend,'unixepoch')||'00:00:00')
END AS nextday_duration
FROM cte_part1
),
/* generate new rows for following days */
cte_part3(userid,name,periodstart,startdate,periodend,enddate,duration,startday_duration,nextday_duration) AS
(
SELECT
userid,
name,
strftime('%s',date(periodend,'unixepoch')||'00:00:00'),
date(periodend,'unixepoch'),
periodend,
enddate,
nextday_duration,
nextday_duration,
0
FROM cte_part2
WHERE nextday_duration
),
/* combine both sets */
cte_part4 AS (SELECT * FROM cte_part2 UNION ALL SELECT * FROM cte_part3)
/* Group the final data */
SELECT *,time(sum(startday_duration),'unixepoch') AS time_worked
FROM cte_part4
WHERE startdate BETWEEN (SELECT selection_start FROM cte_selection) AND (SELECT selection_end FROM cte_selection) GROUP BY userid
;
DROP TABLE IF EXISTS user;
DROP TABLE IF EXISTS attendanceTable ;

Display two count from a single in two different column from a single table

I have a table where I record daily work of employees. I have a query where I display the current work for today for each employee and have another query where I display the total count of work for each employee.
I want to combine the 2 queries into a single one where I have a daily column and a cumulative column.
my query is below:
SELECT staff,
process_inprogress,
not_yet_completed
FROM (SELECT staff,
Count(number) AS Process_InProgress,
Count(team_name) AS Not_Yet_Completed
FROM dbo.empty_shell_workflow
WHERE ( end_date IS NULL )
AND ( process_name IS NOT NULL )
AND ( billing_amount IS NULL )
AND ( deletion IS NULL )
AND ( team_name = 'Team Vishma' )
AND ( CONVERT(DATE, start_date) = CONVERT(DATE, Getdate()) )
GROUP BY staff
UNION ALL
SELECT staff,
Count(number) AS Process_InProgress,
Count(team_name) AS Not_Yet_Completed
FROM dbo.empty_shell_workflow AS Empty_Shell_Workflow_1
WHERE ( team_name = 'Team Vishma' )
AND ( billing_amount IS NULL )
AND ( tag_number IS NULL )
AND ( initiator IS NOT NULL )
AND ( end_date IS NULL )
AND ( deletion IS NULL )
AND ( process_name IS NOT NULL )
GROUP BY staff) AS t
however it is being display only in a single column for both daily and cumulative
Below is how i want it to display
Staff Process_Progress(Daily) Not_YetCompleted(Cumulative)
A 2 5
B 0 1
C 6 8
however from the query above, the cumulative is being display in the daily column
Any idea, how can I modify the query?
you could try like below by using case when
with cte as
( SELECT staff,CONVERT(DATE, start_date) as date_of_month
Count(number) AS Process_InProgress
FROM dbo.empty_shell_workflow AS Empty_Shell_Workflow_1
WHERE ( team_name = 'Team Vishma' )
AND ( billing_amount IS NULL )
AND ( tag_number IS NULL )
AND ( initiator IS NOT NULL )
AND ( end_date IS NULL )
AND ( deletion IS NULL )
AND ( process_name IS NOT NULL )
GROUP BY staff,CONVERT(DATE, start_date)
) select staff, sum(case when date_of_month = CONVERT(DATE, Getdate()) then
Process_InProgress else 0 end) as Process_Progress_Daily,
sum(case when date_of_month != CONVERT(DATE, Getdate()) then
Process_InProgress else 0 end) as Not_YetCompleted
from cte
group by staff

SQL - counts for two days

I have a table like
CREATE TABLE [dbo].[Log](
[Id] [BIGINT] IDENTITY(1,1) NOT NULL,
[GuidId] [UNIQUEIDENTIFIER] NOT NULL,
[TableName] [VARCHAR](50) NULL,
[CreatedDate] [DATETIME] NULL,
[Operation] [VARCHAR](50) NULL,
[Status] [VARCHAR](50) NULL,
[UserId] [VARCHAR](50) NOT NULL)
Am trying to get query like
SELECT TOP 25 UserId, TableName, Operation, COUNT(1) Records
FROM dbo.Log
WHERE CreatedDate > GETDATE() - 1
AND Status='failed'
GROUP BY UserId, TableName,Operation
I need to add another column to have output of count that has GetDate() - 7 criteria too in the same select.
Share some thoughts
Something like this?
SELECT TOP 25
UserId
, TableName
, Operation
, SUM(CASE WHEN CreatedDate > GETDATE() - 1 THEN 1 ELSE 0 END) Records1Day
, COUNT(1) Records7Days
FROM dbo.Log
WHERE
CreatedDate > GETDATE() - 7
AND Status='failed'
GROUP BY
UserId
, TableName
, Operation
If I understand correctly, you want the most recent UserID fails that had a MAX(CreatedDate) > yesterday. The subquery dT, below does this using MAX(CreatedDate) for the user ID, and HAVING to filter. This is Ordered in descending faildate order. I subqueried because you added the condition that you want to know the date for 7 days prior to this, and the receiving query does this... using DATEADD to subtract 7 days from the [Most Recent UserID Failure] date.
SELECT dT.UserId
,dT.[Most Recent UserID Failure]
,DATEADD(DAY, -7, dT.[Most Recent UserID Failure]) AS [7 Days ago]
,(SELECT COUNT (*)
FROM #Log
WHERE UserId = dT.UserID
AND CreatedDate >= DATEADD(DAY, -7, CAST(dT.[Most Recent UserID Failure] as date))
AND CreatedDate <= dT.[Most Recent UserID Failure]
AND Status = 'failed'
) AS [Num Fails]
FROM (
SELECT TOP 25
UserID
,MAX(CreatedDate) AS [Most Recent UserID Failure]
FROM Log
WHERE Status = 'failed'
GROUP BY UserID
HAVING MAX(CreatedDate) > GETDATE() - 1
ORDER BY [Most Recent UserID Failure] DESC
) AS dT

DAX - Last Value

I have this table
I would like to create measurement get the last traded value for each day. E.g.
How the DAX query should look like?
You have to create two measures. One for the last time in each date and another to get the value for that date and time.
Last Time :=
CALCULATE(MAX([Time]),FILTER('Table',[Date]=MAX([Date])))
Last Traded Value =
CALCULATE (
MAX ( 'Table'[Traded Value] ),
FILTER ( 'Table', [Date] = MAX ( [Date] ) && [Last Time] = [Time] )
)
Then add Date column to rows and Last Time and Last Traded Value measures to Values pane in a pivot table.
Let me know if this helps.
For example:
DEFINE
VAR TableTMP =
ADDCOLUMNS ( 'Table', "DateTime", [Date] + [Time] )
EVALUATE
SUMMARIZE (
NATURALINNERJOIN (
TableTMP,
SUMMARIZE (
GROUPBY ( TableTMP, [Date], "DateTime", MAXX ( CURRENTGROUP (), [DateTime] ) ),
[DateTime]
)
),
[Date],
[Time],
[Traded Value]
)
With the new window functions in DAX, I think this can be simplified to
Last Traded Value =
CALCULATE (
MAX ( 'Table'[Traded Value] ),
INDEX (
1,
ORDERBY ( 'Table'[Time], DESC ),
PARTITIONBY ( 'Table'[Date] )
)
)

Group records only if it have intersected periods

I have table like this
declare #data table
(
id int not null,
groupid int not null,
startDate datetime not null,
endDate datetime not null
)
insert into #data values
(1, 1, '20150101', '20150131'),
(2, 1, '20150114', '20150131'),
(3, 1, '20150201', '20150228');
and my current selecting statement is:
select groupid, 'some data', min(id), count(*)
from #data
group by groupid
But now I need to group records if it have intersected periods
desired result:
1, 'some data', 1, 2
1, 'some data', 3, 1
Is someone know how to do this?
One method is to identify the beginning of each group -- because it doesn't overlap with the previous one. Then, count the number of these as a group identifier.
with overlaps as (
select id
from #data d
where not exists (select 1
from #data d2
where d.groupid = d2.groupid and
d.startDate >= d2.startDate and
d.startDate < d2.endDate
)
),
groups as (
select d.*,
count(o.id) over (partition by groupid
order by d.startDate) as grpnum
from #data d left join
overlaps o
on d.id = o.id
)
select groupid, min(id), count(*),
min(startDate) as startDate, max(endDate) as endDate
from groups
group by grpnum, groupid;
Notes: This is using cumulative counts, which are available in SQL Server 2012+. You can do something similar with a correlated subquery or apply in earlier versions.
Also, this query assumes that the start dates are unique. If they are not, the query can be tweaked, but the logic becomes a bit more complicated.