How to improve simple SQL query by using where exists statement? - sql

Could you please help me out on improving simple query below.
Based on a last modified date (ODS_CHANGE_DATE), I set a 'ODS_CHANGE_FLAG' to 'Y' if it was modified in the last two months (done in previous query not shown here).
Then I want to set a RELOAD_INDICATOR flag to 'Y' for all records that were created in those months (based on CREATION_DATE). This way I can send all records of those months where at least 1 record was modified in the past two months.
The query below does the trick but I believe it can be optimised using a where exist statement but I am not familiar with it and I can not get it to work for the example below. Would you be willing to help me out so I can do it in one statement without using temp table?
Thanks!
use ODS
update ODS_DCF_OUTPUT set reload_indicator = 'N'
drop table #dcf_output_changed
select concat(DATEPART(year, creation_date),DATEPART(month, creation_date)) as RELOAD_MONTH_CODES
into #dcf_output_changed
from ODS_DCF_OUTPUT
where ODS_CHANGE_FLAG = 'Y'
update ODS_DCF_OUTPUT
set ODS_DCF_OUTPUT.reload_indicator = 'Y'
FROM ODS_DCF_OUTPUT as a
inner join #dcf_output_changed as b on b.RELOAD_MONTH_CODES = concat(DATEPART(year, a.CREATION_DATE),DATEPART(month, a.CREATION_DATE))

I believe your query could be simplified without the temporary table - as long as 'ODS_CHANGE_FLAG' is set to 'Y' in another query - as follows:
update ODS_DCF_OUTPUT set reload_indicator = 'N' ;
UPDATE ODS_DCF_OUTPUT
SET a.reload_indicator = 'Y'
FROM ODS_DCF_OUTPUT a
WHERE
concat(DATEPART(year, a.CREATION_DATE),DATEPART(month, a.CREATION_DATE))
IN (
SELECT DISTINCT
concat(DATEPART(year, b.CREATION_DATE),DATEPART(month, b.CREATION_DATE))
FROM ODS_DCF_OUTPUT b WHERE b.ODS_CHANGE_FLAG = 'Y'
)
Make sure to take backup before messing with your data.

Related

Multiple Columns and different Join Conditions for Oracle update

I have 2 tables , 1 is location and the Other one is Look up table. I have to look into the look up table for the location values and if they are present mark them as 'Y' and 'N' along with their corresponding values
I have written individual update Statements as below:
**Location1,L1value**
Update Location
set (Location1,L1value) =
(select UPPER(VAlue),'Y' from Location_lookup where trim(Location1)=Location
where exists (select 1 from Location_lookup where trim(Location1)=Location);
commit;
**Location2,value**
Update Location
set (Location2,L2value) =
(select UPPER(VAlue),'Y' from Location_lookup where trim(Location2)=Location
where exists (select 1 from Location_lookup where trim(Location2)=Location);
commit;
Similarly for 3rd flag and value.
Is there a way to write single update for all the three conditions? Reason why I am looking for single update is that I have 10+ million records and I do not want to scan the records three different times. The lookup table has > 32 million records.
Here is a solution which uses Oracle's bulk FORALL ... UPDATE capability. This is not quite as performative as a pure SQL solution but it is simpler to code and the efficiency difference probably won't matter much for 10 million rows on a modern enterprise server, especially if this is a one-off exercise.
Points to note:
You don't say whether LOCATION has a primary key. For this answer I have assumed it has an ID column. The solution won't work if there isn't a primary key, but if your table doesn't have a primary key you've likely got bigger problems.
Your question mentions setting the FLAG columns "as 'Y' and 'N'" but the required output only shows 'Y' setting. I have included processing for 'N' but see the coda underneath.
declare
cursor get_locations is
with lkup as (
select *
from location_lookup
)
select locn.id
,locn.location1
,upper(lup1.value) as l1value
,nvl2(lup1.value, 'Y', 'N') as l1flag
,locn.location2
,upper(lup2.value) as l2value
,nvl2(lup2.value, 'Y', 'N') as l2flag
,locn.location3
,upper(lup3.value) as l3value
,nvl2(lup3.value, 'Y', 'N') as l3flag
from location locn
left outer join lkup lup1 on trim(locn.location1) = lup1.location
left outer join lkup lup2 on trim(locn.location2) = lup2.location
left outer join lkup lup3 on trim(locn.location3) = lup3.location
where lup1.location is not null
or lup2.location is not null
or lup3.location is not null;
type t_locations_type is table of get_locations%rowtype index by binary_integer;
t_locations t_locations_type;
begin
open get_locations;
loop
fetch get_locations bulk collect into t_locations limit 10000;
exit when t_locations.count() = 0;
forall idx in t_locations.first() .. t_locations.last()
update location
set l1value = t_locations(idx).l1value
,l1flag = t_locations(idx).l1flag
,l2value = t_locations(idx).l2value
,l2flag = t_locations(idx).l2flag
,l3value = t_locations(idx).l3value
,l3flag = t_locations(idx).l3flag
where id = t_locations(idx).id;
end loop;
close get_locations;
end;
/
There is a working demo on db<>fiddle here. The demo output doesn't exactly match the sample output posted in the query, because that doesn't the given input data.
Setting flags to 'Y' or 'N'?
The code above uses left outer joins on the lookup table. If a row is found the NVL2() function will return 'Y' otherwise it returns 'N'. This means the flag columns are always populated, regardless of whether the value columns are. The exception is for rows which have no matches in LOCATION_LOOKUP for any location (ID=4000 in my demo). In this case the flag columns will be null. This inconsistency follows from the inconsistencies in the question.
To resolve it:
if you want all flag columns to be populated with 'N' remove the WHERE clause from the get_locations cursor query.
if you don't want to set flags to 'N' change the NVL2() function calls accordingly: nvl2(lup1.value, 'Y', null) as l1flag

Need to update data from another database using db link

I have a table A with null dates (CREATED_ON_DT) in BI database. I need to update those nulls with the right dates from AFLDEV DB using a DB link mtl_system_items_b#afldev. Common key is inventory_item_id in AFLDEV and integration_id in BI DB. I have framed the following query but it does not work:
UPDATE w_product_d
SET w_product_d.CREATED_ON_DT = (SELECT min(creation_date)
FROM mtl_system_items_b#afldev B
where to_char(B.inventory_item_id)=w_product_d.integration_id
and B.organization_id = '102'
AND w_product_d.CREATED_ON_DT IS NULL
and w_product_d.integration_id in (SELECT T.integration_id
FROM (SELECT * FROM w_product_d ORDER BY w_product_d.integration_id )T
WHERE T.CREATED_ON_DT IS NULL)
);
If I run this query it updates all the dates to nulls but I need the opposite to happen i.e. replace null with the right dates.
Please help me out with this! I am doing this on SQL Developer for Oracle DB.
I think you've gotten all tied up between the rows you're updating and the rows you're using to update the column values with.
If you think about it, you're wanting to update rows in your w_product_d table where the created_on_dt is null, which means that your update statement will have a basic structure of:
update w_product_d wpd
set ...
where wpd.created_on_dt is null;
Once you have that, it's easy then to slot in the column you're updating and what you're updating it with:
update w_product_d wpd
set wpd.created_on_dt = (select min(creation_date)
from mtl_system_items_b#afldev b
where to_char(b.inventory_item_id) = wpd.integration_id)
where wpd.created_on_dt is null;

Find multiple SQL columns and update based on defined data listed in query

I have an update query in which I am trying to locate data in a column from a single table. All while taking other defined data listed in the query to update another column in the same table once a match has been found with that original search. Below is an example of my update statement. My end goal is to find '003447710' then update AltId to '540112'
UPDATE Site
SET AltId = ('540112'
'540129'
'540142'
'540143')
WHERE CCMFStatus in ('003447710',
'002754540',
'003564370',
'005942870')
I am sure there may already be something like this out there but I am really having trouble on an easy method on how to do this quickly and accurately.
Try this
update site
set altid = a.altid
from
(select altid,CCMFstatus from site) as a
where site.CCMFstatus = a.CCMFstatus
The best way might be multiple update statements:
UPDATE Site
SET AltId = '540112'
WHERE CCMFStatus = '003447710';
And so on.
If not, you can do this with a giant case statement or a join:
WITH values as (
SELECT '003447710' as oldstatus, '540112' as newaltid UNION ALL
SELECT '002754540', '540129' UNION ALL
SELECT '003564370', '540142' UNION ALL
SELECT '005942870', '540143'
)
UPDATE s
SET AltId = va.newaltid
FROM site s JOIN
values v
ON s.CCMFStatus = v.oldstatus;
If you already have the values in a table, then you don't need the WITH statement. You can just use the table.
Have you tried using CASE statement?
UPDATE SITE SET AltID = (CASE
WHEN CCMFStatus = '003447710' THEN '540112'
WHEN CCMFStatus = '002754540' THEN '540129'
END)
WHERE
CCMFStatus in ('003447710', '002754540', '003564370', '005942870');
BR,

How can I update the value of multiple rows to a different specified value in the same column?

Say I have a table where there are product IDs, product desc., and the language of each desc. I would like it so that if there was a description with a NULL value for American-English, it would update to the British-English version of that the description for that product. Is there a way to do this using the update command in SQL?
I normally prefer this syntax for updating values in one table from values in another (or in this case the same) table, b/c it is easy to change the UPDATE...SET to a SELECT for testing and to quickly see what values would be updated.
UPDATE p_US
SET p_US.product_desc = p_GB.product_desc
FROM dbo.product p_US
INNER JOIN dbo.product p_GB ON p_US.productID = p_GB.productID
WHERE p_US.language_code = 'US'
AND p_GB.language_code = 'GB'
AND p_US.product_desc IS NULL
;
and then you can swap out the first two lines above with this for quick testing to see what would be updated:
SELECT p_US.productID, p_US.product,
oldDesc = p_US.product_desc, newDesc = p_GB.product_desc
update [table] set [column]= case [change factor] when '1' then 'X' else 'Y' end where [where clause]
Maybe:
UPDATE my_table SET desc=(SELECT desc from my_table WHERE my_table.id=id AND my_table.lang='british') WHERE lang='american' and desc is NULL;

Update a table from another table on multiple columns in Oracle 11g

Oracle 11g SQL & both tables have the same column definitions:
VARCHAR2(11)
NUMBER
DATE
DATE
I tried to find a solution to this problem, and this is what I ended up with, which fails:
update jjjTable
set [fourthCol] = B.[fourthOtherCol]
from jjjTable, otherTable B
where jjjTable.[firstCol] = B.[firstOtherCol]
and jjjTable.[secondCol] = B.[secondOtherCol]
and jjjTable.[thirdCol] = B.[thirdOtherCol]
I'm under the impression that I need to have the from in this was based on this article:
SQL update from one Table to another based on a ID match and the edited response from Shivkant
I'm under the impression that I may need to use a join based on this article:
How do I UPDATE from a SELECT in SQL Server? and the response from Robin Day
but as I understand it, joins are only on one column match per row. I'm interested in matching on 3 elements, and I'm not finding a clear path for solution.
Any direction would be well received.
This is what I ended up needing to do as a solution:
DECLARE
CURSOR j_CUR IS
SELECT A.[fourthCol]
FROM JJJtable A, otherTable B
WHERE A.[firstCol] = B.[firstOtherCol]
and A.[secondCol] = B.[secondOtherCol]
and A.[thirdCol] = B.[thirdOtherCol]
FOR UPDATE OF B.[fourthOtherCol];
SOME_DATE DATE;
BEGIN
FOR IDX IN j_CUR LOOP
SOME_DATE :=(IDX.[fourthCol]);
UPDATE otherTable
SET [fourthOtherCol] = SOME_DATE
WHERE CURRENT OF j_CUR;
END LOOP;
END;
Thank you for your efforts and guidance.
This is the close best I was able to get it to work on my similar use case. Try this out.
update jjjTable
SET jjjTable.[fourthCol] = (SELECT distint otherTable.fourthOtherCol from otherTable
WHERE otherTable.firstOtherCol = jjjTable.firstCol and
otherTable.secondOtherCol = jjjTable.secondCol and
otherTable.thirdOtherCol = jjjTable.thirdCol)
WHERE EXISTS (SELECT distint otherTable.fourthOtherCol from otherTable
WHERE otherTable.firstOtherCol = jjjTable.firstCol and
otherTable.secondOtherCol = jjjTable.secondCol and
otherTable.thirdOtherCol = jjjTable.thirdCol);