PL/SQL - Update Rows with Max Date Using Value from another table - sql

I have an Oracle database where I'm working with two tables, as shown below.
ITEMS
ITEM_ID | ITEM_DESC | ITEM_STATUS
============================================
1 | ITEM 1 | A
2 | ITEM 2 | A
3 | ITEM 3 | I
4 | ITEM 4 | A
ITEM_UPDATES
ITEM_ID | LAST_CHANGE | ITEM_STATUS
=============================================
1 | 1/21/2010 |
1 | 4/1/2015 |
2 | 1/21/2010 |
2 | 7/14/2016 |
3 | 1/21/2010 |
3 | 10/21/2011 |
3 | 11/15/2017 |
4 | 11/30/2010 |
We are wanting to change the way that ITEM_STATUS is tracked in this system, and I'm trying to move the ITEM_STATUS column to the ITEM_UPDATES table. Things that occur in the past don't matter and will likely have unique status, however I want to set ITEM_STATUS for each record with a MAX(LAST_CHANGE) for a given ID to the value of the ITEM_STATUS column currently in ITEMS. So basically, the finished table would look like this.
ITEM_UPDATES
ITEM_ID | LAST_CHANGE | ITEM_STATUS
=============================================
1 | 1/21/2010 |
1 | 4/1/2015 | A
2 | 1/21/2010 |
2 | 7/14/2016 | A
3 | 1/21/2010 |
3 | 10/21/2011 |
3 | 11/15/2017 | I
4 | 11/30/2010 | A
I have the query to select the proper data below, but I don't know how to translate this into an update statement given that I'm having to compare item_ids AND whether or not something is the max date record for that item. Is this doable?
SELECT ITEM_UPDATES.ITEM_ID, ITEMS.ITEM_STATUS, MAX(EFFECTIVE_DATE) AS MAX_DATE
FROM ITEM_UPDATES, ITEMS
WHERE ITEM_UPDATES.ITEM_ID = ITEMS.ITEM_ID
GROUP BY ITEM_UPDATES.ITEM_ID, ITEMS.ITEM_STATUS

So you want the status updated on the most recent item_updates record. You can do:
update item_updates iu
set item_status = (select i.item_status from items where i.item_id = iu.item_id)
where iu.effective_date = (select max(iu2.effective_date)
from item_updates iu2
where iu2.item_id = iu.item_id
);

Maybe:
update item_updates iup
set iup.item_status = (select item_status ist
from ist.item_id = iup.item_id)
where (iup.item_id, iup.last_change) = (select iup2.item_id, max(iup.last_change)
from item_updates iup2
where iup2.item_id = iup.item_id
group by iup2.item_id)
Now that I see Gordon Linoff's answer, I aks myself why I added the (already correlated) item_id...

Related

How to select the latest date for each group by number?

I've been stuck on this question for a while, and I was wondering if the community would be able to direct me in the right direction?
I have some tag IDs that needs to be grouped, with exceptions (column: deleted) that need to be retained in the results. After which, for each grouped tag ID, I need to select the one with the latest date. How can I do this? An example below:
ID | TAG_ID | DATE | DELETED
1 | 300 | 05/01/20 | null
2 | 300 | 03/01/20 | 04/01/20
3 | 400 | 06/01/20 | null
4 | 400 | 05/01/20 | null
5 | 400 | 04/01/20 | null
6 | 500 | 03/01/20 | null
7 | 500 | 02/01/20 | null
I am trying to reach this outcome:
ID | TAG_ID | DATE | DELETED
1 | 300 | 05/01/20 | null
2 | 300 | 03/01/20 | 04/01/20
3 | 400 | 06/01/20 | null
6 | 500 | 03/01/20 | null
So, firstly if there is a date in the "DELETED" column, I would like the row to be present. Secondly, for each unique tag ID, I would like the row with the latest "DATE" to be present.
Hopefully this question is clear. Would appreciate your feedback and help! A big thanks in advance.
Your results seem to be something like this:
select t.*
from (select t.*,
row_number() over (partition by tag_id, deleted order by date desc) as seqnum
from t
) t
where seqnum = 1 or deleted is not null;
This takes one row where deleted is null -- the most recent row. It also keeps each row where deleted is not null.
You need 2 conditions combined with OR in the WHERE clause:
the 1st is deleted is not null, or
the 2nd that there isn't any other row with the same tag_id and date later than the current row's date, meaning that the current row's date is the latest:
select t.* from tablename t
where t.deleted is not null
or not exists (
select 1 from tablename
where tag_id = t.tag_id and date > t.date
)
See the demo.
Results:
| id | tag_id | date | deleted |
| --- | ------ | ---------- | -------- |
| 1 | 300 | 2020-05-01 | |
| 2 | 300 | 2020-03-01 | 04/01/20 |
| 3 | 400 | 2020-06-01 | |
| 6 | 500 | 2020-03-01 | |

How can I do SQL query count based on certain criteria including row order

I've come across certain logic that I need for my SQL query. Given that I have a table as such:
+----------+-------+------------+
| product | valid | Date |
+----------+-------+------------+
| 1 | null | 2016-05-10 |
| 1 | null | 2016-05-09 |
| 1 | yes | 2016-05-08 |
+----------+-------+------------+
This table is produced by a simple query:
SELECT * FROM products WHERE product = 1 ORDER BY date desc
Now what I need to do is create a query to count the number of nulls for certain products by order of date until there is a yes value. So the above example the count would be 2 as there are 2 nulls until a yes.
+----------+-------+------------+
| product | valid | Date |
+----------+-------+------------+
| 2 | null | 2016-05-10 |
| 2 | yes | 2016-05-09 |
| 2 | null | 2016-05-08 |
+----------+-------+------------+
Above would return 1 as there is 1 null until a yes.
+----------+-------+------------+
| product | valid | Date |
+----------+-------+------------+
| 3 | yes | 2016-05-10 |
| 3 | yes | 2016-05-09 |
| 3 | null | 2016-05-08 |
+----------+-------+------------+
Above would return 0.
You need a Correlated Subquery like this:
SELECT COUNT(*)
FROM products AS p1
WHERE product = 1
AND Date >
( -- maximum date with 'yes'
SELECT MAX(Date)
FROM products AS p2
WHERE p1.product = p2.product
AND Valid = 'yes'
)
This should do it:
select count(1) from table where valid is null and date > (select min(date) from table where valid = 'yes')
Not sure if your logic provided covers all the possible weird and wonderful extreme scenarios but the following piece of code would do what you are after:
select a.product,
count(IIF(a.valid is null and a.date >maxdate,a.date,null)) as total
from sometable a
inner join (
select product, max(date) as Maxdate
from sometable where valid='yes' group by product
) b
on a.product=b.product group by a.product

SQL Update based on aggregate record set

I have a table with purchase orders:
po_line table
+--------+---------+-----------+
| po_num | po_line | date |
+--------+---------+-----------+
| 1 | 1 | 9/22/2013 |
| 1 | 2 | 9/22/2013 |
| 1 | 3 | 9/22/2013 |
| 2 | 1 | 9/21/2013 |
| 2 | 2 | NULL |
+--------+---------+-----------+
po table
+--------+-----------+
| po_num | confirmed |
+--------+-----------+
| 1 | NULL |
| 2 | NULL |
+--------+-----------+
For a given po, example po_num 1, I am wanting to update a value in table 2 to 'confirmed' if all the records have a date in them for those lines. Example 1 would populate confirmed. PO 2 would fail the criteria since line 2 has no date.
Do I need to use a cursor to do this? Running sql 2008 r2.
UPDATE po SET confirmed = 'confirmed'
FROM po T
WHERE
NOT T.po_num IN
(
SELECT po_num FROM po_line
WHERE po_date IS NULL
)
Alternatively, if you want to make sure that are entries for each po in the po_line table before confirming, you can use:
update po set confirmed = 'confirmed'
where po.po_num in (select po_num from
(select po_num, count(po_date) dated, count(*) total from po_line group by po_num) q
where dated=total)
as shown in http://sqlfiddle.com/#!6/b16988/8/0

Getting detail with the highest priority using Joins/subqueries

I hope you can help me with this problem: I have three tables, similar to this:
ORDER
Order_ID | Order_Date
=====================
1 | 01/01/2001
2 | 02/01/2001
3 | 03/01/2001
4 | 04/01/2001
5 | 05/01/2001
ORDER_DETAIL
Order_Detail_ID | Order_ID | Status_ID
======================================
1 | 1 | 1
2 | 1 | 1
3 | 1 | 2
4 | 2 | 2
5 | 2 | 3
6 | 3 | 3
7 | 3 | 3
STATUS
Status_ID | Status_Name | Status_Priority
=========================================
1 | PENDING | 3
2 | COMPLETED | 2
3 | CANCELLED | 1
Now, as I suppose it shows, each row in the ORDER_DETAIL table is related to the ORDER table using Order_ID, and it also has a status indicated by the Status_ID. Also, the STATUS table has a Status_Priority column. What I need to do is show each order, along with, among other columns, the status with highest priority among the order details each order has, like this:
Order_ID | Order_Date | Status_Name
===================================
1 | 01/01/2001 | PENDING
2 | 02/01/2001 | COMPLETED
3 | 03/01/2001 | CANCELLED
4 | 04/01/2001 |
5 | 05/01/2001 |
In this case, for example, since Order_ID 1 has at least 1 Order_Detail_ID with the PENDING status, which has the highest priority among the details it has, that's the one that appears. I tried using a JOIN with a subquery, based on a similar code I have, but I can't seem to adapt it to this case. Any help will be much appreciated. Thanks in advance.
Select columns, join tables by order_id, order by Status_Priority

Getting Sum of MasterTable's amount which joins to DetailTable

I have two tables:
1. Master
| ID | Name | Amount |
|-----|--------|--------|
| 1 | a | 5000 |
| 2 | b | 10000 |
| 3 | c | 5000 |
| 4 | d | 8000 |
2. Detail
| ID |MasterID| PID | Qty |
|-----|--------|-------|------|
| 1 | 1 | 1 | 10 |
| 2 | 1 | 2 | 20 |
| 3 | 2 | 2 | 60 |
| 4 | 2 | 3 | 10 |
| 5 | 3 | 4 | 100 |
| 6 | 4 | 1 | 20 |
| 7 | 4 | 3 | 40 |
I want to select sum(Amount) from Master which joins to Deatil where Detail.PID in (1,2,3)
So I execute the following query:
SELECT SUM(Amount) FROM Master M INNER JOIN Detail D ON M.ID = D.MasterID WHERE D.PID IN (1,2,3)
Result should be 20000. But I am getting 40000
See this fiddle. Any suggestion?
You are getting exactly double the amount because the detail table has two occurences for each of the PIDs in the WHERE clause.
See demo
Use
SELECT SUM(Amount)
FROM Master M
WHERE M.ID IN (
SELECT DISTINCT MasterID
FROM DETAIL
WHERE PID IN (1,2,3) )
What is the requirement of joining the master table with details when you have all your columns are in Master table.
Also, isnt there any FK relationhsip defined on these tables. Looking at your data it seems to me that there should be FK on detail table for MasterId. If that is the case then you do not need join the table at all.
Also, in case you want to make sure that you have records in details table for the records for which you need sum and there is no FK relationship. Then you could give a try for exists instead of join.