I have a view that does a union between two tables in SQL Server. I am using a INSTEAD OF UPDATE trigger to update the base tables. I have written this code to update Column1 on a specific row in the base tables, but I get an error:
IF UPDATE(Column1)
BEGIN
UPDATE BaseTable
SET Column1 = I.Column1
FROM inserted I INNER JOIN (SELECT * FROM Table1 UNION ALL SELECT * FROM Table2) AS BaseTable
ON I.UniqueID = BaseTable.UniqueID
WHERE BaseTable.UniqueID = I.UniqueID
END
ERROR: Derived table 'Base' is not updatable because a column of the derived table is derived or constant.
The error message is actually pretty clear. One of the requirements for a view/subquery/CTE to be updatable is that each column can be matched to a single column in a single table. That is what the error message is trying to tell you.
You have to update the tables separately:
UPDATE t1
SET Column1 = I.Column1
FROM inserted I INNER JOIN
Table1 t1
ON I.UniqueID = t1.UniqueID;
UPDATE t2
SET Column1 = I.Column1
FROM inserted I INNER JOIN
Table2 t2
ON I.UniqueID = t2.UniqueID;
SQL Server only allows an UPDATE statement to update one table.
Related
I have two tables: Table 1 is the master table and Table 2 is a table that is refreshed monthly and truncated after. Thus, I want to write a query that inserts monthly new values from Table 2 into Table 1 using some matching fields. The columns I need to match by are country and name. Here are the tables:
How do I approach this query to get to the green table? I looked into Update and Insert Statements but could not find anything which could help.
How about using MERGE statement
MERGE INTO Table1 as Target
USING Table2 as Source
ON (target.Name = source.Name AND target.Country = source.Country)
WHEN MATCHED THEN UPDATE SET Age = source.Age, Gender = source.Gender
WHEN NOT MATCHED THEN INSERT (Name, Country, Age, Gender) VALUES (source.Name, source.Country, source.Age, source.Gender);
What the statment does is look for records in Target that match records in source by name and country. If a Match is found, the Age and Gender of the target records are update, when a match is not found, a new record is inserted in target table.
I tested this in SQL Server, but I think snowflake has the same syntax.
You can read about MERGE statement in Snowflake documentation.
Snowflake syntax
UPDATE
table1 AS TA
SET
TA.Age = TB.Age
FROM (SELECT TA1.country, TB1.country, TA1.name, TB1.name, TB1.Age
FROM table1 AS TA1
INNER JOIN table2 AS TB1
ON (TA1.country = TB1.country AND TA1.name= TB1.name)) TB
WHERE TA.country = TB.country
And MySql
UPDATE
table1 AS TA
INNER JOIN table2 AS TB
ON TA.country = TB.country
AND TA.name= TB.name
SET
TA.Age = TB.Age;
Considering you have to only update the age column in the table1.
update table1 set AGE=(select distinct age from table2 t2 where t2.COUNTRY=table1.COUNTRY and t2.NAME=table1.NAME);
or
UPDATE table1 SET table1.age = table2.age FROM table1 , table2 where table1.country = table2.country and table1.name = table2.name
Snowflake syntax is a bit different here. Something like this should work:
UPDATE table1 TA
SET TA.Age = TB.Age
FROM table2 TB
WHERE TA.country = TB.country
AND TA.name = TB.name;
As a note, the documentation on Snowflake is excellent. The syntax for this is located here: https://docs.snowflake.com/en/sql-reference/sql/update.html
How do you write a update statement with a Sub-Select in an Oracle Environment (SQL Developer)?
Example: UPDATE table SET column = (SELECT....)
Every time I try this it gives me ORA-01427 "Sub select returns more then one row" even if there is no WHERE clause..
Based on the understanding of your question I'd suggest use Merge statement.
Merge into Table1
Using
(SELECT * from table2 where condition) Temp
On (Table1.columname condition Temp.columname)
When matched Then update Set Table1.column_name = Temp.column_name;
Table1 is the table where you want to update the records.
Table2 is the table from which you want to get the data (The sub query which you are talking about )
Using this merge statement you will be able to update n number of rows.
If you want to update multiple rows, you can either use a MERGE statement (as in #jackkds7's answer above) or you can use a filter on your subselect:
UPDATE table t1
SET column = ( SELECT column FROM table2 t2 WHERE t2.key = t1.key );
If there aren't matches in table2 for all the records in table then column will be set to NULL for the non-matches. To avoid that, add a WHERE EXISTS clause:
UPDATE table t1
SET column = ( SELECT column FROM table2 t2 WHERE t2.key = t1.key )
WHERE EXISTS ( SELECT 1 FROM table2 t2 WHERE t2.key = t1.key );
Oh and in the event that key is not unique for table2, you can aggregate (up to you to figure out which function would be best):
UPDATE table t1
SET column = ( SELECT MAX(column) FROM table2 t2 WHERE t2.key = t1.key )
WHERE EXISTS ( SELECT 1 FROM table2 t2 WHERE t2.key = t1.key );
Hope this helps.
I think it would help if you posted your actual query.
In essence, the "inner" select would be executed for each row that would be updated. This inner select query is called a correlated subquery:
UPDATE table t SET t.column = (
select ot.othercolumn from othertable ot
where ot.fk = t.id --This is the correlation part, that finds
--he right value for the row you are currently updating
)
You must ensure the subquery you use will always return just a single row and a single column for every time it runs (that is, for every row that is going to be updated). If needed, you can use MAX(), or ROWNUM to ensure you always only get 1 value
More examples:
Using Correlated Subqueries
I'm working on SQL Server, Oracle, DB2 Database.
Currently, I'm performing the below 2 update statement to update one table.
Update 1:
UPDATE TABLE1
SET COL1=TABLE2.ATTRIBUTE1,
COL2=TABLE2.ATTRIBUTE2,
COL3=TABLE2.ATTRIBUTE3
FROM TABLE1
INNER JOIN TABLE2
ON COL1=TABLE2.PID1
AND COL2=TABLE2.PID2
WHERE ROWNUM<10
Update 2:
UPDATE TABLE1
SET COL1=’123-4567890-1’,
COL2=’0000000000’,
COL3=’CONSTANT FULL NAME’
WHERE NOT EXISTS (SELECT 1 FROM TABLE2 WHERE COL1=TABLE2.PID1)
Update 1, helps to updates the TABLE1 if the values match with Table2 and
Update 2, helps to update the TABLE1 if the values, not match with Table2
Is there any other way to convert two update statement into single UPDATE statement?
NOTE: We can use MERGE also, but MERGE will update if the data matched and will insert if the data not matched.
To have one update, you can use two LEFT JOINs, one for each table join condition. And then, in SET part, you use CASE WHEN ... THEN ... END checking if the PK from related joins IS NULL.
Something like below
UPDATE TABLE1
SET COL1=CASE
WHEN T1.PID1 IS NOT NULL THEN T1.ATTRIBUTE1
WHEN T2.PID1 IS NULL THEN ’123-4567890-1’
ELSE COL1
END,
etc.
FROM TABLE1
LEFT JOIN TABLE2 T1 ON COL1=T1.PID1 AND COL2=T1.PID2 AND ROWNUM < 10
LEFT JOIN TABLE2 T2 ON CEDULA=T2.PID1
WHERE T2.PID1 IS NULL OR T1.PID1 IS NOT NULL
However, having one UPDATE statement doesn't mean it will be faster - check the query plan and actual performance.
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
I want to do a query to update values that I forgot to copy over in a mass insert. However I'm not sure how to phrase it.
UPDATE table
SET text_field_1 = (SELECT text_field_2
FROM other_table
WHERE id = **current row in update statement, outside parens**.id )
How do I do this? It seems like a job for recursion.
Use:
UPDATE YOUR_TABLE
SET text_field_1 = (SELECT t.text_field_2
FROM other_table t
WHERE t.id = YOUR_TABLE.id)
Warning
If there's no supporting record in other_table, text_field_1 will be set to NULL.
Explanation
In standard SQL, you can't have table aliases on the table defined for the UPDATE (or DELETE) statement, so you need to use full table name to indicate the source of the column.
It's called a correlated subquery -- the correlation is be cause of the evaluation against the table from the outer query.
Clarification
MySQL (and SQL Server) support table aliases in UPDATE and DELETE statement, in addition to JOIN syntax:
UPDATE YOUR_TABLE a
JOIN OTHER_TABLE b ON b.id = a.id
SET a.text_field_1 = b.text_field_2
...is not identical to the provided query, because only the rows that match will be updated -- those that don't match, their text_field_1 values will remain untouched. This is equivalent to the provided query:
UPDATE YOUR_TABLE a
LEFT JOIN OTHER_TABLE b ON b.id = a.id
SET a.text_field_1 = b.text_field_2
If there is one ID field:
UPDATE updtable t1
SET t1.text_field_1 = (
SELECT t2.text_field_2
FROM seltable t2
WHERE t1.ID = t2.ID
)
;
UPDATE Table1, Tabl2
SET Table1.myField = Table2.SomeField
WHERE Table1.ID = Table2.ID
Note: I have not tried it.
This will only update records where IDs match.
Try this:
UPDATE table
SET text_field_1 = (SELECT text_field_2
FROM other_table
WHERE id = table.id )