Valid UPDATE, INSERT syntax? - sql

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

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.

SQL update statement with different table in from clause

Out of accident I noticed that the following query is actually valid:
UPDATE bikes
SET price = NULL
FROM inserted
WHERE inserted.owner_id = 123456
This is part of a trigger where someone forgot to join the original table to the inserted table. The result is that when the trigger is executed, all prices are set to NULL.
The correct SQL statement is this:
UPDATE bikes
SET price = NULL
FROM inserted
INNER JOIN bikes ON bikes.id=inserted.id
WHERE inserted.owner_id = 123456
How/why is this first statement valid?
Why wouldn't it be valid? SQL Server doesn't know what you're trying to do. It thinks you want to update all of the fields where some condition exists on another table. See the last update below.
SETUP
declare #table table
(
id int,
name varchar(10)
)
declare #itable table
(
id int,
name varchar(10)
)
insert into #table (id, name)
select 1,'abc' union
select 2,'def' union
select 3,'ghi' union
select 4,'jkl' union
select 5,'mno' union
select 6,'pqr'
insert into #itable (id, name)
select 1,'abc' union
select 2,'def' union
select 3,'ghi' union
select 4,'jkl' union
select 5,'mno' union
select 6,'pqr'
All names on #table will change to zzz
update #table
set name = 'zzz'
from #itable i
where i.id = 1
select * from #itable
select * from #table
All names where id = 1 on #table becomes yyy
update #table
set name = 'yyy'
from #itable i
inner join #table t on i.id = t.id
where i.id = 1
select * from #itable
select * from #table
This will NOT update anything
update #table
set name = 'aaa'
from #itable i
where i.id = 133
select * from #itable
select * from #table
The first statement does not work as expected because it is missing the entire INNER JOIN line with the bikes and inserted table. Without that SQL Server will update all rows as all rows will qualify for an update when the inserted.owner_id = 123456.
You can reproduce this outside of the trigger in TSQL like :
update bikes set price =null
from SomeOtherTable
where SomeOtherTable.SomeColumn = 'some_value_that_exists'
This is syntactically valid statement in SQL Server. If the Intention is to update bikes table based on existance of a row in some unrelated table that cant be joined because the 2 tables arent related then this is how you would do it. But that is not your requirement. Hence why it updates all records instead of only those that match the bikes.id In programming terms this is called as a logical bug.
The inner join makes it more restrictive and forces to to update only those rows that match the join condition between the 2 tables (bikes.id=inserted.id comparison )
In Simple terms the from clause is optional..Consider below query..
update table
set id=10
This has one table right after update clause ,sql just updates it..
now consider below query..
update table1
set id=40
from table2..
What do you think SQL does,it updates all the rows same as first query..
unless you refer to another table in from clause and join like below
update t1
set t1.id=40
from
table1 t1
join
table2 t2
on t1.id=t2.id
below is the from clause explanation in update syntax stripped down to show only to relevant parts..
If the object being updated is the same as the object in the FROM clause and there is only one reference to the object in the FROM clause, an object alias may or may not be specified. If the object being updated appears more than one time in the FROM clause, one, and only one, reference to the object must not specify a table alias. All other references to the object in the FROM clause must include an object alias
As long as above rules are valid (as in your case),SQL will happily update table found immediately after update clause

Update table values from another table with the same user name

I have two tables, with a same column named user_name, saying table_a, table_b.
I want to, copy from table_b, column_b_1, column_b2, to table_b1, column_a_1, column_a_2, respectively, where the user_name is the same, how to do it in SQL statement?
As long as you have suitable indexes in place this should work alright:
UPDATE table_a
SET
column_a_1 = (SELECT table_b.column_b_1
FROM table_b
WHERE table_b.user_name = table_a.user_name )
, column_a_2 = (SELECT table_b.column_b_2
FROM table_b
WHERE table_b.user_name = table_a.user_name )
WHERE
EXISTS (
SELECT *
FROM table_b
WHERE table_b.user_name = table_a.user_name
)
UPDATE in sqlite3 did not support a FROM clause for a long time, which made this a little more work than in other RDBMS. UPDATE FROM was implemented in SQLite 3.33 however (2020-08-14) as mentioned at: https://stackoverflow.com/a/63079219/895245
If performance is not satisfactory, another option might be to build up new rows for table_a using a select and join with table_a into a temporary table. Then delete the data from table_a and repopulate from the temporary.
Starting from the sqlite version 3.15 the syntax for UPDATE admits a column-name-list
in the SET part so the query can be written as
UPDATE table_a
SET
(column_a_1, column_a_2) = (SELECT table_b.column_b_1, table_b.column_b_2
FROM table_b
WHERE table_b.user_name = table_a.user_name )
which is not only shorter but also faster
the last "WHERE EXISTS" part
WHERE
EXISTS (
SELECT *
FROM table_b
WHERE table_b.user_name = table_a.user_name
)
is actually not necessary
It could be achieved using UPDATE FROM syntax:
UPDATE table_a
SET column_a_1 = table_b.column_b_1
,column_a_2 = table_b.column_b_2
FROM table_b
WHERE table_b.user_name = table_a.user_name;
Alternatively:
UPDATE table_a
SET (column_a_1, column_a_2) = (table_b.column_b_1, table_b.column_b_2)
FROM table_b
WHERE table_b.user_name = table_a.user_name;
UPDATE FROM - SQLite version 3.33.0
The UPDATE-FROM idea is an extension to SQL that allows an UPDATE statement to be driven by other tables in the database. The "target" table is the specific table that is being updated. With UPDATE-FROM you can join the target table against other tables in the database in order to help compute which rows need updating and what the new values should be on those rows
There is an even much better solution to update one table from another table:
;WITH a AS
(
SELECT
song_id,
artist_id
FROM
online_performance
)
UPDATE record_performance
SET
op_song_id=(SELECT song_id FROM a),
op_artist_id=(SELECT artist_id FROM a)
;
Update tbl1
Set field1 = values
field2 = values
Where primary key in tbl1 IN ( select tbl2.primary key in tbl1
From tbl2
Where tbl2.primary key in tbl1 =
values);
The accepted answer was very slow for me, which is in contrast to the following:
CREATE TEMPORARY TABLE t1 AS SELECT c_new AS c1, table_a.c2 AS c2 FROM table_b INNER JOIN table_a ON table_b.c=table_a.c1;
CREATE TEMPORARY TABLE t2 AS SELECT t1.c1 AS c1, c_new AS c2 FROM table_b INNER JOIN t1 ON table_b.c=t1.c2;

T-SQL For Each Alternative?

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

Avoid duplicates in INSERT INTO SELECT query in SQL Server

I have the following two tables:
Table1
----------
ID Name
1 A
2 B
3 C
Table2
----------
ID Name
1 Z
I need to insert data from Table1 to Table2. I can use the following syntax:
INSERT INTO Table2(Id, Name) SELECT Id, Name FROM Table1
However, in my case, duplicate IDs might exist in Table2 (in my case, it's just "1") and I don't want to copy that again as that would throw an error.
I can write something like this:
IF NOT EXISTS(SELECT 1 FROM Table2 WHERE Id=1)
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1
ELSE
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1 WHERE Table1.Id<>1
Is there a better way to do this without using IF - ELSE? I want to avoid two INSERT INTO-SELECT statements based on some condition.
Using NOT EXISTS:
INSERT INTO TABLE_2
(id, name)
SELECT t1.id,
t1.name
FROM TABLE_1 t1
WHERE NOT EXISTS(SELECT id
FROM TABLE_2 t2
WHERE t2.id = t1.id)
Using NOT IN:
INSERT INTO TABLE_2
(id, name)
SELECT t1.id,
t1.name
FROM TABLE_1 t1
WHERE t1.id NOT IN (SELECT id
FROM TABLE_2)
Using LEFT JOIN/IS NULL:
INSERT INTO TABLE_2
(id, name)
SELECT t1.id,
t1.name
FROM TABLE_1 t1
LEFT JOIN TABLE_2 t2 ON t2.id = t1.id
WHERE t2.id IS NULL
Of the three options, the LEFT JOIN/IS NULL is less efficient. See this link for more details.
In MySQL you can do this:
INSERT IGNORE INTO Table2(Id, Name) SELECT Id, Name FROM Table1
Does SQL Server have anything similar?
I just had a similar problem, the DISTINCT keyword works magic:
INSERT INTO Table2(Id, Name) SELECT DISTINCT Id, Name FROM Table1
I was facing the same problem recently...
Heres what worked for me in MS SQL server 2017...
The primary key should be set on ID in table 2...
The columns and column properties should be the same of course between both tables. This will work the first time you run the below script. The duplicate ID in table 1, will not insert...
If you run it the second time, you will get a
Violation of PRIMARY KEY constraint error
This is the code:
Insert into Table_2
Select distinct *
from Table_1
where table_1.ID >1
Using ignore Duplicates on the unique index as suggested by IanC here was my solution for a similar issue, creating the index with the Option WITH IGNORE_DUP_KEY
In backward compatible syntax
, WITH IGNORE_DUP_KEY is equivalent to WITH IGNORE_DUP_KEY = ON.
Ref.: index_option
From SQL Server you can set a Unique key index on the table for (Columns that needs to be unique)
A little off topic, but if you want to migrate the data to a new table, and the possible duplicates are in the original table, and the column possibly duplicated is not an id, a GROUP BY will do:
INSERT INTO TABLE_2
(name)
SELECT t1.name
FROM TABLE_1 t1
GROUP BY t1.name
In my case, I had duplicate IDs in the source table, so none of the proposals worked. I don't care about performance, it's just done once.
To solve this I took the records one by one with a cursor to ignore the duplicates.
So here's the code example:
DECLARE #c1 AS VARCHAR(12);
DECLARE #c2 AS VARCHAR(250);
DECLARE #c3 AS VARCHAR(250);
DECLARE MY_cursor CURSOR STATIC FOR
Select
c1,
c2,
c3
from T2
where ....;
OPEN MY_cursor
FETCH NEXT FROM MY_cursor INTO #c1, #c2, #c3
WHILE ##FETCH_STATUS = 0
BEGIN
if (select count(1)
from T1
where a1 = #c1
and a2 = #c2
) = 0
INSERT INTO T1
values (#c1, #c2, #c3)
FETCH NEXT FROM MY_cursor INTO #c1, #c2, #c3
END
CLOSE MY_cursor
DEALLOCATE MY_cursor
I used a MERGE query to fill a table without duplications.
The problem I had was a double key in the tables ( Code , Value ) ,
and the exists query was very slow
The MERGE executed very fast ( more then X100 )
examples for MERGE query
For one table it works perfectly when creating one unique index from multiple field. Then simple "INSERT IGNORE" will ignore duplicates if ALL of 7 fields (in this case) will have SAME values.
Select fields in PMA Structure View and click Unique, new combined index will be created.
A simple DELETE before the INSERT would suffice:
DELETE FROM Table2 WHERE Id = (SELECT Id FROM Table1)
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1
Switching Table1 for Table2 depending on which table's Id and name pairing you want to preserve.