How to update or copy data between 2 tables SQL - sql

I have table name Merge_table like :
Employee_Number MINISTRY_CODE BRANCH_SECRETARIAT_CODE
12 333 30
13 222 31
l want to copy the value of BRANCH_SECRETARIAT_CODE and paste it in different table called EMPLOYMENTS look like :
and ENTITY_BRANCH has null data
EMPLOYEE_NUMBER JOINING_DATE ENTITY_BRANCH
12 11/12/2006 null
13 01/11/2009 null
so, now i want to copy the value of BRANCH_SECRETARIAT_CODE from table1 to
table2 ENTITY_BRANCH for each employee according his EMPLOYEE_NUMBER

You can declare several tables in your UPDATE instruction and specify which column of which table has to be updated from the values of the other table.
In your case you have only 2 tables so the easier is to make an implict jointure using T1.Employee_Number = T2.Employee_Number :
UPDATE Table1 T1, Table2 T2
SET T2.ENTITY_BRANCH = T1.BRANCH_SECRETARIAT_CODE
WHERE T1.Employee_Number = T2.Employee_Number
I guessed this is for SQL server but this UPDATE statement will work also on MySQL and Access. Please edit your question to add the proper RDBMS tag.

Use the ANSI Standard's merge statement, which provides better join-style syntax for matching source and destination tables, supports complex source clauses, supports inserts too, etc.
merge into EMPLOYMENTS -- destination table
using Merge_table -- source table, or nested subquery, CTE, etc.
on Merge_table Employee_Number = EMPLOYMENTS.EMPLOYEE_NUMBER
-- any other criteria to determine which destination rows to affect
-- e.g.: and EMPLOYMENTS.EMPLOYEE_NUMBER is null
-- when not matched then
-- [...]
when matched then
update
set EMPLOYMENTS.ENTITY_BRANCH = Merge_table.BRANCH_SECRETARIAT_CODE;

Related

Improving insert query for SCD2 solution

I have two insert statements. The first query is to inserta new row if the id doesn't exist in the target table. The second query inserts to the target table only if the joined id hash value is different (indicates that the row has been updated in the source table) and the id in the source table is not null. These solutions are meant to be used for my SCD2 solution, which will be used for inserts of hundreds thousands of rows. I'm trying not to use the MERGE statement for practices.
The columns "Current" value 1 indicates that the row is new and 0 indicates that the row has expired. I use this information later to expire my rows in the target table with my update queries.
Besides indexing is there a more competent and effective way to improve my insert queries in a way that resembles the like of the SCD2 merge statement for inserting new/updated rows?
Query:
Query 1:
INSERT INTO TARGET
SELECT Name,Middlename,Age, 1 as current,Row_HashValue,id
from Source s
Where s.id not in (select id from TARGET) and s.id is not null
Query 2:
INSERT INTO TARGET
SELECT Name,Middlename,Age,1 as current ,Row_HashValue,id
FROM SOURCE s
LEFT JOIN TARGET t ON s.id = t.id
AND s.Row_HashValue = t.Row_HashValue
WHERE t.Row_HashValue IS NULL and s.ID IS NOT NULL
You can use WHERE NOT EXISTS, and have just one INSERT statement:
INSERT INTO TARGET
SELECT Name,Middlename,Age,1 as current ,Row_HashValue,id
FROM SOURCE s
WHERE NOT EXISTS (
SELECT 1
FROM TARGET t
WHERE s.id = t.id
AND s.Row_HashValue = t.Row_HashValue)
AND s.ID IS NOT NULL;

SQL Update only updates one row

This code is only updating one row, why? It has to do with one of the sub-queries but I am not sure. I'm thinking the WHERE..IN in the UPDATE statement but I am not sure.
UPDATE [sde].[sy1].[Valve_evw]
SET [sde].[sy1].[Valve_evw].[MA]
= (SELECT [sde].[sy1].[Valve_Join_evw].[MC]
FROM [sde].[sy1].[Valve_Join_evw])
WHERE [sde].[sy1].[Valve_evw].[PrimaryKey]
IN (SELECT [sde].[sy1].[Valve_Join_evw].[PrimaryKey]
FROM [sde].[sy1].[Valve_Join_evw]
WHERE [sde].[sy1].[Valve_Join_evw].[MA]
!= [sde].[sy1].[Valve_Join_evw].[MC])
Context:
What I am trying to do is update the MA column in Valve_evw using the MC column in Valve_Join_evw. The PrimaryKey in Valve_evw references equivalent rows as the PrimaryKey in Valve_Join_evw. As in, a single row in Valve_Join_evw will have the same PrimaryKey as a single row in Valve_evw, thus that equivalency can be used to update the records in Valve_evw. Also the MA column is equivalent in both tables. [Note: The Valve_Join_evw table is created with ESRI mapping software using the spatial relationship between the Valve_evw and a separate table, this is how the duplicate rows exist]
I am using database views (hence the '_evw') in SQL Server with a default INSTEAD OF UPDATE trigger. This combination, views and trigger, prevents the use of table joins to do this update. I have also tried MERGE but that will not work either. Therefore I am stuck with the ANSI standard, hence the sub-queries. This script runs with no errors but it only updates a single row whereas there are about 9000 thousand rows in the tables.
The output message:
(1 row(s) affected)
(0 row(s) affected)
First of all let's reduce the eye hurting SQL to what it really is:
update sde.sy1.valve_evw
set ma = (select mc from sde.sy1.valve_join_evw)
where primarykey in (select primarykey from sde.sy1.valve_join_evw where ma <> mc)
WHERE clause
We look for all primarykey in valve_join_evw where a record's ma <> mc. We update all valve_evw records with such primarykey.
SET clause
For a record we want to update, we set ma to the value found with:
select mc from sde.sy1.valve_join_evw
But this query has no where clause, so what value does it select to fill the record's ma field? It selects all mc from valve_join_evw, so the DBMS probably picks one of these values arbitrarily. (It would be better, it raised an error.)
Conclusion
It is very easy to see which records the statement will update.
Which primarykey:
select primarykey from sde.sy1.valve_join_evw where ma <> mc
Which rows:
select *
from sde.sy1.valve_evw
where primarykey in (select primarykey from sde.sy1.valve_join_evw where ma <> mc)
As to the SET clause: Add a WHERE clause to your subquery that relates the record to select to the record to update (same ma? same primarykey?) E.g.:
set ma =
(
select mc
from sde.sy1.valve_join_evw vj
where vj.primarykey = valve_evw.primarykey
and vj.ma = valve_evw.ma
)
Hi there i recomend first to do the select statement and when you are ok with te records retrieved use the same where for the update statement
Here is what the final script looks like.
UPDATE [Valve_evw]
SET [Valve_evw].[MA] =
(
SELECT [Valve_Join_evw].[MC]
FROM [Valve_Join_evw]
WHERE[Valve_Join_evw].[PrimaryKey] = [Valve_evw].[PrimaryKey]
)
WHERE [Valve_evw].[PrimaryKey]
IN (
SELECT [Valve_Join_evw].[PrimaryKey]
FROM [Valve_Join_evw]
WHERE [Valve_Join_evw].[MA]
!= [Valve_Join_evw].[MC]
);

Merge statement inserting instead of updating in SQL Server

I'm using SQL Server 2008 and I'm trying to load a new (target) table from a staging (source) table. The target table is empty.
I think since my target table is empty, the MERGE statement skips the WHEN MATCHED part i.e. result of INNER JOIN is NULL and so nothing is UPDATED, and it just proceed to the WHEN NOT MATCHED BY TARGET part (LEFT OUTER JOIN) an inserts all the records in the staging table.
My target table looks exactly similar to my staging table (rows #1 and #4). There should be only 3 rows in the target table (3 inserts and one update for row #4). So, I'm not sure whats going on.
FileID client_id account_name account_currency creation_date last_modified
210 12345 Cars USD 2013-11-21 2013-11-27
211 23498 Truck USD 2013-09-22 2013-11-27
212 97652 Cars - 1 USD 2013-09-17 2013-11-27
210 12345 Cars JPY 2013-11-21 2013-11-29
QUERY
MERGE [AccountSettings] AS tgt -- RIGHT TABLE
USING
(
SELECT * FROM [AccountSettings_Staging]
) AS src -- LEFT TABLE
ON src.client_id = tgt.client_id
AND src.account_name = tgt.account_name
WHEN MATCHED -- INNER JOIN
THEN UPDATE
SET
tgt.[FileID] = src.[FileID]
,tgt.[account_currency] = src.[account_currency]
,tgt.[creation_date] = src.[creation_date]
,tgt.[last_modified] = src.[last_modified]
WHEN NOT MATCHED BY TARGET -- left outer join: A row from the source that has no corresponding row in the target
THEN INSERT
(
[FileID],
[client_id],
[account_name],
[account_currency],
[creation_date],
[last_modified]
)
VALUES
(
src.[FileID],
src.[client_id],
src.[account_name],
src.[account_currency],
src.[creation_date],
src.[last_modified]
);
Since the target table is empty, using MERGE seems to me like hiring a plumber to pour you a glass of water. And MERGE operates only one branch, independently, for every row of a table - it can't see that the key is repeated and so perform an insert and then an update - this betrays that you think SQL always operates on a row-by-row basis, when in fact most operations are performed on the entire set at once.
Why not just insert only the most recent row:
;WITH cte AS
(
SELECT FileID, ... other columns ...,
rn = ROW_NUMBER() OVER (PARTITION BY FileID ORDER BY last_modified DESC)
FROM dbo.AccountSettings_Staging
)
INSERT dbo.AccountSettings(FileID, ... other columns ...)
SELECT FileID, ... other columns ...
FROM cte WHERE rn = 1;
If you have potential for ties on the most recent last_modified, you'll need to find another tie-breaker (not obvious from your sample data).
For future versions, I would say run an UPDATE first:
UPDATE a SET client_id = s.client_id /* , other columns that can change */
FROM dbo.AccountSettings AS a
INNER JOIN dbo.AccountSettings_Staging AS s
ON a.FileID = s.FileID;
(Of course, this will choose an arbitrary row if the source contains multiple rows with the same FileID - you may want to use a CTE here too to make the choice predictable.)
Then add this clause to the INSERT CTE above:
FROM dbo.AccountSettings_Staging AS s
WHERE NOT EXISTS (SELECT 1 FROM dbo.AccountSettings
WHERE FileID = s.FileID);
Wrap it all in a transaction at the appropriate isolation level, and you are still avoiding a ton of complicated MERGE syntax, potential bugs, etc.
I think since my target table is empty, the MERGE statement skips the WHEN MATCHED part
Well, that's correct, but it's by design - MERGE is not a "progressive" merge. It does not go row-by-row to see if records inserted as part of the MERGE should now be updated. It processes the source in "batches" based on whether or not a match was found in the destination.
You'll need to deal with the "duplicate" records at the source before attempting the MERGE.

Writing a single UPDATE statement that prevents duplicates

I've been trying for a few hours (probably more than I needed to) to figure out the best way to write an update sql query that will dissallow duplicates on the column I am updating.
Meaning, if TableA.ColA already has a name 'TEST1', then when I'm changing another record, then I simply can't pick a value for ColA to be 'TEST1'.
It's pretty easy to simply just separate the query into a select, and use a server layer code that would allow conditional logic:
SELECT ID, NAME FROM TABLEA WHERE NAME = 'TEST1'
IF TableA.recordcount > 0 then
UPDATE SET NAME = 'TEST1' WHERE ID = 1234
END IF
But I'm more interested to see if these two queries can be combined into a single query.
I am using Oracle to figure things out, but I'd love to see a SQL Server query as well. I figured a MERGE statement can work, but for obvious reasons you can't have the clause:
..etc.. WHEN NOT MATCHED UPDATE SET ..etc.. WHERE ID = 1234
AND you can't update a column if it's mentioned in the join (oracle limitation but not limited to SQL Server)
ALSO, I know you can put a constraint on a column that prevents duplicate values, but I'd be interested to see if there is such a query that can do this without using constraint.
Here is an example start-up attempt on my end just to see what I can come up with (explanations on it failed is not necessary):
ERROR: ORA-01732: data manipulation operation not legal on this view
UPDATE (
SELECT d.NAME, ch.NAME FROM (
SELECT 'test1' AS NAME, '2722' AS ID
FROM DUAL
) d
LEFT JOIN TABLEA a
ON UPPER(a.name) = UPPER(d.name)
)
SET a.name = 'test2'
WHERE a.name is null and a.id = d.id
I have tried merge, but just gave up thinking it's not possible. I've also considered not exists (but I'd have to be careful since I might accidentally update every other record that doesn't match a criteria)
It should be straightforward:
update personnel
set personnel_number = 'xyz'
where person_id = 1001
and not exists (select * from personnel where personnel_number = 'xyz');
If I understand correctly, you want to conditionally update a field, assuming the value is not found. The following query does this. It should work in both SQL Server and Oracle:
update table1
set name = 'Test1'
where (select count(*) from table1 where name = 'Test1') > 0 and
id = 1234

MS SQL update table with multiple conditions

Been reading this site for answers for quite a while and now asking my first question!
I'm using SQL Server
I have two tables, ABC and ABC_Temp.
The contents are inserted into the ABC_Temp first before making its way to ABC.
Table ABC and ABC_Temp have the same columns, except that ABC_Temp has an extra column called LastUpdatedDate, which contains the date of the last update. Because ABC_Temp can have more than 1 of the same record, it has a composite key of the item number and the last updated date.
The columns are: ItemNo | Price | Qty and ABC_Temp has an extra column: LastUpdatedDate
I want to create a statement that follows the following conditions:
Check if each of the attributes of ABC differ from the value of ABC_Temp for records with the same key, if so then do the update (Even if only one attribute is different, all other attributes can be updated as well)
Only update those that need changes, if the record is the same, then it would not update.
Since an item can have more than one record in ABC_Temp I only want the latest updated one to be updated to ABC
I am currently using 2005 (I think, not at work at the moment).
This will be in a stored procedure and is called inside the VBscript scheduled task. So I believe it is a once time thing. Also I'm not trying to sync the two tables, as the contents of ABC_Temp would only contain new records bulk inserted from a text file through BCP. For the sake of context, this will be used with in conjunction with an insert stored proc that checks if records exist.
UPDATE
ABC
SET
price = T1.price,
qty = T1.qty
FROM
ABC
INNER JOIN ABC_Temp T1 ON
T1.item_no = ABC.item_no
LEFT OUTER JOIN ABC_Temp T2 ON
T2.item_no = T1.item_no AND
T2.last_updated_date > T1.last_updated_date
WHERE
T2.item_no IS NULL AND
(
T1.price <> ABC.price OR
T1.qty <> ABC.qty
)
If NULL values are possible in the price or qty columns then you will need to account for that. In this case I would probably change the inequality statements to look like this:
COALESCE(T1.price, -1) <> COALESCE(ABC.price, -1)
This assumes that -1 is not a valid value in the data, so you don't have to worry about it actually appearing there.
Also, is ABC_Temp really a temporary table that's just loaded long enough to get the values into ABC? If not then you are storing duplicate data in multiple places, which is a bad idea. The first problem is that now you need these kinds of update scenarios. There are other issues that you might run into, such as inconsistencies in the data, etc.
You could use cross apply to seek the last row in ABC_Temp with the same key. Use a where clause to filter out rows with no differences:
update abc
set col1 = latest.col1
, col2 = latest.col2
, col3 = latest.col3
from ABC abc
cross apply
(
select top 1 *
from ABC_Temp tmp
where abc.key = tmp.key
order by
tmp.LastUpdatedDate desc
) latest
where abc.col1 <> latest.col1
or (abc.col2 <> latest.col2
or (abc.col1 is null and latest.col2 is not null)
or (abc.col1 is not null and latest.col2 is null))
or abc.col3 <> latest.col3
In the example, only col2 is nullable. Since null <> 1 is not true, you have to check differences involving null using the is null syntax.