SQL get latest availability per member - sql

I have a situation where I store in a table each member's availability.
It's a simple table with 4 column.
CREATE TABLE availablities (
availablity_id serial PRIMARY KEY,
member_id serial,
availablity_status_id serial,
start_date timestamp
);
Each member can have multiple records in the table and to get the current status
I get for each member the record that has the most recent start_date that is smaller then now().
I first tried with a naive Max() and Group by query
select
status_code, max(start_date) start_date,availablities.member_id
from
availablities
join
availablity_status on availablity_status.availablity_status_id = availablities.availablity_status_id
where
start_date <= now()
group by
status_code,availablities.member_id;
But this return multiple records per user as I get the most recent record by user and by status.
I finally came up with a query that gives me the expected result.
select status_code,start_date,a2.member_id from availablities a2
join availablity_status on availablity_status.availablity_status_id = a2.availablity_status_id
where a2.availablity_id in(
select
max(availablity_id)
from availablities a
where
a.member_id = a2.member_id and
start_date in(
select
max(start_date) start_date
from availablities
where
start_date <= now()
and a.member_id = availablities.member_id
)
);
But this query takes 60 times longer to execute and doesn't feel right.
I'm pretty sure there must be a better solution but I can't get my hands on it.
What is the correct way to get the expected result?
I've created a DB-fiddle to make it easier to see. Query 1 is incorrect and Query 2 is much slower when we have a couple more data.
https://www.db-fiddle.com/f/iWgvuj8kcms9F5CKuoKsny/2

It looks like you need to use a simple row_number window function here:
with a as (
select *, Row_Number() over(partition by member_id order by start_date desc, availablity_id desc) rn
from availablities
where start_date<now()
)
select s.status_code, a.start_date, a.member_id
from a join availablity_status s on s.availablity_status_id=a.availablity_status_id
where rn=1
Note your data is not selective enough, so for member_id 3, is it available or not? What is the most recent date when there are two identical dates?
I added a tie-breaker to also sort by availability_id to get your expected results
Actually it's availablity_id - you seem to have a common typo here!
See your updated Fiddle

Related

Can I avoid joining the same table multiple times?

Is there a way to improve the following query?
I would need an optimized version of the following query.
The reason I'm joining the Date_Table multiple times is because the ID and date_value columns are not in ascending order.
ie
ID = 1, date_value = '2022-09-07'; ID = 2, date_value = '2022-02-02'; ID = 3, date_value = '2022-11-12';
Sample data:
The maximum Date from the Agreements table is calculated based on the Date_Table.date_value column. The query will only return a row. In this case, the row highlighted in green will be the result.
Thank you so much!
SELECT * FROM Agreement
WHERE
dim_date_id = (
SELECT
Date_Table.ID
FROM (
SELECT
MAX(Date_Table.date_value) AS date_value
FROM Agreement
INNER JOIN Date_Table
ON Agreement.DIM_DATE_ID = Date_Table.ID
) AS last_day
INNER JOIN Date_Table
ON last_day.date_value = Date_Table.date_value
);
If Agreement is a large table, you should first find all the distinct date_ids, then join it to Date_Table. Also use a rank() windowing function to find the id of the most recent record:
Select Agreement.* From Agreement Inner Join (
Select ID From (
Select Date_Table.ID
,rank() Over (Order by Date_Table.date_value desc) as recent
From Date_Table Inner Join (
Select Distinct Dim_Date_ID as ID From Agreement
) A On A.ID=Date_Table.ID
) where recent=1
) X On Agreement.DIM_DATE_ID = X.ID
On first glance this looks just as complicated as your original query. But it quickly reduces the Agreement results to only a list of date ids, and especially if that field is indexed it is a fast query. Date_Table is then Inner Joined to find the best (most recent) Date_Value using a rank() function. The whole thing is filtered to retain only one record, the most recent, and that date_id is used to filter Agreement.
Again, I recommend that you index Agreement.Dim_Date_ID to make this query perform well.

Max of a Date field into another field in Postgresql

I have a postgresql table wherein I have few fields such as id and date. I need to find the max date for that id and show the same into a new field for all the ids. SQLFiddle site was not responding so I have an example in the excel. Here is the screenshot of the data and the output for the table.
You could use the windowing variant of max:
SELECT id, date, MAX(date) OVER (PARTITION BY id)
FROM mytable
Something like this might work:
WITH maxdts AS (
SELECT id, max(dt) maxdt FROM table GROUP BY id
)
SELECT id, date, maxdt FROM table t, maxdts m WHERE t.id = m.id;
Keep in mind without more information that this could be a horribly inefficient query, but it will get you what you need.

select multiple records based on order by

i have a table with a bunch of customer IDs. in a customer table is also these IDs but each id can be on multiple records for the same customer. i want to select the most recently used record which i can get by doing order by <my_field> desc
say i have 100 customer IDs in this table and in the customers table there is 120 records with these IDs (some are duplicates). how can i apply my order by condition to only get the most recent matching records?
dbms is sql server 2000.
table is basically like this:
loc_nbr and cust_nbr are primary keys
a customer shops at location 1. they get assigned loc_nbr = 1 and cust_nbr = 1
then a customer_id of 1.
they shop again but this time at location 2. so they get assigned loc_nbr = 2 and cust_Nbr = 1. then the same customer_id of 1 based on their other attributes like name and address.
because they shopped at location 2 AFTER location 1, it will have a more recent rec_alt_ts value, which is the record i would want to retrieve.
You want to use the ROW_NUMBER() function with a Common Table Expression (CTE).
Here's a basic example. You should be able to use a similar query with your data.
;WITH TheLatest AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY group-by-fields ORDER BY sorting-fields) AS ItemCount
FROM TheTable
)
SELECT *
FROM TheLatest
WHERE ItemCount = 1
UPDATE: I just noticed that this was tagged with sql-server-2000. This will only work on SQL Server 2005 and later.
Since you didn't give real table and field names, this is just psuedo code for a solution.
select *
from customer_table t2
inner join location_table t1
on t1.some_key = t2.some_key
where t1.LocationKey = (select top 1 (LocationKey) as LatestLocationKey from location_table where cust_id = t1.cust_id order by some_field)
Use an aggregate function in the query to group by customer IDs:
SELECT cust_Nbr, MAX(rec_alt_ts) AS most_recent_transaction, other_fields
FROM tableName
GROUP BY cust_Nbr, other_fields
ORDER BY cust_Nbr DESC;
This assumes that rec_alt_ts increases every time, thus the max entry for that cust_Nbr would be the most recent entry.
By using time and date we can take out the recent detail for the customer.
use the column from where you take out the date and the time for the customer.
eg:
SQL> select ename , to_date(hiredate,'dd-mm-yyyy hh24:mi:ss') from emp order by to_date(hiredate,'dd-mm-yyyy hh24:mi:ss');

Selecting a column that is not an aggregate or in group

The goal of this select is to get the latest score for a system that is in status = 'FD'. I want to get the ID of the row (id), the system ID (sys_id), and the score (score).
The following SQL gives me the id of the system (sys_id) as well as the score (score), but I also would like to get the id column associated with this score and sys_id. Hopefully that makes sense.
select sys_id, score from example
where (sys_id, end_date) in
(
select sys_id, max (end_date)
from example
where status = 'FD'
group by sys_id
);
Here is a SQL Fiddle to give you an idea of what I am talking about http://www.sqlfiddle.com/#!4/169a2/3
Before you ask, yes the combination of sys_id and end_date would give me a unique row and I could find the id that way, but I would rather get the id in my select statement.
You can use a simple CTE to get the max date for each SYS_ID, and join that back to your table to get all the details for that particular record.
with CTE as (
select sys_id, max (end_date) as MaxDate
from example
where status = 'FD'
group by sys_id)
select
EXAMPLE.*
from
EXAMPLE
INNER JOIN CTE
ON EXAMPLE.SYS_ID = CTE.SYS_ID
and EXAMPLE.END_DATE = CTE.MaxDate
Check out the change to your SQL Fiddle
answer from comment. SUbquery a is from your statement...lazy programming on my part.
select a.*, e.score
from
(
select sys_id, max (end_date) as 'ed'
from example
where status = 'FD'
group by sys_id
)a
inner join example e on a.ed = e.end_date and a.sys_id = e.sys_ID
Works on the predicate that there is only one unqiue value for a given sys_id and end date. Multiple end dates will return multiple rows in a cross join format.

SQL - Group By unique column combination

I am trying to write a script that will return the latest values for a unique documentid-physician-patient triplet. I need the script to act similar to a group by statement, except group by only works with one column at a time. I need to date and status information for only the most recent unique triplet. Please let me know what you will need to see from me to help. Here is the current, very bare, statement:
SELECT
TransmissionSend.CreateTimestamp,
TransmissionSendItem.Status,
TransmissionSendItem.PhysicianId,
TransmissionSendItem.DocumentIdDisplay,
Utility.SqlFunctions_NdnListToAccountList(TransmissionSendItem.NdocNum) AS AccountNum
FROM
Interface_SFAX.TransmissionSend,
Interface_SFAX.TransmissionSendItem
WHERE
TransmissionSend.ID = TransmissionSendItem.childsub --I don't know exactly what this does, I did not write this script. It must stay here though for the exact results.
ORDER BY TransmissionSend.CreateTimestamp DESC -- In the end, each latest result of the unique triplet will be ordered from most recent to oldest in return
My question is, again, how can I limit results to only the latest status for each physician id, document id, and account number combination?
First select the MAX(date) with the documentid GROUP BY documentid then select all data from the table by the first select result for example with an inner join.
SELECT table.additionalData, J.id, J.date
FROM table
INNER JOIN (SELECT id, MAX(date) AS date
FROM table GROUP BY id) AS J
ON J.id = table.id
AND J.date /* this is the max date */ = table.date