I need to create a SQL query that populates a table when someone has been of 3 times or more in a 3 month period.
The data I have available to me is :
SELECT 'John Doe' AS Name, 1406 AS InstanceID, '2016-01-08 00:00:00.000' AS AbsenceStart, '2016-01-13 00:00:00.000' AS AbsenceEnd, 4 AS NoOfDays, 1 AS rnk
UNION ALL
SELECT 'John Doe' AS Name, 1493 AS InstanceID, '2016-02-02 00:00:00.000' AS AbsenceStart, '2016-02-05 00:00:00.000' AS AbsenceEnd, 4 AS NoOfDays, 2 AS rnk
UNION ALL
SELECT 'John Doe' AS Name, 1536 AS InstanceID, '2016-02-19 00:00:00.000' AS AbsenceStart, '2016-02-22 00:00:00.000' AS AbsenceEnd, 2 AS NoOfDays, 3 AS rnk
UNION ALL
SELECT 'John Doe' AS Name, 1547 AS InstanceID, '2016-02-26 00:00:00.000' AS AbsenceStart, '2016-03-10 00:00:00.000' AS AbsenceEnd, 10 AS NoOfDays, 4 AS rnk
UNION ALL
SELECT 'John Doe' AS Name, 1660 AS InstanceID, '2016-04-04 00:00:00.000' AS AbsenceStart, '2016-04-04 00:00:00.000' AS AbsenceEnd, 0.5 AS NoOfDays, 5 AS rnk
UNION ALL
SELECT 'John Doe' AS Name, 1666 AS InstanceID, '2016-04-07 00:00:00.000' AS AbsenceStart, '2016-04-14 00:00:00.000' AS AbsenceEnd, 6 AS NoOfDays, 6 AS rnk
UNION ALL
SELECT 'John Doe' AS Name, 1698 AS InstanceID, '2016-04-27 00:00:00.000' AS AbsenceStart, '2016-04-28 00:00:00.000' AS AbsenceEnd, 1 AS NoOfDays, 7 AS rnk
Which gives the below output.
I need to design a query that will flag when there are more than 3 instances within a 90 day period. So with the above date 1406,1493,1536,1547,1660,1666 would all flag up (historically). Ideally the query will run daily and set the alert as soon as a 3rd instance is logged. I’ve tried various DATEDIFF’s and derived queries but can’t seem to get it to work.
So the desired output would be the above table but limited to those that fall within the date range of the first absence start + 90 days. I know I’m missing something simple!
You can do this with a loop, see my code below (commented). I put your initial data into #temp:
if object_id('tempdb..#temp') is not null
drop table #temp
SELECT 'John Doe' AS Name, 1406 AS InstanceID, '2016-01-08 00:00:00.000' AS
AbsenceStart, '2016-01-13 00:00:00.000' AS AbsenceEnd, 4 AS NoOfDays, 1 AS rnk
into #temp
UNION ALL
SELECT 'John Doe' AS Name, 1493 AS InstanceID, '2016-02-02 00:00:00.000' AS AbsenceStart, '2016-02-05 00:00:00.000' AS AbsenceEnd, 4 AS NoOfDays, 2 AS rnk
UNION ALL
SELECT 'John Doe' AS Name, 1536 AS InstanceID, '2016-02-19 00:00:00.000' AS AbsenceStart, '2016-02-22 00:00:00.000' AS AbsenceEnd, 2 AS NoOfDays, 3 AS rnk
UNION ALL
SELECT 'John Doe' AS Name, 1547 AS InstanceID, '2016-02-26 00:00:00.000' AS AbsenceStart, '2016-03-10 00:00:00.000' AS AbsenceEnd, 10 AS NoOfDays, 4 AS rnk
UNION ALL
SELECT 'John Doe' AS Name, 1660 AS InstanceID, '2016-04-04 00:00:00.000' AS AbsenceStart, '2016-04-04 00:00:00.000' AS AbsenceEnd, 0.5 AS NoOfDays, 5 AS rnk
UNION ALL
SELECT 'John Doe' AS Name, 1666 AS InstanceID, '2016-04-07 00:00:00.000' AS AbsenceStart, '2016-04-14 00:00:00.000' AS AbsenceEnd, 6 AS NoOfDays, 6 AS rnk
UNION ALL
SELECT 'John Doe' AS Name, 1698 AS InstanceID, '2016-04-27 00:00:00.000' AS AbsenceStart, '2016-04-28 00:00:00.000' AS AbsenceEnd, 1 AS NoOfDays, 7 AS rnk
-- First find the start rank:
declare #x int = (select top 1 rnk from #temp order by rnk asc)
-- Find the maximum number of records to loop through
declare #y int = (select top 1 rnk from #temp order by rnk desc)
-- This is your threshold for publishing
declare #a int
-- start loop
while #x <= #y
begin
if #a >=3
break; -- if threshold breached, stop loop.
else
if object_id('tempdb..#list') is not null
drop table #list
declare #z datetime = (select AbsenceStart from #temp where rnk = #x)
print #z
select
instanceid,
AbsenceStart
into #list
from #temp
where AbsenceStart >= #z
and AbsenceStart <= dateadd(dd,90,#z)
set #a = (select count(instanceid) from #list)
set #x = #x + 1
print #x
end
insert into dbo.DestinationTable
select *
from #list
Related
I have a table with few fields like id, country, ip, created_at. Then I am trying to get the deltas between total entry of one day and total entry of the next day.
CREATE TABLE session (
id int NOT NULL AUTO_INCREMENT,
country varchar(50) NOT NULL,
ip varchar(255),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);
INSERT INTO `session` (`id`, `country`, `ip`, `created_at`) VALUES
('1', 'IN', '10.100.102.11', '2021-04-05 20:26:02'),
('2', 'IN', '10.100.102.11', '2021-04-05 19:26:02'),
('3', 'US', '10.120.102.11', '2021-04-17 10:26:02'),
('4', 'US', '10.100.112.11', '2021-04-16 12:26:02'),
('5', 'AU', '10.100.102.122', '2021-04-12 19:36:02'),
('6', 'AU', '10.100.102.122', '2021-04-12 18:20:02'),
('7', 'AU', '10.100.102.122', '2021-04-12 23:26:02'),
('8', 'US', '10.100.102.2', '2021-04-16 21:33:01'),
('9', 'AU', '10.100.102.122', '2021-04-18 20:46:02'),
('10', 'AU', '10.100.102.111', '2021-04-04 13:19:12'),
('11', 'US', '10.100.112.11', '2021-04-16 12:26:02'),
('12', 'IN', '10.100.102.11', '2021-04-05 15:26:02'),
('13', 'IN', '10.100.102.11', '2021-04-05 19:26:02');
Now I have written this query to get the delta
SELECT T1.date1 as date, IFNULL(T1.cnt1-T2.cnt2, T1.cnt1) as delta from (
select TA.dateA as date1, MAX(TA.countA) as cnt1 from (
select DATE(created_at) AS dateA, COUNT(*) AS countA
FROM session
GROUP BY DATE(created_at)
UNION
select DISTINCT DATE(DATE(created_at)+1) AS dateA, 0 AS countA
FROM session
) as TA
group by TA.dateA
) as T1
LEFT OUTER JOIN (
select DATE(DATE(created_at)+1) AS date2,
COUNT(*) AS cnt2
FROM session
GROUP BY DATE(created_at)
) as T2
ON T1.date1=T2.date2
ORDER BY date;
http://sqlfiddle.com/#!9/4f5fd26/60
Then I am getting the results as
date delta
2021-04-04 1
2021-04-05 3
2021-04-06 -4
2021-04-12 3
2021-04-13 -3
2021-04-16 3
2021-04-17 -2
2021-04-18 0
2021-04-19 -1
Now, is there any place of improvements/optimizes on it with/or window functions? (I am zero with SQL, still playing around).
Try a shorter version
with grp as (
SELECT t.dateA, SUM(t.cnt) AS countA
FROM session,
LATERAL (
select DATE(created_at) AS dateA, 1 as cnt
union all
select DATE(DATE(created_at)+1), 0 as cnt
) t
GROUP BY dateA
)
select t1.dateA as date, IFNULL(t1.countA-t2.countA, t1.countA) as delta
from grp t1
left join grp t2 on DATE(t2.dateA + 1) = t1.dateA
order by t1.dateA
db<>fiddle
I have a requirement to get the data from mysql db.
below is the DDL, DML ,sample output and query.
I need help on optimizing the query, because I am sure the query I have wrote is very basic.
create table team (
team_id int,
team_name char(10)
);
create table matches (
matches_id int,
host_team_id int,
guest_team_id int,
host_goals int,
guest_goals int
);
insert into team values (10, 'mumbai');
insert into team values (20, 'delhi');
insert into team values (30, 'banglore');
insert into team values (40, 'chennai');
insert into team values (50, 'gujarat');
insert into matches values (1, 50, 20, 2,1);
insert into matches values (2, 30, 40, 2,0);
insert into matches values (3, 10, 50, 1,1);
insert into matches values (4, 20, 30, 0,1);
insert into matches values (5, 40, 20, 3,2);
insert into matches values (6, 50, 30, 1,0);
insert into matches values (7, 40, 10, 1,2);
-- rules
-- team with more goals wins and gets 3points
-- team which lose gets 0points
-- for a tie, each team gets 1point
expected output [order by goals and then by team_name
team_id | team_name | goals
50 gujarat 7
30 bangalore 6
10 mumbai 4
40 chennai 3
20 delhi 0
QUERY
select * from (select team_id, team_name, sum(goals) as goals from (
select team_id, team_name,
SUM( Case
when host_goals > guest_goals then 3
when host_goals = guest_goals then 1
else 0
end ) as goals
from team t , matches m1
where t.team_id = m1.host_team_id
group by team_id, team_name
union all
select team_id, team_name,
SUM( Case
when guest_goals > host_goals then 3
when host_goals = guest_goals then 1
else 0
end ) as goals
from team t , matches m1
where t.team_id = m1.guest_team_id
group by team_id, team_name
order by goals desc, team_name asc) as finalOut
group by team_id, team_name ) as t1
order by goals desc, team_name asc
Use better criteria instead of having to union two queries. Forget the union all and do this:
select team_id, team_name,
SUM(
(t.team_id = m1.host_team_id) * ((host_goals >= guest_goals) + 2 * (host_goals > guest_goals)) +
(t.team_id = m1.guest__team_id) * ((host_goals <= guest_goals) + 2 * (host_goals < guest_goals))
) as goals
from team t , matches m1
where ((t.team_id = m1.host_team_id) or (t.team_id = m1.guest_team_id))
group by team_id, team_name
This should be a good starting point. (untested)
You should perform the sum, group by and order by only on the most external select
select * from (select team_id, team_name, sum(goals) as goals from (
select
team_id
, team_name
, Case
when host_goals > guest_goals then 3
when host_goals = guest_goals then 1
else 0
end as goals
from team t
Inner join matches m1 on t.team_id = m1.host_team_id
union all
select
team_id
, team_name
, Case
when guest_goals > host_goals then 3
when host_goals = guest_goals then 1
else 0
end
from team t
inner join matches m1 on t.team_id = m1.guest_team_id
) as finalOut
group by team_id, team_name
order by goals desc, team_name asc
I would go for radical simplification:
select t.team_id, t.team_name, sum(points) as points
from teams t join
((select host_team_id as team_id, host_goals as goals, 'host' as which,
(case when host_goals > guest_goals then 3
when host_goals = guest_goals then 1
else 0
end) as points
from matches
) union all
(select guest_team_id as team_id, guest_goals as goals, 'guest' as which
(case when guest_goals > host_goals then 3
when guest_goals = host_goals then 1
else 0
end) as points
from matches
)
) hg
on hg.team_id = t.team_id
group by t.team_id, t.team_name
order by sum(points) desc, team_name;
If you want all teams -- even those who have played no matches -- then use left join.
I have several datetime columns. I need to calculate in SQL Server 2008 for each timestamp how many datetime stamps in the same column are smaller than each of datetime stamps.
For example: for 2016-05-01 14:24:000.00 in column DateTime1 I need to calculate how many datetime values are smaller then it in DateTime1 column.
I also need to know how many datetimestamps are smaller than a datetime stamp for the same record (in the same row) in column DateTime2 and 3.
DateTime1 DateTime2 DateTime3
----------------------------------------------------------------------------
2016-05-01 13:24:000.00 2016-05-01 15:24:000.00 2016-05-01 16:20:000.00
2016-05-01 13:30:000.00 2016-05-01 14:21:000.00 2016-05-01 15:10:000.00
2016-05-01 14:24:000.00 2016-05-01 17:21:000.00 2016-05-01 18:10:000.00
If I understand correctly, you can use rank():
select t.*,
rank() over (order by datetime1) as dt1_rank,
rank() over (order by datetime2) as dt2_rank,
rank() over (order by datetime3) as dt3_rank
from t ;
Depending on how you want to treat tied values, you might actually want dense_rank(). Also, you might want to subtract 1 from the ranking value.
Assume I have a Table name [TestTB] has 3 columns DateTime1,DateTime2,DateTime3 .
I said CountSmallerDateTime1 as "how many datetime values are smaller then it in DateTime1 column"
I said CountSmallerDateTime2 as "how many datetimestamps are smaller than a datetime stamp for the same record (in the same row) in column DateTime2" , Similarity , CountSmallerDateTime3 for DateTime3 .
Then I have a query for your request :
SELECT [DateTime1]
,[DateTime2]
,[DateTime3]
,(SELECT COUNT(1)
FROM [TestTB] Sub
WHERE TB.[DateTime1] >Sub.[DateTime1]) AS CountSmallerDateTime1
,(
CASE WHEN TB.[DateTime2] > TB.[DateTime1] AND TB.[DateTime2] > TB.[DateTime3] THEN
2
WHEN ( (TB.[DateTime2] <= TB.[DateTime1] AND TB.[DateTime2] > TB.[DateTime3])
OR (TB.[DateTime2] > TB.[DateTime1] AND TB.[DateTime2] <= TB.[DateTime3])) THEN
1
ELSE
0
END
) AS CountSmallerDateTime2,
(
CASE WHEN TB.[DateTime3] > TB.[DateTime1] AND TB.[DateTime3] > TB.[DateTime2] THEN
2
WHEN ( (TB.[DateTime3] <= TB.[DateTime1] AND TB.[DateTime3] > TB.[DateTime2])
OR (TB.[DateTime3] > TB.[DateTime1] AND TB.[DateTime3] <= TB.[DateTime2])) THEN
1
ELSE
0
END
) AS CountSmallerDateTime3 FROM [TestTB] TB
;WITH CTE(DATE1, DATE2, DATE3,RN)
AS
(
SELECT CONVERT(DATETIME , '2016-05-01 13:24:000.00'), CONVERT(DATETIME,'2016-05-01 15:24:000.00'), CONVERT(DATETIME,'2016-05-01 16:20:000.00'),1
UNION ALL
SELECT CONVERT(DATETIME , '2016-05-01 13:30:000.00'), CONVERT(DATETIME,'2016-05-01 14:21:000.00'), CONVERT(DATETIME,'2016-05-01 15:10:000.00'),2
UNION ALL
SELECT CONVERT(DATETIME , '2016-05-01 14:24:000.00'), CONVERT(DATETIME,'2016-05-01 17:21:000.00'), CONVERT(DATETIME,'2016-05-01 18:10:000.00'),3
)
SELECT RANK() OVER (ORDER BY DATE1) -1 AS SAME_COLUMN_DATE1
, RANK() OVER (ORDER BY DATE2) -1 AS SAME_COLUMN_DATE2
, RANK() OVER (ORDER BY DATE3) -1 AS SAME_COLUMN_DATE3
, CASE WHEN RN=1 AND DATE1< DATE2 AND DATE1<DATE3 THEN 0
WHEN RN=1 AND DATE1< DATE2 AND DATE1>DATE3 THEN 1
WHEN RN=1 AND DATE1> DATE2 AND DATE1<DATE3 THEN 1
ELSE 2
SAME_ROW_1
, CASE WHEN RN=2 AND DATE2< DATE1 AND DATE2<DATE3 THEN 0
WHEN RN=2 AND DATE2< DATE1 AND DATE2>DATE3 THEN 1
WHEN RN=2 AND DATE2> DATE1 AND DATE2<DATE3 THEN 1
ELSE 2
END SAME_ROW_2
, CASE WHEN RN=3 AND DATE3< DATE1 AND DATE3<DATE2 THEN 0
WHEN RN=3 AND DATE3< DATE1 AND DATE3>DATE2 THEN 1
WHEN RN=3 AND DATE3> DATE1 AND DATE3<DATE2 THEN 1
ELSE 2
END SAME_ROW_3
FROM CTE ORDER BY RN
I have a table like this(tblFuel):
time fuel
2014-11-04 17:11:08 231
2014-11-04 17:34:16 254
2014-11-04 18:03:48 241
2014-11-04 18:41:34 137
2014-11-04 18:43:42 111
Now I expect to show the biggest value of fuel during each 1 hour. For example: max from 17:00:00 to 17:59:59 and so on. And follow the previous requirement, the expected result should:
time fuel
2014-11-04 17:34:16 254
2014-11-04 18:03:48 241
So what should I do to achieve this result?
create table tblFuel (time timestamp, fuel int);
insert into tblFuel values ('2014-11-04 17:11:08', 231);
insert into tblFuel values ('2014-11-04 17:34:16', 254);
insert into tblFuel values ('2014-11-04 18:03:48', 241);
insert into tblFuel values ('2014-11-04 18:41:34', 137);
insert into tblFuel values ('2014-11-04 18:43:42', 111);
select
*
from tblFuel
where concat(date(time), hour(time), fuel) in
(select
concat(date(time), hour(time), max(fuel))
from tblFuel
group by
date(time),
hour(time))
Returns:
time fuel
2014-11-04 17:34:16 254
2014-11-04 18:03:48 241
This query will be helpful.
DECLARE #tblFuel TABLE
(
Val INTEGER,
Time DATETIME
)
INSERT INTO #tblFuel
SELECT '231', '2014-11-04 17:11:08' union All
SELECT '254', '2014-11-04 17:34:16' union All
SELECT '241', '2014-11-04 18:03:48' union All
SELECT '137', '2014-11-04 18:41:34' union All
SELECT '111', '2014-11-04 18:43:42'
SELECT A.Val, A.Time FROM #tblFuel AS A
Inner join
(SELECT MAX(Val) AS VAL,
CONVERT(VARCHAR(20), Time, 110) +' ' + CAST(DATEPART(hour, Time) as varchar(2)) AS Time
FROM #tblFuel GROUP BY CONVERT(VARCHAR(20), Time, 110) +' ' + CAST(DATEPART(hour, Time) as varchar(2))) AS B
ON A.Val = B.val AND CONVERT(VARCHAR(20), A.Time, 110) +' ' + CAST(DATEPART(hour, A.Time) as varchar(2)) = B.Time
Here's one way to do it. It uses the DATE_FORMAT function to group by date and hour.
SELECT ft.time, ft.fuel
FROM fuel_table ft
JOIN
(SELECT DATE_FORMAT(time, '%Y%m%d %H') date_and_hour, MAX(fuel) max_fuel
FROM fuel_table
GROUP BY date_and_hour) AS max_fuel
ON DATE_FORMAT(ft.time, '%Y%m%d %H') = max_fuel.date_and_hour
AND ft.fuel = max_fuel
I'm trying to sum a column with rank function and group by month, my code is
select dbo.UpCase( REPLACE( p.Agent_name,'.',' '))as Agent_name, SUM(convert ( float ,
p.Amount))as amount,
RANK() over( order by SUM(convert ( float ,Amount )) desc ) as arank
from dbo.T_Client_Pc_Reg p
group by p.Agent_name ,p.Sale_status ,MONTH(Reg_date)
having [p].Sale_status='Activated'
Currently I'm getting all total value of that column not month wise
Name amount rank
a 100 1
b 80 2
c 50 3
for a amount 100 is total amount till now but , i want get current month total amount not last months..
Maybe you just need to add a WHERE clause? Here is a minor re-write that I think works generally better. Some setup in tempdb:
USE tempdb;
GO
CREATE TABLE dbo.T_Client_Pc_Reg
(
Agent_name VARCHAR(32),
Amount INT,
Sale_Status VARCHAR(32),
Reg_date DATETIME
);
INSERT dbo.T_Client_Pc_Reg
SELECT 'a', 50, 'Activated', GETDATE()
UNION ALL SELECT 'a', 50, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'NotActivated', GETDATE()
UNION ALL SELECT 'c', 25, 'Activated', GETDATE()
UNION ALL SELECT 'c', 25, 'Activated', GETDATE()
UNION ALL SELECT 'c', 25, 'Activated', GETDATE()-40;
Then the query:
SELECT
Agent_name = UPPER(REPLACE(Agent_name, '.', '')),
Amount = SUM(CONVERT(FLOAT, Amount)),
arank = RANK() OVER (ORDER BY SUM(CONVERT(FLOAT, Amount)) DESC)
FROM dbo.T_Client_Pc_Reg
WHERE Reg_date >= DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP), 0)
AND Reg_date < DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP) + 1, 0)
AND Sale_status = 'Activated'
GROUP BY UPPER(REPLACE(Agent_name, '.', ''))
ORDER BY arank;
Now cleanup:
USE tempdb;
GO
DROP TABLE dbo.T_Client_Pc_Reg;