Update an Oracle table using another ungrouped table - sql

The scenario involves two tables with the following structures :
>table e1:
Id Name Addr
---------------------
1 Ramas
2 Sam
3 Musa
4 Sebi
>table e1_addr:
Id Addr Mod_Date
-----------------------
1 Gzb 10-05-2018
1 Dli 18-05-2018
2 Gzb 25-05-2018
2 Dli 22-05-2018
3 Gzb 09-05-2018
3 Dli 05-05-2018
4 Gzb 14-05-2018
4 Dli 24-05-2018
table e1 has 4 personnel with no updated addresses in address column.
table e_addr has updated address record for each id with mod_date column having the date the record was entered.
I need to update table e1 so that its addr column has the value from the e1_addr table with maximum mod_date.
On my part, I have tried to fetch addr from e1_addr table corresponding to the max(mod_date) but cannot achieve the desired result.
Desired output is as follows :
Final Table :
Id Name Addr
------------------
1 Ramas Dli
2 Sam Gzb
3 Musa Gzb
4 Sebi Dli
Any and all help would be apprecited.
Please inform if i should give in some more details.

You can do this with keep:
update e1
set addr = (select max(a.addr) keep (dense_rank first order by mod_date desc)
from ei_addr a
where e1.id = a.id
);
I would suggest that you not store the address in two places and simply fetch the latest address when you query the e1 table.

You can try this query to update Addr column.
windows function with ROW_NUMBER to get Rownumber is 1 on e1_addr,which mean MAX Mod_Date.
then update e1.addr.
UPDATE
(
SELECT t.addr as OLD,t2.addr as NEW
FROM e1 t
INNER JOIN
(
SELECT t2.*,ROW_NUMBER() OVER(PARTITION BY t2.ID ORDER BY t2.Mod_Date desc) rn
FROM e1_addr t2
) t2 on t.id = t2.id
where t2.rn = 1
) t
SET t.OLD = t.NEW
or another update query you can try subquery.
UPDATE e1 t1
SET t1.addr = (SELECT t2.addr
FROM
(
SELECT t2.*,ROW_NUMBER() OVER(PARTITION BY t2.ID ORDER BY t2.Mod_Date desc) rn
FROM e1_addr t2
) t2
WHERE t2.rn = 1 and t1.id = t2.id
)
sqlfiddle:http://sqlfiddle.com/#!4/e14f5/1

Couldn't resist, the MERGE syntax wasn't mentioned yet:
MERGE INTO e1 USING (SELECT ID, MAX(addr) KEEP (DENSE_RANK FIRST ORDER BY mod_date DESC) AS newest_addr
FROM e1_addr GROUP BY ID) q ON (e1.ID = q.ID)
WHEN MATCHED THEN UPDATE SET e1.addr = q.newest_addr;
This subquery selects the newest address for each id:
SELECT ID, MAX(addr) KEEP (DENSE_RANK FIRST ORDER BY mod_date DESC) AS newest_addr
FROM e1_addr GROUP BY ID;
ID newest_addr
1 Dli
2 Gzb
3 Gzb
4 Dli
... and MERGE looks up each id in the subquery, and if it finds a match, it updates the addr.
Some people love it, some hate it.

Try this out:
Step1: Take id and max date from address table
Step 2: Join on id and max date with the address table to get the address
Step 3 Join this table with my e1 table to get the final output
select p.*,q.addr
from
e1 p
left join
(
select a.id,b.addr
from
(select id,max(mod_date) as updated_date
from e1_addr
group by id ) a
left join
e1_addr b
on a.id = b.id and a.updated_date = b.mod_date
) q
on p.id = q.id;
Let me know in case of any concerns

Thank you everyone for all the answers and different ways you posted to solve the query while i found a much simpler way to do same after having a discussion with one of my juniors. I felt pity for myself.
update e1 a set a.addr=(select addr from e1_addr b
where mod_date= (select max(mod_date) from e1_addr
where id= a.id ));
Thanks for all the support guys!

Related

SQL Query to fetch a user without an associated service

I have the below data in a table. I am trying to fetch all the "Modem" users, who do not have an associated telephone service.
UserID DeviceNumber DeviceType DeviceRole
1 A Telephone SingleUser
1 A Modem MultiUser
1 B Modem MultiUser
2 C Telephone SingleUser
2 C Modem MultiUser
2 D Modem MultiUser
select distinct t.* from table t
join table t1 on t1.UserID= v.UserID
and t1.DeviceNumber <> t.DeviceNumber
and t.DeviceType = 'Modem';
I want to see DeviceNumber B and D in my output. But above query is not returning expected results.
Hmmm . . . One method would be:
select t.*
from t
where t.devicetype = 'Modem' and
not exists (select 1
from t t2
where t2.userid = t.userid and t2.devicenumber = t.devicenumber and
t2.devicetype = 'Telephone'
);
You can do by count. Here is the demo.
select
UserID,
DeviceNumber,
DeviceType,
DeviceRole
from
(
select
yt.*,
count(*) over (partition by DeviceNumber) as cnt
from yourTable yt
) val
where cnt = 1
and DeviceType = 'Modem'
Output:

sql query - update fields in existing with record values in the same table

Environment: SQL Server 2012
I have a transaction table that contains a group of records with group id column.
In the example illustrated below, group 2 records were copied from group 1 records, Except for the sideId, sideSort, topId and topSort columns. Is there a way to cascade that down from group 1 to group 2 for just topSort and sideSort? The hard part is that topId and sideId aren't the same because of Identity fields in parent tables.
Here is a sqlfiddle of the example
You can do it this way. This is based on the assumption that you copy all the records for one group (#copiedFromGroupId) to another (#copiedToGroupId), i.e. that the ID's will be shifted by the number of records in the first group.
declare #copiedFromGroupId int = 1
declare #copiedToGroupId int = 2
declare #shift int
select #shift = (select max(id) from Tracker where GroupId = #copiedToGroupId)
- (select max(id) from Tracker where GroupId = #copiedFromGroupId)
UPDATE T1
SET
T1.TopSort = T2.TopSort,
T1.SideSort = T2.SideSort
FROM Tracker T1
INNER JOIN Tracker T2 ON T1.ID = T2.ID + #shift
Check this SQL Fiddle
This is based on the row_number over the ORDER of TopId and SortID columns:
update test
set test.topSort = mix.topSort, test.sideSort = mix.sideSort
from
(select a.groupid aGroupid, b.groupID, b.Id bID, a.topSort, a.sideSort
from (select groupid,
row_number() over(order by topID, sideId) rn,
topSort, sideSort,
id
from test where groupid=1) a
inner join
(select groupid,
row_number() over(order by topID, sideId) rn,
topSort, sideSort,
id
from test where groupid=2) b ON a.rn = b.rn) mix
inner join test on test.id=mix.bId
WHERE test.groupid=2;
SQLFiddle
Assuming that Id is an order field in the Group:
WITH C as
(
select Tracker.*,
ROW_NUMBER() OVER (PARTITION BY GroupId ORDER BY Id) as RN
FROM Tracker
)
UPDATE CT
SET CT.topSort=CTU.topSort,
CT.sideSort=CTU.sideSort
FROM C as CT
JOIN C as CTU ON (CT.rn=CTU.rn)
and (CTU.GroupID=1)
WHERE CT.GroupID=2
SQLFiddle demo

SQL Update Skipping duplicates

Table 1 looks like the following.
ID SIZE TYPE SERIAL
1 4 W-meter1 123456
2 5 W-meter2 123456
3 4 W-meter 585858
4 4 W-Meter 398574
As you can see. Items 1 and 2 both have the same Serial Number. I have an innerjoin update statement that will update the UniqueID on these devices based on linking their serial number to the list.
What I would like to do. Is modify by hand the items with duplicate serial numbers and scripted update the ones that are unique. Im presuming I have to reference the distinct command here somewhere buy not sure.
This is my update statement as is. Pretty simple and straight forward.
update UM00400
Set um00400.umEquipmentID = tb2.MIUNo
from UM00400 tb1
inner join AA_Meters tb2 on
tb1.umSerialNumber = tb2.Old_Serial_Num
where tb1.umSerialNumber <> tb2.New_Serial_Num
;WITH CTE
AS
(
SELECT * , rn = ROW_NUMBER() OVER (PARTITION BY SERIAL ORDER BY SERIAL)
FROM UM00400
)
UPDATE CTE
SET CTE.umEquipmentID = tb2.MIUNo
inner join AA_Meters tb2
on CTE.umSerialNumber = tb2.Old_Serial_Num
where tb1.umSerialNumber <> tb2.New_Serial_Num
AND CTE.rn = 1
This will update the 1st record of multiple records with the same SERIAL.
If i understand your question correctly below query will help you out :
;WITH CTE AS
(
// getting those serial numbers which are not duplicated
SELECT umSerialNumber,COUNT(umSerialNumber) as CountOfSerialNumber
FROM UM00400
GROUP BY umSerialNumber
HAVING COUNT(umSerialNumber) = 1
)
UPDATE A SET A.umEquipmentID = C.MIUNo
FROM UM00400 A
INNER JOIN CTE B ON A.umSerialNumber = B.umSerialNumber
INNER JOIN AA_Meters C ON A.umSerialNumber = C.Old_Serial_Num

Selecting max value from 2nd table in first table results

I have 2 tables as below-
Table I
ID DATE
1 05/11/12
2 23/11/12
3 29/11/12
4 04/10/12
5 20/11/12
And another table (IH) with the following info-
ID RECNO NOTE
1 1 Open
1 2 Update
1 3 Close
2 1 Open
2 2 Update
2 3 Hold
2 4 Close
3 1 Open
4 1 Open
4 2 Update
5 1 Open
I would like to output a result as shown below, displaying the Note field using the highest value of RecNo for each ID. So using the data above the output should be-
ID DATE NOTE
2 23/11/12 Close
3 29/11/12 Open
The code I have is-
SELECT I.ID, I.DATE, IH.NOTE FROM
I I, IH IH
JOIN (SELECT MAX([RECNO]) [RECNO] FROM
IH
GROUP BY RECNO) IH2 ON IH2.ID = IH.ID AND
IH2.[RECNO] = IH.[RECNO]
JOIN I I2 ON I2.ID = IH.ID WHERE
(I2.DATE>={TS ‘2012-11-22 00:00:002}) GROUP BY I2.ID
However when I execute the code I get-
Invalid Column Name 'RECNO'. Statement(s) could not be prepared.
How about this? Note, haven't tried it, I'm on my Mac at the moment.
SELECT I.ID, I.DATE, IH.NOTE
FROM I I
OUTER APPLY
(SELECT TOP 1 *
FROM IH
WHERE IH.ID = I.ID
ORDER BY RECNO DESC) IH
WHERE I.DATE >= '2012-11-22'
Your SQL is rather, uh, messy.
Assuming you are using SQL Server 2005 or greater, you can use the row_number() function, as follows:
SELECT I.ID, I.DATE, IH.NOTE
FROM I join
(select ih.*, ROW_NUMBER() over (PARTITION by id order by recno desc) as seqnum
from IH
) ih
on IH2.[RECNO] = IH.[RECNO] and seqnum = 1
WHERE I2.DATE>='2012-11-22 00:00:002'
This is assigning a sequence number in the IH table, for each id with the highest record number getting the value "1". The rest is just SQL.
Your original query is simply not correct syntactically, but I think this is what you want based on the description.
and another one
SELECT I.ID, I.DATE
,(Select TOP 1 IH.NOTE FROM IH where IH.ID=i.ID Order by Recno DESC) as Note
from I
WHERE
I.DATE>'20121122'
maybe this will help
SELECT a.ID, a.DATE, b.NOTE FROM a
inner join b on a.ID = b.ID
where b.recno in (select max(bb.recno)
from b as bb where bb.id = b.id)
http://sqlfiddle.com/#!3/fd141/2
If you don't mind the different identifiers, look at this solution:
select t1.MyID, t1.MyDate, y.Note
from t1
join
(
select MyID, max(RecNo) as RecNo
from t2
group by MyID
) x
on t1.MyID = x.MyID
left join
(
select *
from t2
) y
on t1.MyID = y.MyID
and x.RecNo = y.RecNo
where t1.MyDate >= '2012.11.22'
The complete solution is here: http://sqlfiddle.com/#!3/4ca09/3
Update: Oops, forgot to bring in the date in where clause. Updated SQL Fiddle and the query above.

SQL JOIN Statement

Lets say I have a table e.g
Request No. Type Status
---------------------------
1 New Renewed
and then another table
Action ID Request No LastUpdated
------------------------------------
1 1 06-10-2010
2 1 07-14-2010
3 1 09-30-2010
How can I join the second table with the first table but only get the latest record from the second table(e.g Last Updated DESC)
SELECT T1.RequestNo ,
T1.Type ,
T1.Status,
T2.ActionId ,
T2.LastUpdated
FROM TABLE1 T1
JOIN TABLE2 T2
ON T1.RequestNo = T2.RequestNo
WHERE NOT EXISTS
(SELECT *
FROM TABLE2 T2B
WHERE T2B.RequestNo = T2.RequestNo
AND T2B.LastUpdated > T2.LastUpdated
)
Using aggregates:
SELECT r.*, re.*
FROM REQUESTS r
JOIN REQUEST_EVENTS re ON re.request_no = r.request_no
JOIN (SELECT t.request_no,
MAX(t.lastupdated) AS latest
FROM REQUEST_EVENTS t
GROUP BY t.request_no) x ON x.request_no = re.request_no
AND x.latest = re.lastupdated
Using LEFT JOIN & NOT EXISTS:
SELECT r.*, re.*
FROM REQUESTS r
JOIN REQUEST_EVENTS re ON re.request_no = r.request_no
WHERE NOT EXISTS(SELECT NULL
FROM REQUEST_EVENTS re2
WHERE re2.request_no = r2.request_no
AND re2.LastUpdated > re.LastUpdated)
SELECT *
FROM REQUEST, ACTION
WHERE REQUEST.REQUESTNO = ACTION.REQUESTNO --Joining here
AND ACTION.LastUpdated = (SELECT MAX(LastUpdated) FROM ACTION WHERE REQUEST.REQUESTNO = ACTION.REQUESTNO);
A sub-query is used to get the last updated record's date and matches against itself to prevent the other records being joined.
Granted, depending on how precise the LastUpdated field is, it can have problems with two records being updated on the same date, but that is a problem encountered in any other implementation, so the precision would have to be increased or some other logic would have to be in place or another distinguishing characteristic to prevent multiple rows being returned.
SELECT r.RequestNo, r.Type, r.Status, a.ActionID, MAX(a.LastUpdated)
FROM Request r
INNER JOIN Action a ON r.RequestNo = a.RequestNo
GROUP BY r.RequestNo, r.Type, r.Status, a.ActionID
We can use the operation Top 1 with ORDER BY clause. For instance, if your tables are RequestTable(ID,Type,Status) and ActionTable(ActionID,RequestID,LastUpdated), the query will be like this:
Select Top 1 rq.ID, rq.Status, at.ActionID
From RequestTable as rq
JOIN ActionTable as at ON rq.ID = at.RequestID
Order by at.LastUpdated DESC