MS SQL - Conditional UPDATE return result - sql

I have a stored procedure which is doing the following.
The populated target table data is checked against several similar source tables for a match (based on name and address data). If a match is found in the first table then it updates the target with a flag identifying which source table the match was from. However if it doesn't find a match I need it to look in the next source table and the next until either a match is found or not as the case may be.
Is there an easy way for the UPDATE statement to provide some kind of return value I can query to say whether it updated the target table? I would like to use this return value so that I can skip checking subsequent source tables unnecessarily.
Otherwise will I have to perform the conditional UPDATE then do a separate query to determine if the UPDATE actually updated the flag?

Probably the safest approach is to use the OUTPUT clause. This will return the modified rows into a new table.
You can check the table to see if any rows have been updated.
One advantage of the OUTPUT clause is that you can update multiple rows at the same time.

I like the soulution of Gordon, but I do not think you actualy need it.
Simply run the updates in order:
UPDATE BASE_TABLE
SET FLAG='first_table'
where FLAG IS null AND
EXIST (SELECT 1 FROM first_table f1 where f1.ID = ID)
UPDATE BASE_TABLE
SET FLAG='second_table'
where FLAG IS null AND
EXIST (SELECT 1 FROM second_table f2 where f2.ID = ID)
...
And so on.
You dont need to check every row conditionaly, that would be very slow.

you can put your update in try/catch and insert your result to another table

Related

How to identify which records were not updated after executing an update query in oracle?

I am trying to update 100 records in oracle through the following update statement, but when I execute it, it says 90 records updated, whereas I have 100 records in the where clause.
Now how to identify which 10 records (codes) are not updated out of 100 in oracle?
In the following statement, I want to know the code names which were not updated? is there any simple trick to know?
update table1 a set a.column1='Yes'
where a.column2 in ('code1','code2','code3','code4',........,'code100');
You can't really directly from the update; but you could use the same list of values (assuming it's a hard-coded list, not coming from a table) as a collection, and then look for values in the collection that are not in the table:
select *
from table(sys.odcivarchar2list('code1','code2','code3','code4',........,'code100')) t
where not exists (
select null
from table1 a
where a.column2 = t.column_value
);
db<>fiddle with a smaller set to demonstrate the idea.
You could modify your update to only update rows which are not already 'Yes'; and if you did that then you could either look for collection values that don't exist at all, or those which exist but don't need to be updated - in that case, before you actually run the update, of course. db<>fiddle.
odcivarchar2list is a built-in collection type, but you could use your own.
If you already have the values in a collection or table you can use that directly, both for this query and for the update.

PL/SQL Merge statement

I'm looking to implement a sort of Type 2 Slow Changing Dimension like behavior into my dimension table using PL/SQL's MERGE statement. It workd just fine for updating existing values and inserting new ones. I'm looking to extend this functionality by not only updating existing values but creating a different row with the updated values while preserving the row with the "outdated" values.
In short, is it possible to do this?
MERGE INTO A
USING B
ON (A.ID = B.ID)
WHEN MATCHED THEN
UPDATE END_DATE ON THE EXISTING ROW;
INSERT UPDATED VALUES IN A NEW ROW;
WHEN NOT MATCHED THEN
INSERT A NEW ROW WITH NEW VALUES;
Thanks you guys in advance.
If you are truly doing a Type 2 dimension and want to use a merge, yes it can be done but it's not terribly straightforward. Effectively, if you will need to compare your data using an inline view in the USING clause, that features a column that indicates whether its an insert or an update. This field gets joined to an slowly changing dimension table that drives whether an insert or an update occurs.
This blog post describes the technique in great detail and has worked for us, albeit we used a hash to determine unqiueness as opposed to a column by column compare.
Load Slowly Changing Dimension Type 2 using Oracle Merge Statement
You need to run two statements instead of 1 here.
(in your pseudo-code)
UPDATE END_DATE IN A WHERE A.ID = something_from_B
INSERT new VALUES IN A
The insert needs to happen no matter what. So no need of checking if the row exists or not (and only updating if the row exists). Just end_date all records that match B. This would end_date 0 to n number of rows. And then Insert everything together without worrying if that record has been end-dated already or not.
Put this inside a BEGIN...END; if you want transactional atomicity.
In short - no. A MERGE statement provides the option to INSERT if matching data is not found, and to UPDATE if matching data is found. It does NOT give the option to "update-and-insert" if matching data is found.
Best of luck.
As I understand, you wrote a pseudocode. So I can suggest just an idea also in pseudocode:
MERGE INTO A
USING (select * from B1 union all
select * from B2) B
ON (A.ID = B.ID)
WHEN MATCHED THEN
UPDATE END_DATE ON THE EXISTING ROW FROM B1;
WHEN NOT MATCHED THEN
INSERT A NEW ROW WITH NEW VALUES FROM B2;
If you want to insert updated rows as new, you can generate subquery based on B, which will contain "updated rows", but these rows have to be considered as new by SQL engine (it is impossible to say how to do it without details about your tables). In my query B1 is "rows to update", and B2 is "updated rows which have to look as new". If you do this, updated rows will be inserted.
Also, no guarantee that in your case it could be implemented.

What's a good logic/design of a SQL script to incrementally update a table?

So there's this table of just about 40,000 rows I am looking to update. Colleague said it's best to incrementally update the table instead of complete delete and load.
So I've tried hashing out the design and logic of a script to do this, but my inexperience is getting to me. I just don't know what's efficient and unneeded to incrementally update a table.
Currently, the warehouse looks like this: data comes from source into a table (let's call this T1) in Teradata. Then it's sent into another table (let's call this T2) in Teradata with some added fields such as timestamp. Lastly, a view is built on that last table for security reasons.
So with that laid out, I was thinking of creating a temp/volatile table with data from T1. This would have all the data up to the time the script is run with new records. Then, go through the entire table seeing if the ID (primary index) already exists in T2, and if not, add it to another temp table. Then somehow combine the second temp table with T2 and override T2 and build a view on top of that.
Does this make any sense?
There's also the possibility of records being updated. So they would already exist in T2, but have updated data in a new version of T1. I think comparing the values of all the columns from T1 to T2 would be highly inefficient, but can't think of another way to do this
A 40,000 row delete and insert should be pretty painless for any modern database. Ditto for updates.
The real reason for doing and incremental delete/update/insert is so you can log the changes and timestamp rows in the permanent table with the date/time of nsertion and/or last update. The usual technique goes something like this:
remove rows from the permanent table that don't exist in the temp table
update rows that exist in both tables
insert rows that exist in the temp table, but don't exist in the permanent table.
Looking at the Teradata docs, that would be something like this (no warranties about this being syntactically correct, since I don't have a Teradata instance to play with):
delete permanent p
where not exists ( select *
from temp t
where t.id = p.id
)
update p
from permanent p ,
temp t
set ...
where t.id = p.id
insert permanent
select ...
from temp t
where not exists ( select *
from permanent p
where p.id = t.id
)
One might note that the deletes might get a little hairy if there are dependent foreign key constraints involved.
One might also note that on the update, the where clause might get a tad...complicated if you want to check for actual changes to column values: not much point in updating a row if nothing has changed.
There's a Teradata MERGE command that you might find useful, check this post:
https://forums.teradata.com/forum/database/merge-syntax-simple-version
merge into merge_tmp as t using (select 1 as a,'stf' as b,'uuj' as c) as s
on t.a = s.a
when matched then update set c = s.c
when not matched then insert values (s.a,s.b,s.c);
If you need to match on more columns simple put an and in the on statement.
Edit: If you want to use MERGE you might also need to use a delete statement like the one in nicholas' post.

How to update multiple matching records with data from another table

Working with DB2 but guess this applies to SQL in general.
I have two tables; Table1 contains data where the Fix column could be same in multiple rows. Table2 has unique rows and has data I want to add to columns in the first table if one or more matches between the Fix column in Table1 and the Title column in Table2 are found.
I'm getting an issue in that the SQL is returning an error saying: "The same row of target table "xxxxxxx" was identified more than once for an update, delete or insert operation of the MERGE statement.."
Now that is expected ie I know there are multiple rows in the target table that match the criteria and need to have the data from the source table applied to them.
I'm using MERGE but is that just not going to be possible? Been looking at GROUP BY too but can't get it to work.
If I was doing this in another language I'd go through the source table, build a collection of matching records from the source then iterate through the collection updating that with the source data that needed adding. Thinking there is a more efficient way in SQL though?
This code is completely wrong (and returns the error above) but adding it here to help any good person who wants to lend a hand :-)
CREATE PROCEDURE Update_RawData_With_xKey_Data ()
P1: BEGIN
MERGE INTO table1 AS T
USING table2 AS S
ON (T.FIX = S.TITLE)
WHEN MATCHED THEN
UPDATE SET
T.Rating = S.Rating,
T.GSDS = S.GSDS_Date,
ELSE IGNORE ;
END P1

Reconciling a column across two tables in SQL Server

There are two Databases, Database A has a table A with columns of id, group and flag. Database B has a table B with columns of ID and flag. Table B is essentially a subset of table A where the group == 'B'.
They are updated/created in odd ways that are outside my understanding at this time, and are beyond the scope of this question (this is not the time to fix the basic setup and practices of this client).
The problem is that when the flag in Table A is updated, it is not reflected in table B, but should be. This is not a time-critical problem, so it was suggested I create a job to handle this. Maybe because it's the end of the week, or maybe because I've never written more than the most basic stored procedure (I'm a programmer, not a DBA), but I'm not sure how to go about this.
At a simplistic level, the stored procedure would be something along of the lines of
Select * in table A where group == B
Then, loop through the resultset, and for each id, update the flag.
But I'm not even sure how to loop in a stored procedure like this. Suggestions? Example code would be preferred.
Complication: Alright, this gets a little harder too. For every group, Table B is in a separate database, and we need to update this flag for all groups. So, we would have to set up a separate trigger for each group to handle each DB name.
And yes, inserts to Table B are already handled - this is just to update flag status.
Assuming that ID is a unique key, and that you can use linked servers or some such to run a query across servers, this SQL statement should work (it works for two tables on the same server).
UPDATE Table_B
SET Table_B.Flag = Table_A.Flag
FROM Table_A inner join Table_B on Table_A.id = Table_B.id
(since Table_B already contains the subset of rows from Table_A where group = B, we don't have to include this condition in our query)
If you can't use linked servers, then I might try to do it with some sort of SSIS package. Or I'd use the method described in the linked question (comments, above) to get the relevant data from Database A into a temp table etc. in Database B, and then run this query using the temp table.
UPDATE
DatabaseB.dbo.Table_B
SET
DatabaseB.dbo.Table_B.[Flag] = DatabaseA.dbo.Table_A.Flag
FROM
DatabaseA.dbo.Table_A inner join DatabaseB.dbo.Table_B B
on DatabaseA.dbo.id = DatabaseB.dbo.B.id
Complication:
For sevaral groups run one such update SQL per group.
Note you can use Flag without []. I'm using the brackets only because of syntax coloring on stackoverflow.
Create an update trigger on table A that pushes the necessary changes to B as A is modified.
Basically (syntax may not be correct, I can't check it right now). I seem to recall that the inserted table contains all of the updated rows on an update, but you may want to check this to make sure. I think the trigger is the way to go, though.
create trigger update_b_trigger
on Table_A
for update
as
begin
update Table_B
set Table_B.flag = inserted.flag
from inserted
inner join Table_B
on inserted.id = Table_B.id
and inserted.group = 'B'
and inserted.flag <> Table_B.flag
end
[EDIT] I'm assuming that inserts/deletes to Table B are already handled and it's just flag updates to Table B that need to be addressed.