only max(value) from union by several columns - mysql

I want to retrieve one result for each [Group] by the highest Time.
Result of current code:
SELECT [Group], ArticleNumber, max(TimeTrue) as Time
FROM PerformanceOpc (NOLOCK) WHERE ([Group]='Pack2' OR [Group]='70521-030')
GROUP BY [Group], ArticleNumber
UNION
SELECT [Group], ArticleNumber, max(StopTime) as Time
FROM StoppageOpc (NOLOCK) WHERE ([Group]='Pack2' OR [Group]='70521-030')
GROUP BY [Group], ArticleNumber
ORDER BY Time DESC
The result should be only two records (csv):
Group,ArticleNumber,Time
70521-030,,2021-03-15 13:50:15
Pack2,183026,2021-03-15 13:47:39

Hmmm . . . you would seem to want to union all before aggregating:
SELECT [Group], ArticleNumber, max(Time) as Time
FROM ((SELECT [Group], ArticleNumber, TimeTrue as Time
FROM PerformanceOpc
WHERE [Group] IN ('Pack2', '70521-030')
) UNION ALL
(SELECT [Group], ArticleNumber, StopTime as Time
FROM StoppageOpc
WHERE [Group] IN ('Pack2', '70521-030')
)
) g
GROUP BY [Group], ArticleNumber;
This returns one row per group and article, which seems to be what your query is doing.
If you really want only one row per group, then you want ROW_NUMBER() and not aggregation:
SELECT g.*
FROM (SELECT g.*, ROW_NUMBER() OVER (PARTITION BY [Group] ORDER BY time DESC) as seqnum
FROM ((SELECT [Group], ArticleNumber, TimeTrue as Time
FROM PerformanceOpc
WHERE [Group] IN ('Pack2', '70521-030')
) UNION ALL
(SELECT [Group], ArticleNumber, StopTime as Time
FROM StoppageOpc
WHERE [Group] IN ('Pack2', '70521-030')
)
) g
) g
WHERE seqnum = 1;

Try select top 1 with order by in temporal tables and then query them with union
SELECT top 1[Group], ArticleNumber, max(TimeTrue) as Time into #tmp1
FROM PerformanceOpc (NOLOCK) WHERE ([Group]='Pack2' OR [Group]='70521-030')
GROUP BY [Group], ArticleNumber
order by Time desc
SELECT top 1 [Group], ArticleNumber, max(StopTime) as Time into #tmp2
FROM StoppageOpc (NOLOCK) WHERE ([Group]='Pack2' OR [Group]='70521-030')
GROUP BY [Group], ArticleNumber
ORDER BY Time DESC
select * from #tmp1
union
select * from #tmp2
drop table #tmp1
drop table #tmp2

Related

MySQL-ordering partitions randomly

table1 has 3 columns in my database: id, category, timestamp. I need to query the newest 3 rows from each category:
WITH ranked_rows AS
(SELECT t.*, ROW_NUMBER() OVER (PARTITION BY category ORDER BY t.timestamp DESC) AS rn
FROM table1 AS t)
SELECT ranked_rows.* FROM ranked_rows WHERE rn<=3
now I need to select 10 partitions from the results randomly (please notice that each partition has 3 rows). how to do that?
There are various methods. One is:
WITH ranked_rows AS (
SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY t.timestamp DESC) AS seqnum,
DENSE_RANK() OVER (ORDER BY MD5(category)) as catnum
FROM table1 t
)
SELECT ranked_rows.*
FROM ranked_rows
WHERE seqnum <= 3 AND catnum <= 10;
The md5() just makes the results look random.
if you want true random per category, here is one way :
with categorycte as (
select category , rand() randomcatid
from table1
group by category
),ranked_rows AS
(
SELECT t.*
, ROW_NUMBER() OVER (PARTITION BY category ORDER BY t.timestamp DESC) AS rn
, dense_rank() over (order by randomcatid) catnum
FROM table1 AS t
join categorycte c on t.category = c.category
)
SELECT ranked_rows.* FROM ranked_rows
WHERE rn<=3 and catnum <= 10;

How to keep the latest record of a table

I have a table with following structure
Date
train
time1
train1
time2
train2
time3
train1
time4
train2
I want to create a new table and keeping only the latest record of each distinct train
Date
train
time3
train1
time4
train2
How should I achieve so?
One method is for selecting the most recent rows is:
select t.*
from releng_retry_test_phases t
where t.date = (select max(t2.date) from releng_retry_test_phases t2 where t2.train = t.train);
If you actually want to modify the table and delete the older rows;
delete t
from releng_retry_test_phases t join
(select t2.train, max(date) as max_date
from releng_retry_test_phases t2
group by t2.train
) t2
using (train)
where t.date < t2.max_date;
You can use ROW_NUMBER() to identify the rows you want:
select date, train
from (
select *,
row_number() over(partition by train order by date desc) as rn
) x
where rn = 1
WITH temp As(
SELECT *, Row_Number() over (PARTITION BY train ORDER BY date DESC ) as
rowNumber FROM table
)
SELECT date, train FROM temp WHERE rowNumber = 1
You can use row_number() method.

Group by date and take the last one

This is my table :
What I'm trying to do, is to take the last disponibility of a user, by caserne. Example, I should have this result :
id id_user id_caserne id_dispo created_at
31 21 12 1 2019-10-24 01:21:46
33 21 13 1 2019-10-23 20:17:21
I've tried this sql, but it does not seems to work all the times :
SELECT * FROM
( SELECT id, id_dispo, id_user, id_caserne, MAX(created_at)
FROM disponibilites GROUP BY id_user, id_caserne, id_dispo
ORDER BY created_at desc ) AS sub
GROUP BY id_user, id_caserne
What am I doing wrong ?
I would simply use filtering in the where clause using a correlated subquery:
select d.*
from disponibilites d
where d.created_at = (select max(d2.created_at)
from disponibilites d2
where d2.id_user = d.id_user
);
EDIT:
Based on your comments:
select d.*
from disponibilites d
where d.created_at = (select max(d2.created_at)
from disponibilites d2
where d2.id_user = d.id_user and
d2.id_caserne = d.id_caserne
where date(d2.created_at) = date(d.created_at)
);
You can use a correlated subquery, as demonstrated by Gordon Linoff, or a window function if your RDBMS supports it:
select * from (
select
t.*,
rank() over(partition by id_caserne, id_user order by created_at desc) rn
from disponibilites t
) x
where rn = 1
Another option is to use a correlated subquery without aggregation, only with a sort and limit:
select *
from mytable t
where created_at = (
select created_at
from mytable t1
where t1.id_user = t.id_user and t1.id_caserne = t.id_caserne
order by created_at desc
limit 1
)
With an index on (id_user, id_caserne, created_at), this should be a very efficient option.
you can join your max(created_date) to your original table
select t1.* from disponibilites t1
inner join
(select max(created_at), id_caserne, id
from disponibilites
group by id_caserne, id) t2
on t2.id = t1.id

mySQL - show first and last log from each day

i have a mysql table with colums: id(primary), name(varchar), TIME(timestamp)
ID , NAME , TIME
i want to get just first and last log for each day
example if i have data like this
1,name,2018-20-21 12:35:00
2,name,2018-20-21 13:38:00
3,name,2018-20-21 14:25:00
4,name,2018-20-21 15:39:00
5,name,2018-20-21 21:48:00
6,name,2018-20-22 13:25:00
7,name,2018-20-22 14:39:00
8,name,2018-20-22 19:48:00
i want to get in just this
1,name,2018-20-21 12:35:00
5,name,2018-20-21 21:48:00
6,name,2018-20-22 13:25:00
8,name,2018-20-22 19:48:00
Try this:
SELECT name, MAX(time), MIN(time) FROM Table GROUP BY DATE(time);
You could use the union for the min and the max time group by date
and join this with your table
select * from my_table
inner join (
select * from (
select min(time) my_time
from my_table
group by date(time)
union
select max(time)
from my_table
group by date(time)
) t on t.my_time = my_table.time
order by my_table.time
Hope this helps.
SELECT id, tmp.name, tmp.time FROM
(SELECT id, name, min(time) as time FROM table1 GROUP BY DATE(time)
UNION ALL
(SELECT id, name, max(time) as time FROM table1 GROUP BY DATE(time)) tmp
ORDER BY tmp.time
You can try selecting the min and max for each day, since you want the entire line, a join is needed
and to filter out the actual min and max day, a aub query is needed
SELECT id, name, time
FROM
(
SELECT t2.*, MIN(DATE(t.time)) As min0 MAX(DATE(t.time)) As max0
FROM
table t
INNER JOIN table t2 ON t.id = t2.id
GROUP BY
DATE (t.time),
min0,
max0
) a
SELECT
l.id,l.name,l.time
FROM
log l
LEFT JOIN
(SELECT
max(time) as maxTime
FROM
log
GROUP BY date(time)) l1 ON l.time = l1.maxTime
LEFT JOIN
(SELECT
min(time) as minTime
FROM
log
GROUP BY date(time)) l2 ON l.time = l2.minTime
WHERE
(maxTime IS NOT NULL
OR minTime IS NOT NUll);
SELECT * from stack.log;

DATEDIFF two tables multiple entries based on ID

I am looking to write a query that will generate the TOTAL_HOURS_WORKED for each employee in an attendance management system with multiple access points (DEVICE_ID) and multiple entries (ROW_ID or ENTRY_ID).
So far I thought the best way would be to get the first 'CLOCK_IN' time and subtract it from the last 'CLOCK_OUT' time; then subtract the sum of any subsequent clockin's or out's from that. Below is the data and what I've got so far:
TIME IN LOG
TIME OUT LOG
As seen above there are numerous entries and even some erroneous or double entries. I would like the output to reflect something like this
PIN 326
TOTAL HOURS: 06:15:49 (hh:mm:ss)
This is the code I've writer so far that generates the above tables:
-- ALL LOG DATA
WITH BDEV_RPT1 AS
(
SELECT CONVERT(VARCHAR(9), TIME, 112)+CONVERT(CHAR(2), TIME, 114)+SUBSTRING(CONVERT(CHAR(5), TIME, 114),4,2)+SUBSTRING(CONVERT(CHAR(8), TIME, 114),7,2) AS ENTRY_ID, CONVERT(VARCHAR(9), TIME, 112) AS ENTRY_DATE, CONVERT(CHAR(15), TIME, 8) AS ENTRY_TIME, TIME, PIN, DEVICE_ID, STATE, EVENT_POINT_NAME FROM ACC_MONITOR_LOG WHERE PIN='326'
)
,BDEV_RPT2 AS
-- ALL EXIT ENTRIES
(
SELECT ROW_NUMBER() OVER(ORDER BY TIME DESC) AS ROW_NUM, ENTRY_ID, ENTRY_DATE, ENTRY_TIME, TIME, PIN, DEVICE_ID, STATE, EVENT_POINT_NAME FROM BDEV_RPT1 WHERE STATE IN ('1','2') AND DEVICE_ID IN ('18','10','6','9')
)
,BDEV_RPT3 AS
-- ALL ENTRANCE ENTRIES
(
SELECT ROW_NUMBER() OVER(ORDER BY TIME DESC) AS ROW_NUM, ENTRY_ID, ENTRY_DATE, ENTRY_TIME, TIME, PIN, DEVICE_ID, STATE, EVENT_POINT_NAME FROM BDEV_RPT1 WHERE STATE IN ('0','2') AND DEVICE_ID IN ('21','10','6','9')
)
,BDEV_RPT4 AS
-- LAST ENTRY
(
SELECT TOP 1 * FROM BDEV_RPT1 WHERE ENTRY_DATE='20160117' AND STATE IN ('1','2') AND DEVICE_ID IN ('18','10','6','9') ORDER BY ENTRY_TIME DESC
)
,BDEV_RPT5 AS
-- FIRST ENTRY
(
SELECT TOP 1 * FROM BDEV_RPT1 WHERE ENTRY_DATE='20160117' AND STATE IN ('0','2') AND DEVICE_ID IN ('21','10','6','9') ORDER BY ENTRY_TIME ASC
)
--MAIN ENTRY TOTAL HOURS
SELECT CONVERT(varchar, DATEADD(ss, (SELECT DATEDIFF(SECOND, (SELECT ENTRY_TIME FROM BDEV_RPT5),(SELECT ENTRY_TIME FROM BDEV_RPT4))), 0), 108)
I FIGURED IT OUT BUT NOW AM STUCK ON SOME PL SQL LOOPS. WILL POST TO AOTHER THREAD.
-- ALL ENTRIES INTO TEMP_TABLE
INSERT INTO ACC_FINAL_TIMES (ROW_NUM, ENTRY_ID, ENTRY_DATE, ENTRY_TIME, TIME, PIN, DEVICE_ID, STATE, EVENT_POINT_NAME, DUPLICATE)
WITH BDEV_RPT2 AS
-- LAST OUT ENTRY
(
SELECT *
FROM (SELECT ROW_NUMBER()OVER(ORDER BY TIME DESC) ROW_NUM, a.* FROM acc_time_logs a WHERE PIN='944' AND ENTRY_DATE='20160117' AND STATE IN ('1','2') AND DEVICE_ID IN ('18','10','6','9') ORDER BY TIME DESC)
WHERE ROW_NUM IN ('1','2') AND DEVICE_ID='18' OR (ROW_NUM='1' AND DEVICE_ID IN ('10','6','9') AND NOT EXISTS(SELECT NULL FROM (SELECT ROW_NUMBER()OVER(ORDER BY TIME DESC) ROW_NUM, a.* FROM acc_time_logs a WHERE PIN='944' AND ENTRY_DATE='20160117' AND STATE IN ('1','2') AND DEVICE_ID IN ('18','10','6','9') ORDER BY TIME DESC) WHERE ROW_NUM IN ('1','2') AND DEVICE_ID='18'))
)
,BDEV_RPT3 AS
-- FIRST IN ENTRY
(
SELECT *
FROM (SELECT ROW_NUMBER()OVER(ORDER BY TIME ASC) ROW_NUM, a.* FROM acc_time_logs a WHERE PIN='944' AND ENTRY_DATE='20160117' AND STATE IN ('0','2') AND DEVICE_ID IN ('21','10','6','9') ORDER BY TIME ASC)
WHERE ROW_NUM IN ('1','2') AND DEVICE_ID='21' OR (ROW_NUM='1' AND DEVICE_ID IN ('10','6','9') AND NOT EXISTS(SELECT NULL FROM (SELECT ROW_NUMBER()OVER(ORDER BY TIME ASC) ROW_NUM, a.* FROM acc_time_logs a WHERE PIN='944' AND ENTRY_DATE='20160117' AND STATE IN ('0','2') AND DEVICE_ID IN ('21','10','6','9') ORDER BY TIME ASC) WHERE ROW_NUM IN ('1','2') AND DEVICE_ID='21'))
)
,ENTRY_DUP1 AS
-- REMOVE DUPLICATE ROWS OUT
(
SELECT ROW_NUMBER()OVER(ORDER BY TIME DESC) ROW_NUM, ENTRY_ID, ENTRY_DATE, ENTRY_TIME, TIME, PIN, DEVICE_ID, STATE, EVENT_POINT_NAME FROM acc_time_logs WHERE PIN='944' AND ENTRY_DATE='20160117' AND STATE
IN ('1','2') AND DEVICE_ID IN ('18','10','6','9') AND ENTRY_ID NOT IN (SELECT ENTRY_ID FROM BDEV_RPT2)
)
,ENTRY_INT1 AS
-- REMOVE DUPLICATE ROWS OUT
(
SELECT a.ROW_NUM, a.ENTRY_ID, a.ENTRY_DATE, a.ENTRY_TIME, a.TIME, a.PIN, a.DEVICE_ID, a.STATE, a.EVENT_POINT_NAME FROM ENTRY_DUP1 a INNER JOIN ENTRY_DUP1 b ON a.ROW_NUM = b.ROW_NUM + 1 WHERE DATEDIFF(a.TIME, b.TIME) < 60 AND a.ROW_NUM <> 1
)
,ENTRY_DUP2 AS
-- REMOVE DUPLICATE ROWS IN
(
SELECT ROW_NUMBER()OVER(ORDER BY TIME DESC) ROW_NUM, ENTRY_ID, ENTRY_DATE, ENTRY_TIME, TIME, PIN, DEVICE_ID, STATE, EVENT_POINT_NAME FROM acc_time_logs WHERE PIN='944' AND ENTRY_DATE='20160117' AND STATE IN ('0','2') AND DEVICE_ID IN ('21','10','6','9') AND ENTRY_ID NOT IN (SELECT ENTRY_ID FROM BDEV_RPT3)
)
,ENTRY_INT2 AS
-- REMOVE DUPLICATE ROWS IN
(
SELECT a.ROW_NUM, a.ENTRY_ID, a.ENTRY_DATE, a.ENTRY_TIME, a.TIME, a.PIN, a.DEVICE_ID, a.STATE, a.EVENT_POINT_NAME FROM ENTRY_DUP2 a INNER JOIN ENTRY_DUP2 b ON a.ROW_NUM = b.ROW_NUM + 1 WHERE DATEDIFF(a.TIME, b.TIME) < 60 AND a.ROW_NUM <> 1
)
SELECT ROW_NUMBER()OVER(ORDER BY TIME ASC) ROW_NUM, ENTRY_ID, ENTRY_DATE, ENTRY_TIME, TIME, PIN, DEVICE_ID, STATE, EVENT_POINT_NAME, NULL FROM
(
SELECT ENTRY_ID, ENTRY_DATE, ENTRY_TIME, TIME, PIN, DEVICE_ID, STATE, EVENT_POINT_NAME FROM ENTRY_DUP2 c WHERE NOT EXISTS(SELECT * FROM ENTRY_INT2 d WHERE c.ROW_NUM = d.ROW_NUM)
UNION ALL
SELECT ENTRY_ID, ENTRY_DATE, ENTRY_TIME, TIME, PIN, DEVICE_ID, STATE, EVENT_POINT_NAME FROM ENTRY_DUP1 c WHERE NOT EXISTS(SELECT * FROM ENTRY_INT1 d WHERE c.ROW_NUM = d.ROW_NUM)) a ORDER BY TIME ASC
=====================================================================================================================================================================
=====================================================================================================================================================================
=====================================================================================================================================================================
=====================================================================================================================================================================
=====================================================================================================================================================================
=====================================================================================================================================================================
=====================================================================================================================================================================
=====================================================================================================================================================================
-- CHECK FIRST ROW OUT ENTRY
DELETE FROM ACC_FINAL_TIMES WHERE ROW_NUM=1 AND STATE=0
-- UPDATE MAIN DOOR ENTRIES
UPDATE acc_final_times a
SET STATE =
CASE
WHEN EVENT_POINT_NAME = 'Main Check-In-1'
THEN 0
ELSE 1
END
WHERE STATE='2'
--MARK IRREGULAR ENTRY'S AS DUPLICATE
exec IRR_AS_DUP
CREATE PROCEDURE IRR_AS_DUP IS
CURSOR ftime_logs IS
SELECT * FROM ACC_FINAL_TIMES WHERE DUPLICATE IS NULL;
BEGIN
FOR x IN ftime_logs LOOP
UPDATE ACC_FINAL_TIMES a
SET DUPLICATE =
CASE
WHEN a.STATE=x.STATE
THEN 1
ELSE 0
END
WHERE a.ROW_NUM=x.ROW_NUM+1;
END LOOP;
END IRR_AS_DUP;
/
-- REMOVE DUPLICATE OR IRREGULAR ENTRIES
DELETE ACC_FINAL_TIMES WHERE DUPLICATE = 1
-- RENUMBER ROWS
UPDATE ACC_FINAL_TIMES SET ROW_NUM=ROWNUM
-- HOUR MISSED FROM WORK
SELECT
a.ENTRY_ID,
a.ENTRY_DATE,
a.PIN,
a.ENTRY_TIME AS OUT_ENTRY,
b.ENTRY_TIME AS IN_ENTRY,
DATEDIFF(a.TIME,b.TIME) BREAK_TIMES
FROM ACC_FINAL_TIMES a
INNER JOIN ACC_FINAL_TIMES b
ON a.ROW_NUM = b.ROW_NUM-1 AND a.STATE='1' AND b.STATE='0'
-- IRREGULAR ENTRIES REPORT
SELECT a.* FROM acc_final_times a
LEFT JOIN (
SELECT
b.ROW_NUM AS ROW_A,
c.ROW_NUM AS ROW_B
FROM acc_final_times b
INNER JOIN acc_final_times c
ON b.ROW_NUM=c.ROW_NUM-1 AND b.STATE='1' AND c.STATE='0'
) d
ON a.ROW_NUM IN (d.ROW_A, d.ROW_B)
WHERE d.ROW_A IS NULL
-- TOTAL HOURS WORKED
,TIME_DIFF AS
(
SELECT
a.ENTRY_ID,
a.ENTRY_DATE,
a.PIN,
a.ENTRY_TIME OUT_ENTRY,
b.ENTRY_TIME IN_ENTRY,
DATEDIFF(a.TIME,b.TIME) BREAK_TIMES
FROM acc_final_times a
INNER JOIN acc_final_times b
ON a.ROW_NUM = b.ROW_NUM-1 AND a.STATE='1' AND b.STATE='0'
)
SELECT TO_CHAR(TRUNC(SYSDATE) + NUMTODSINTERVAL (TOTAL_SECS, 'second'),'hh:mi:ss') TOTAL_HOURS FROM (SELECT DATEDIFF((SELECT TIME FROM BDEV_RPT3),(SELECT TIME FROM BDEV_RPT2))-(SELECT SUM(BREAK_TIMES) FROM TIME_DIFF) TOTAL_SECS FROM DUAL)