T-SQL For Each Alternative? - sql

I need to take data from one table and import it into another table. In pseudocode, something like this:
For Each row in table1
If row.personid is in table2 then
update table2.row
Else
insert row into table2
End If
Next
What is the best way to do this in T-SQL? As I understand it T-SQL doesn't support For Each..Next, so what alternatives do I have?

If you're using SQL Server 2008 then you could use the MERGE statement. Maybe something like this:
MERGE table2 AS t -- target
USING table1 AS s -- source
ON ( t.personid = s.personid )
WHEN MATCHED THEN
UPDATE
SET second_column = s.second_column,
third_column = s.third_column,
etc = s.etc
WHEN NOT MATCHED THEN
INSERT ( personid, second_column, third_column, etc )
VALUES ( s.personid, s.second_column, s.third_column, s.etc )

All things being equal, set based operations are better.
update t1
set t1.x = t2.x
.
.
.
from table1 t1
inner join table2 t2 on t1.id = t2.t1id
then
insert into table1
select * from table2 t2 where t2.t1id not in (select table1.id from table1 )

You could use a cursor for this as others have described. Personally I like doing two statements in a row like so:
UPDATE tbl2 SET field1=tbl1.field1, field2=tbl1.field2 -- etc.
FROM tb12
JOIN tbl1 on tbl2.personid = tbl1.personid
INSERT tbl2 (personid, field1, field2)
SELECT personid, field1, field2
FROM tbl1
WHERE NOT EXISTS (select personid from tbl2 where personid = tbl1.persondid)

doing this in a while loop is just wrong.
for your situatuin you can use the new MERGE statement in sql server 2008.
Here's a simple example on how to do it.

If you're on SQL Server 2008 then the best way to do this is with the MERGE statement. Something like...
MERGE INTO target_table t
USING source_table s
ON t.personid = s.personid
WHEN MATCHED THEN
UPDATE ...
WHEN NOT MATCHED THEN
INSERT ...

You state TSQL but don't give a version. If you are on SQL2008 the Merge statement should do what you need.

One of the most common ways is to use cursors. That way you can go through each record that your query returns and handle it accordingly, either with an UPDATE or INSERT.
See: http://msdn.microsoft.com/en-us/library/ms180169.aspx

Related

Update table if exist or insert if not

I have 2 SQL tables with same column label, Table1 and Table2.
I want to update Table1 with value of Table2 if there is a same value for an attribute.
The tables has CODE, POSITION, DESCRIPTION as columns.
I do this:
UPDATE Table1
SET CODE = Table2.CODE,
POSITION= Table2.POSITION
FROM Table2
WHERE DESCRIPTION = Table2.DESCRIPTION
It works, but if in the DESCRIPTION Value of Table2 is not present into Table1, I want to insert the entire row, in other words I need to do something like:
UPDATE IF DESCRIPTION EXISTS
ELSE INSERT
How can I do this?
You can do this from first-principals in 2 steps
Update the existing values like you have done:
UPDATE Table1
SET
CODE= t2.CODE,
POSITION= t2.POSITION
FROM Table1 t1
INNER JOIN Table2 t2 ON t1.DESCRITPION = t2.DESCRITPION
Then you can insert the missing records
INSERT INTO Table1 (CODE, POSITION, DESCRITPION)
SELECT CODE, POSITION, DESCRITPION
FROM Table2 t2
WHERE NOT EXISTS (SELECT DESCRITPION
FROM Table1 t1
WHERE t1.DESCRITPION = t2.DESCRITPION)
Most RDBMS have a MERGE statement that allows you to combine these two queries into a single atomic operation, you'll need to check the documentation for your vendor, but an MS SQL Server MERGE solution looks like this:
MERGE INTO Table1 AS tgt
USING Table2 AS src
ON tgt.DESCRITPION = src.DESCRITPION
WHEN MATCHED
THEN UPDATE SET CODE= src.CODE,
POSITION= src.POSITION
WHEN NOT MATCHED THEN
INSERT (CODE, POSITION, DESCRITPION)
VALUES (src.CODE, src.POSITION, src.DESCRITPION)
An additional benefit to the MERGE clause is that it simplifies capturing the changed state into the OUTPUT clause which allows for some complex logic all while keeping the query as a single operation without having to manually create and track transaction scopes.

Can I UPDATE multiple records using different values in a SELECT query?

For example I have table1 with field1 and field2 and want to do something like:
UPDATE table1
SET field1, field2 = (SELECT field1, field 2 FROM tableXYZ)
WHERE field 3 = 'foobar'
or do I have to do multiple SETs, running the same SELECT query several times?
Assuming whatever database you are using supports it, you can join the tables. SO:
Update table1
set field1 = tbx.field1,
field2 = tbx.field2
from table1 join tablexyz on --some key value join
You can do a tuple assignment by putting the columns on the left hand side between parentheses.
UPDATE table1
SET (column1, column2) = (SELECT col1, col2
FROM tableXYZ
WHERE ...)
WHERE column3 = 'foobar';
The above is standard SQL, but not all DBMS support that.
Note that you have to use a WHERE clause in the sub-select to make sure that select only returns a single row (you would typically make that a co-related sub-query).

How to select a value that can come from two different tables?

First, SQL is not my strength. So I need help with the following problem. I'll simplify the table contents to describe the problem.
Let's start with three tables : table1 with columns id_1 and value, table2 with columns id_2 and value, and table3 with columns id_3 and value. As you'll notice, a field value appears in all three tables, while ids have different column names. Modifying column names is not an option because they are used by Java legacy code.
I need to set table3.value using table1.value or table2.value according to the fields table1.id_1, table2.id_2 and table3.id_3.
My last attempt, which describes what I try to do, is the following:
UPDATE table3
SET value=(IF ((SELECT COUNT(\*) FROM table1 t1 WHERE t1.id_1=id_3) > 0)
SELECT value FROM table1 t1 WHERE t1.id_1=id_3
ELSE IF ((SELECT COUNT(\*) FROM table2 t2 WHERE t2.id_2=id_3)) > 0)
SELECT value FROM table2 t2 WHERE t2.id_2=id_3)
Here are some informations about the tables and the update.
This update will be included in an XML file used by Liquibase.
It must work with Oracle or SQL Server.
An id from table3.id_3 can be found at most once in table1.id_1 or in table2.id_2, but not in both tables simultaneously.
If table3.id_3 is not found in table1.id_1 nor in table2.id_2, table3.value remains null.
As you can imagine, my last attempt failed. In that case, the IF command was not recognized during the Liquibase update. If anyone has any ideas how to deal with this, I'd appreciate. Thanks in advance.
I don't know Oracle very well, but a SQL Server approach would be the following using COALESCE() and OUTER JOINs.
Update T3
Set Value = Coalesce(T1.Value, T2.Value)
From Table3 T3
Left Join Table2 T2 On T3.Id_3 = T2.Id_2
Left Join Table1 T1 On T3.Id_3 = T1.Id_1
The COALESCE() will return the first non-NULL value from the LEFT JOIN to tables 1 and 2, and if a record was not found in either, it would be set to NULL.
It is Siyual's UPDATE written with MERGE operator.
MERGE into table_1
USING (
SELECT COALESCE(t2.value, t3.value) as value, t1.id_1 as id
FROM table_1 t1, table_2 t2, table_3 t3
WHERE t2.id_2 = t3.id_3 and t1.id_1 = t2.id_2
) t on (table_1.id_1 = t.id)
WHEN MATCHED THEN
UPDATE SET table_1.value = t.value
This should work in Oracle.
In Oracle
UPDATE table3 t
SET value=COALESCE((SELECT value FROM table1 t1 WHERE t1.id_1=t.id_3),
(SELECT value FROM table2 t2 WHERE t2.id_2=t.id_3))
Given your assumption #3, you can use union all to put together tables 1 and 2 without running the risk of duplicating information (at least for the id's of interest). So a simple merge solution like the one below should work (in all DB products that implement the merge operation).
merge into table3
using (
select id_2 as id, value from table2
union all
select id_3, value from table 3
) t
on table3.id_3 = t.id
when matched
then update set table3.value = t.value;
You may want to test the various solutions and see which is most effective for your specific tables.
(Note: merge should be more efficient than the update solution using coalesce, at least when relatively few of the id's in table3 have a match in the other tables. This is because the update solution will re-insert NULL where NULL was already stored when there is no match. The merge solution avoids this unnecessary activity.)

Oracle update using subquery

I need to convert my update Ms SQL Server query for Oracle
I have two tables:
Table1(id int, SaveId int, ChangeId int)
Table2(id int, smSaved int, boldId int)
The query is:
UPDATE
Table1
Set
SaveId=tbl.smSaved
FROM
Table1 tbl1
join Table2 tbl on tbl1.ChangeId=tbl.boldId
In MsSql everything works fine, but when I'm trying to run it in Oracle thats doesn't works. I can't figure out whats wrong with it.
Can anybody explain me what I need to change?
Try this:
UPDATE table1 SET SaveId =
(SELECT tbl.saved FROM table2 tbl
WHERE tbl.boldId = table1.ChangeId)
WHERE EXISTS(
SELECT tbl.saved FROM table2 tbl
WHERE tbl.boldId = table1.ChangeId
)
The first part of query update SaveId with value of tbl.saved (I suppose you have only one row of table2 linked with table1.
The second part (exists) guarantee you have a correspond between two table (if you add exists you put NULL where not exists row in table2)
You are may to use update with subquery same MS SQL. In Oracle its look like:
UPDATE (SELECT t1.savedId t1_saved
, t2.smsaved t2_saved
FROM table1 t1
, table2 t2
WHERE t1.changeid = t2.boldid)
SET t1_saved = t2_saved

Valid UPDATE, INSERT syntax?

Are these statements valid?
UPDATE Table1
FROM (SELECT * FROM Table2)
INSERT INTO Table1
(SELECT * FROM Table2)
Your update statement needs a Set for each field you want to update, so no.
Correct syntax:
UPDATE table1
SET table1.field1=table2.field1, table1.field2=table2.field2
FROM table1
INNER JOIN table2 ON table1.keyfield = table2.keyfield
Your insert statement will work provided that Table1 and Table2 have the same columns in the same order.
Edit: If you're looking for an example of Update/Insert (Upsert), check out this blog post (which works on SQL 2008--not sure about 2005 but doubt it).
Another option is an Update and then an insert. Example from this blog post :
UPDATE CustomersA
SET CustomerName = B.CustomerName
FROM CustomersA A (NoLock)
INNER JOIN CustomersB B (NoLock) ON A.CustomerId = B.CustomerId
And later run the Insert command
INSERT INTO CustomersA (
CustomerId,
CustomerName
)
SELECT
Id,
Name
FROM CustomersB (NoLock)
WHERE
Id NOT IN (
SELECT CustomerId FROM CustomersA (NoLock)
)
INSERT INTO Table1
(SELECT * FROM Table2)
That could work - if you're lucky.
I'd recommend using a more strict syntax:
INSERT INTO dbo.Table1 (Field1, Field2, ..., FieldN)
SELECT Field1, Field2, ..., FieldN
FROM dbo.Table2
I would always and explicitly specify the schema (dbo) and fields for both my INSERT and my SELECT statements. That way, you can
eliminate columns that are e.g. computed, IDENTITY and other potential "problem" cases
your SQL statement won't break when you add another column to either table
Marc