find all rows after the recent update using oracle - sql

I tried below query to bring all rows after last Action="UNLOCKED", but ORDER BY is not allowed in subquery it seems.
SELECT *
FROM TABLE
WHERE id >= (SELECT MAX(id)
FROM TABLE
WHERE ACTION='UNLOCKED' AND action_id=123
ORDER BY CREATE_DATE DESC);
Sample data
Id action_id Action ... CREATE_DATE
1 123 ADD 03/18/2018
2 123 Unlocked 03/19/2018
3 123 Updated1 03/19/2018
4 123 Updated2 03/19/2018
5 123 Unlocked 03/20/2018
6 123 Updated3 03/20/2018
7 123 Updated4 03/20/2018
Output should be rows with id 5,6,7. What should i use to get this output

you could use an inner join on subselect for max create_date
select * from TABLE
INNER JOIN (
select max(CREATE_DATE) max_date
from TABLE
where Action = 'Unlocked' ) T on t.max_date = TABLE.CREATE_DATE

You need not order the inner query because it will return only one value. You can do it as follows
SELECT * FROM TABLE WHERE id >= (select max(id) from TABLE where ACTION='UNLOCKED' and action_id=123);

Related

Ignoring a primary key in a SQL delete where exists statement

Last CTE based SQL question for today, I promise!
I have a CTE and it returns a couple of rows:
WITH myCte AS
( ... )
SELECT 123 AS PersonId, DeviceId
FROM myCte
would return:
PersonId | DeviceId
---------+---------
123 4
123 8
I tried to use the results in a delete statement so I could get rid of the respective rows in another table PersonHasDevice
pk | PersonId | DeviceId
---+----------+---------
1 123 4 - to delete
2 123 5
3 123 8 - to delete
4 991 8
So I appended the delete bit to the CTE select:
WITH myCte AS
( ... )
DELETE FROM PersonHasDevice
WHERE EXISTS (SELECT 123 AS PersonId, DeviceId FROM myCte)
But this just deletes everything from the PersonHasDevice table.
I'm not sure if it's because I'm not taking into account the PK on the table - but then if that were the case surely it shouldn't delete anything?!
You need to "connect" the CTE to your table. You can do this using exists or using a join:
with myCte as ( ... )
delete phd
from PersonHasDevice phd join
myCte
on phd.PersonId = myCte.PersonId and
phd.DeviceId = myCte.DeviceId
Edit by OP
Just reiterating that the hardcoded value 123 in my original query is easily substituted here:
with myCte as ( ... )
delete phd
from PersonHasDevice phd join
myCte
on phd.PersonId = 123 and
phd.DeviceId = myCte.DeviceId
This is the correct syntax if you want to use EXISTS:
WITH myCte AS (.....)
DELETE p FROM PersonHasDevice p
WHERE p.PersonId = 123
AND EXISTS (
SELECT 1
FROM myCte m
WHERE p.DeviceId = m.DeviceId
);

Selecting values in columns based on other columns

I have two tables, info and transactions.
info looks like this:
customer ID Postcode
1 ABC 123
2 DEF 456
and transactions looks like this:
customer ID day frequency
1 1/1/12 3
1 3/5/12 4
2 4/6/12 2
3 9/9/12 1
I want to know which day has the highest frequency for each postcode.
I know how to reference from two different tables but im not too sure how to reference multiple columns based on their values to other columns.
The output should be something like this:
customer ID postcode day frequency
1 ABC 123 3/5/12 4
2 DEF 456 4/6/12 2
3 GHI 789 9/9/12 1
and so on.
You can filter with a correlated subquery:
select
i.*,
t.day,
t.frequency
from info i
inner join transactions t on t.customerID = i.customerID
where t.frequency = (
select max(t.frequency)
from info i1
inner join transactions t1 on t1.customerID = i1.customerID
where i1.postcode = i.postcode
)
Or, if your RBDMS supports window functions, you can use rank():
select *
from (
select
i.*,
t.day,
t.frequency,
rank() over(partition by i.postcode order by t.frequency desc)
from info i
inner join transactions t on t.customerID = i.customerID
) t
where rn = 1

Get most recent records where one field is not null

I'm looking to narrow down my database to have only the most records. The most recent records need to have a value in a specific field.
ID Account_nbr Date Name
1 622 7/10/2018 Stu
2 622 7/24/2018
3 151 7/18/2018 Taylor
4 151 7/24/2018 Taylor
This is an example of the database.
I want the code to do this:
ID Account_nbr Date Name
1 622 7/10/2018 Stu
4 151 7/24/2018 Taylor
I have tried the following code:
Select m.*
FROM [table] m
INNER JOIN
(
SELECT last(Date) as LatestDate
,account_nbr
FROM [table]
WHERE Name IS NOT NULL
GROUP BY account_nbr
) b
ON m.Date = b.LatestDate
AND m.account_nbr = b.account_nbr
The output only included the most recent date and did not take into account records that were null in the name field.
I would do :
select t.*
from table as t
where t.name is not null and
t.date = (select max(t1.date)
from table as t1
where t1.account_nbr = t.account_nbr
);
Try this:
Select
m.*
From
[table] As m
Where
m.[Date] In
(Select Max([Date])
From [table] As T
Where T.[Name] Is Not Null
And T.account_nbr = m.account_nbr)

Getting the most recent data from a dataset based on a date field

This seems easy, but I can't get it. Assume this dataset:
ID SID AddDate
1 123 1/1/2014
2 123 2/3/2015
3 123 1/4/2010
4 124
5 124
6 125 2/3/2012
7 126 2/2/2012
8 126 2/2/2011
9 126 2/2/2011
What I need is the most recent AddDate and the associated ID for each SID.
So, my dataset should return IDs 2, 5, 6 and 7
I tried doing a max(AddDate), but it won't give me the proper ID that's associated with it.
My SQL string:
SELECT First(Table1.ID) AS FirstOfID, Table1.SID, Max(Table1.AddDate) AS MaxOfAddDate
FROM Table1
GROUP BY Table1.SID;
You can use a subquery that returns the Maximum add date for each Sid, then you can join back this subquery to the dataset table:
SELECT
MAX(id)
FROM
ds INNER JOIN (
SELECT Sid, Max(AddDate) AS MaxAddDate
FROM ds
GROUP BY ds.Sid) mx
ON ds.Sid = mx.Sid AND (ds.AddDate=mx.MaxAddDate or MaxAddDate IS NULL)
GROUP BY
ds.Sid
the join still has to succeed if the MaxAddDate is NULL (there's no AddDate), and in case there are multiple ID that matches, it looks like you want the biggest one.
You can change your query to get the grouping first and then perform a JOIN like
SELECT First(Table1.ID) AS FirstOfID,
Table1.SID, xx.MaxOfAddDate
FROM Table1 JOIN (
SELECT ID, Max(AddDate) AS MaxOfAddDate
FROM Table1
GROUP BY SID) xx ON Table1.ID = xx.ID;
Try
select SID from table1
where addDate in (select max(addDate) from Table1)

SQL Select only rows where foreign key has an attribute

I have this table:
| id | Reader id | Book id | Taken date | Return date |
And, for example, 3 rows
id Reader_id Book_id Taken_date Return_date
1 1 1 1999-01-08 NULL
2 2 2 2015-03-09 2015-04-10
3 1 3 2013-01-01 2014-01-01
I need to get the id's of the readers who have returned books, so all the rows with that id in this table need to have Return_date != NULL. In the case above the query is supposed to return only the second row and not return the last row, because the reader from the last row has not returned a book. How to achieve that?
First identify the Reader_id who has to return books
SELECT Reader_id
FROM yourtable
WHERE Return_date IS NULL
Then select the readers from which is not present in above query result
Use NOT IN
SELECT *
FROM yourtable
WHERE Reader_id NOT IN (SELECT Reader_id
FROM yourtable
WHERE Return_date IS NULL)
Or use Not Exists which can handle NULL values from Sub-Query
SELECT *
FROM yourtable a
WHERE NOT EXISTS (SELECT 1
FROM yourtable b
WHERE b.Return_date IS NULL
AND a.Reader_id = b.Reader_id)
You can do this with a left join :
SELECT * FROM YourTable t
LEFT OUTER JOIN YourTable s
ON(t.reader_id = s.reader_id
and s.id <> t.id
and s.Return_date is null)
WHERE s.id is null
AND t.Return_date is not null
Try this...
select
reader_id
from
tablename
where id not in (SELECT id
FROM tablename
WHERE Return_date IS NULL)