Tie-breaking mutliple matches on MAX() in SQL - sql

I have a table that looks like this:
| client_id | program_id | provider_id | date_of_service | data_entry_date | data_entry_time |
| --------- | ---------- | ----------- | --------------- | --------------- | --------------- |
| 2 | 5 | 6 | 02/02/2022 | 02/02/2022 | 0945 |
| 2 | 5 | 6 | 02/02/2022 | 02/07/2022 | 0900 |
| 2 | 5 | 6 | 02/04/2022 | 02/04/2022 | 1000 |
| 2 | 5 | 6 | 02/04/2022 | 02/04/2022 | 1700 |
| 2 | 5 | 6 | 02/04/2022 | 02/05/2022 | 0800 |
| 2 | 5 | 6 | 02/04/2022 | 02/05/2022 | 0900 |
I need to get the most recent date_of_service entered. From the table above, the desired result/row is:
date_of_service = 02/04/2022, data_entry_date = 02/05/2022, data_entry_time = 0900
This resulting date_of_service will be left joined to the master table.
This query mostly works:
SELECT t1.client_id, t1.program_id, t1.provider_id, t2.date_of_service
FROM table1 as t1
WHERE provider_id = '6'
LEFT JOIN
(SELECT client_id, program_id, provider_id, date_of_service
FROM table2) as t2
ON t2.client_id = t1.client_id
AND t2.program_id = t1.program_id
AND t2.provider_id = t1.provider_id
AND t2.date_of_service =
(SELECT MAX(date_of_service)
FROM t2 as t3
WHERE t3.client_id = t1.client_id
AND t3.program_id = t1.program_id
AND t3.provider_id = t1.provider_id
)
)
But it also returns multiple rows whenever there is more than one match on the max(date_of_service).
To solve this, I need to use the max data_entry_date to break any ties whenever there is more than one row that matches the max(date_of_service). Likewise, I also need to use the max data_entry_time to break any ties whenever there is more than one row that also matches the max data_entry_date.
I tried the following:
SELECT t1.client_id, t1.program_id, t1.provider_id, t2.date_of_service
FROM table1 as t1
WHERE provider_id = '6'
LEFT JOIN
(SELECT TOP(1) client_id, program_id, provider_id, date_of_service, data_entry_date, data_entry_time
FROM table2
ORDER BY date_of_service DESC, data_entry_date DESC, data_entry_time DESC
) as t2
ON t2.client_id = t1.client_id
AND t2.program_id = t1.program_id
AND t2.provider_id = t1.provider_id
But I can only get it to return null values for the date_of_service.
Likewise, this:
SELECT t1.client_id, t1.program_id, t1.provider_id, t2.date_of_service
FROM table1 as t1
WHERE provider_id = '6'
LEFT JOIN
(
SELECT TOP(1) client_id AS client_id2, program_id AS program_id2, provider_id AS provider_id2, date_of_service, data_entry_date, data_entry_time
FROM table2 AS t3
JOIN
(SELECT
MAX(date_of_service) AS max_date_of_service
,MAX(data_entry_date) AS max_data_entry_date
FROM table1
WHERE date_of_service = (SELECT MAX(date_of_service) FROM table2)
) AS t4
ON t3.date_of_service = t4.max_date_of_service
AND t3.data_entry_date = t4.max_data_entry_date
ORDER BY data_entry_time
) AS t2
ON t2.client_id2 = t1.client_id
AND t2.program_id2 = t1.program_id
AND t2.provider_id2 = t1.provider_id
... works (meaning it doesn't throw any errors), but it only seems to return null values for me.
I've tried various combinations of MAX, ORDER BY, and multiple variations of JOIN's, but haven't found one that works yet.
I don't know what version my SQL database is, but it doesn't appear to handle window functions like OVER and PARTITION or other things like COALESCE. I've been using DBeaver 22.2.0 to test the SQL scripts.

Based on your what you've provided, looks like you can simply query table2:
SELECT client_id, program_id, provider_id, MAX(date_of_service), MAX(data_entry_date), MAX(data_entry_time)
FROM table2
GROUP BY client_id, program_id, provider_id
If you need to join this result set to table1, just JOIN to the statement above on client_id, program_id, provider_id

Try using below query. This is using just joins and sub query.
SELECT TOP 1 * FROM table1 t1
JOIN (
SELECT
MAX(date_of_Service) AS Max_date_of_Service
,MAX(data_entry_date) AS Max_data_entry_date
FROM table1
WHERE date_of_Service = (SELECT MAX(date_of_Service) FROM table1)
)t2
ON t1.date_of_Service = t2.Max_date_of_Service
AND t1.data_entry_date = t2.Max_data_entry_date
ORDER BY data_entry_time

Related

Linking tables on multiple criteria

I've got myself in a bit of a mess on something I'm doing where I'm trying to get two tables linked together based on multiple bits of info.
I want to link one table to another based on the basic rules of(in this hierarchy)
where main linking is where orderid matches between the two tables
records from table 2 where valid=Y,
from those i want the valid records which has the highest seqn1 number and then from those the one that has the highest seqn2 value
table1
orderid | date | otherinfo
223344 | 22/10/2020 | okokkokokooeodijjf
table2
orderid | seqn1 | seqn2 | valid | additonaldata
223344 | 1 | 3 | y | sdfsfsf
223344 | 2 | 1 | y | sffferfr
223344 | 2 | 2 | y | sfrfrefr -- This row
223344 | 2 | 3 | n | rfrg66rr
223344 | 2 | 4 | n | adwere
223344 | 3 | 4 | n | adwere
so would want the final record to be
orderid | date | otherinfo | seqn1 | seqn2 | valid | additonaldata
223344 | 22/10/2020 | okokkokokooeodijjf | 2 | 2 | y | sfrfrefr
I started off with the code below but I'm not sure I'm doing it right and I can't seem to get it to pay attention to the valid flag when i try to add it in.
SELECT * FROM table1
left JOIN table2
ON table1.orderid = table2.orderid
AND table2.seqn1 = (SELECT MAX(table2.seqn1) FROM table2 WHERE table1.orderid = table2.orderid)
AND table2.seqn2 = (SELECT MAX(table2.seqn2) FROM table2 WHERE table1.orderid = table2.orderid
AND table2.seqn1 = (SELECT MAX(table2.seqn1) FROM table2 WHERE table1.orderid = table2.orderid))
Could someone help me amend the code please.
Use row_number analytic function with partition by orderid and order by SEQNRs in the order you need. No need for multiple subselects. To add more selections for the single row, use CASE to map your values to numbers and order by them also.
Fiddle here.
with l as (
select *,
rank() over(partition by orderid order by seqn1 desc, seqn2 desc) as rn
from line
where valid = 'y'
)
select *
from header as h
join l
on h.orderid = l.orderid
and l.rn = 1
How about something like this:
;
with cte_table2 as
(
SELECT ordered
,MAX(seqn1) as seqn1
,MAX(seqn2) as seqn2
FROM table2
where valid = 'y'
group by ordered --check if you need to add 'valid' to the group by but I don't think so.
)
SELECT
t1.*
,t3.otherinfo
--,t3.[OtherFields]
from table1 t1
inner join cte_table2 t2 on t1.orderid = t2.orderid -- first match on id
left join table2 t3 on t3.orderid = t2.orderid and t3.seqn1 = t2.seqn1 and t3.seqn2 = t2.seqn2

Count missing values

I have a following table called Test:
Id | SomeId | Value
-----------------------------------------------------
019D9E52-41D1-45DF-81B6-C7CC484115A7 | 1 | 1
262640CA-65C2-4E30-8654-E187ACA1EEF4 | 1 | 1
53710AFC-4E19-4B1C-B68B-CDB713EC3D62 | 1 | 2
8FF7E77C-D04C-4961-82D9-87C2E5A1A096 | 1 | 2
-----------------------------------------------------
119D9E52-41D1-45DF-81B6-C7CC484115A7 | 2 | 1
762640CA-65C2-4E30-8654-E187ACA1EEF4 | 2 | 1
93710AFC-4E19-4B1C-B68B-CDB713EC3D62 | 2 | 2
4FF7E77C-D04C-4961-82D9-87C2E5A1A096 | 2 | 2
And there is a view called TestView:
SomeId | Value | Description
----------------------------
1 | 1 | 'One'
1 | 2 | 'Two'
1 | 3 | 'Three'
----------------------------
2 | 1 | 'One'
2 | 2 | 'Two'
These are just pseudo code examples.
I want to count all the values from the Test table (for a specific [SomeId]), and if value from the TestView (with a specific [SomeId]) is not in the Test table I just want to display 0 as count.
If I wanted to count values WHERE [Test].[SomeId] = 1, here's the expected result:
Value | Count
-----------------
One | 2
Two | 2
Three | 0
This is my query so far:
SELECT
tv.[Description] AS [Value],
COUNT(t.[Id]) - COUNT(tv.[Value]) AS [Count]
FROM [TestView] AS tv
LEFT JOIN [Test] AS t ON
t.[SomeId] = tv.[SomeId]
AND t.[Value] = tv.[Value]
WHERE
t.[SomeId] = 1
GROUP BY
tv.[Description]
But this gives me bad result... Anyways, here's the SQL Fiddle
EDIT:
This is just an addition to a Test table. What is Test table has one more foreign key Id, let's call it OtherId. Now when I use the query from the answer I won't get the result I wanted. Here's the modified query:
SELECT
t1.Description AS Value,
COUNT(t2.Value) AS Count
FROM TestView t1
LEFT JOIN test t2
ON t1.Value = t2.Value AND t1.SomeId = t2.SomeId
WHERE t1.SomeId = 1
AND t2.[OtherId] = *something* -- this is the addition
GROUP BY t1.Value, t1.Description
ORDER BY t1.Value;
Try this:
SELECT
t1.Description AS Value,
COUNT(t2.Value) AS Count
FROM TestView t1
LEFT JOIN test t2
ON t1.Value = t2.Value AND t1.SomeId = t2.SomeId
WHERE t1.SomeId = 1
GROUP BY t1.Value, t1.Description
ORDER BY t1.Value;
Demo
Below is your Solution
SELECT
tv.[Description] AS [Value],
COUNT(t.[Id]) AS [Count]
FROM [TestView] AS tv
LEFT OUTER JOIN [Test] AS t ON tv.SomeId = t.SomeId
AND t.Value = tv.value
AND t.[SomeId] = 1
GROUP BY
tv.[Description]

how to query range?

Raw Data
| ID | STATUS |
| 1 | A |
| 2 | A |
| 3 | B |
| 4 | B |
| 5 | B |
| 6 | A |
| 7 | A |
| 8 | A |
| 9 | C |
Result
| START | END |
| 1 | 2 |
| 6 | 8 |
Range of STATUS A
How to query ?
This should give you the correct ranges:
SELECT
STATUS,
MIN(ID),
max_id
FROM (
SELECT
t1.STATUS,
t1.ID,
COALESCE(MAX(t2.ID), t1.ID) max_id
FROM
yourtable t1 LEFT JOIN yourtable t2
ON t1.STATUS=t2.STATUS AND t1.ID<t2.ID
WHERE
NOT EXISTS (SELECT NULL
FROM yourtable t3
WHERE
t3.STATUS!=t1.STATUS
AND t3.ID>t1.ID AND t3.ID<t2.ID)
GROUP BY
t1.ID,
t1.STATUS
) s
WHERE
status = 'A'
GROUP BY
STATUS,
max_id
Please see fiddle here.
You are probably better off with a cursor-based solution or a client-side function.
However, if you were using Oracle - the following would work.
WITH LOWER_VALS AS
( -- All the Ids with no immediate predecessor
SELECT ROWNUM AS RN, STATUS, ID AS LOWER FROM
(
SELECT STATUS, ID
FROM RAWDATA RD1
WHERE RD1.ID -1 NOT IN
(SELECT ID FROM RAWDATA PRED_TABLE WHERE PRED_TABLE.STATUS = RD1.STATUS)
ORDER BY STATUS, ID
)
) ,
UPPER_VALS AS
( -- All the Ids with no immediate successor
SELECT ROWNUM AS RN, STATUS, ID AS UPPER FROM
(
SELECT STATUS, ID
FROM RAWDATA RD2
WHERE RD2.ID +1 NOT IN
(SELECT ID FROM RAWDATA SUCC_TABLE WHERE SUCC_TABLE.STATUS = RD2.STATUS)
ORDER BY STATUS, ID
)
)
SELECT
L.STATUS, L.LOWER, U.UPPER
FROM
LOWER_VALS L
JOIN UPPER_VALS U ON
U.RN = L.RN;
Results in the set
A 1 2
A 6 8
B 3 5
C 9 9
http://sqlfiddle.com/#!4/10184/2
There is not a lot to go on from what you put, but I think this might work. I am using T-SQL because I don't know what you are using?
SELECT
min(ID)
, max(ID)
FROM RawData
WHERE [Status] = 'A'

MySQL query help (semi-unique results)

user_id | user_destination | user_date_out | user_date_in | user_purpose | uid
0095 | NYC | 2010-11-25 | 2010-11-26 | Work | 1
0105 | Seattle | 2010-11-15 | 2010-11-20 | Work | 2
0095 | Home | 2010-11-10 | 2010-11-11 | Personal | 3
0123 | Nashville | 2010-11-10 | 2010-11-12 | Doctober | 4
I have the above data in a MySQL table. I need a query that will output the row if it is the most recent user_date_out for that user_id. Hard to explain, but the rows that should be displayed via the query are uid 2,3 and 4 (UID 1 would fall off because the user_date_out is "older" than UID 3 for the same user). Can anyone help me with this? Thanks in advance!
EDIT: I solved it by using the following query:
SELECT *
FROM `table`
WHERE `uid` = (
SELECT `uid`
FROM `table` as `alt`
WHERE `alt`.`user_id` = `table`.`user_id`
ORDER BY `user_date_out`
LIMIT 1
)
ORDER BY `user_date_out`
Per request
SELECT *
FROM `table`
WHERE `uid` = (
SELECT `uid`
FROM `table` as `alt`
WHERE `alt`.`user_id` = `table`.`user_id`
ORDER BY `user_date_out`
LIMIT 1
)
ORDER BY `user_date_out`
SELECT t.*, a_subquery.min_user_date_out
FROM your_table t
JOIN (SELECT user_id, MIN(user_date_out) AS min_user_date_out
FROM your_table
GROUP BY user_id) a_subquery
ON a_subquery.min_user_date_out = t.user_date_out
AND a_subquery.user_id = t.user_id
Since MySQL sometimes has issues w/ subqueries, here's the same equivalent query with a self-exclusion join:
SELECT t.*
FROM your_table t
LEFT JOIN your_table t2
ON t.user_id = t2.user_id
AND t.user_date_out > t2.user_date_out
WHERE t2.user_id IS NULL
Note that both of these queries will not work if you have more than one of any user_date_out for a given user_id.

mysql query with group by and order by

I have a table like this:
.--------------------------------.
| Name | XX | Date |
.--------------------------------.
| N1 | 5 | 2009-05-01 |
| N1 | 10 | 2008-01-08 |
| N2 | 8 | 2009-02-02 |
.________________________________.
My result should be this:
.------------.
| Name | XX |
.------------.
| N1 | 5 |
| N2 | 8 |
.____________.
I just want the rows, grouped by Name but just the newest ones. I dont want the row with XX=10 in this example because the date 2009-05-01 > 2008-01-08.
I don't know how to sort by Date and group it :(
What do you want the grouping for?
select Name, XX
from yourTable t1
where Date = (select MAX(Date)
from yourTable t2
where t1.Name = t2.Name)
If you need to sum XX for the last day, then:
select Name, SUM(XX)
from yourTable t1
where Date = (select MAX(Date)
from yourTable t2
where t1.Name = t2.Name)
group by Name
SELECT Name, XX FROM myTable GROUP BY Name ORDER BY Date DESC
You may need to put quotes around your date field
`Date`
Sorry but these 2 are both wrong.
You should
SELECT Name, XX
FROM t1
JOIN (SELECT Name, max(date)
FROM t1
GROUP BY Name) subquery
ON t1.Name = subquery.Name AND t1.Date = subquery.Date