I have two tables DESTINATION and STAGING with the same structure and very similar data. Only in few rows I've some differences. For example:
In DESTINATION table I have rows:
ID, Name, ShortName
1, Name1, N1
2, Name2, N2
3, Name3, N3
In STAGING table I have:
ID, Name, ShortName
1, Name1, N1
2, Name2, newN2
3, Name3, N3
How to update DESTINATION table only 2nd row with new data in ShortName column (newN2) and doing nothing with rows without any differences?
You can update like this
UPDATE A
SET A.shortname = B.ShortName
FROM DESTINATION A
INNER JOIN STAGING B
ON A.ID=B.ID
AND A.shortname<>B.ShortName;
Assuming you are using SQL Server, this is how you can do it:
update d
set ShortName = s.ShortName
from destination d
join staging s
on d.id = s.id
and d.ShortName != s.ShortName;
SQLFiddle: http://sqlfiddle.com/#!6/0119c/1
Try using an UPDATE...FROM:
UPDATE Destination
SET ShortName = stg.ShortName
FROM Staging stg -- Reference "staging" table
WHERE Destination.ID = stg.ID
AND Destination.ShortName <> stg.ShortName; -- Only update rows with "ShortName" values don't match
This will update your DESTINATION table using the data in your STAGING table. This works on Postgres.
http://www.sqlfiddle.com/#!17/ad370/1
In MS SQL , using merge script is best way of updating table. You can look up on multiple columns.
MERGE dbo.Destination AS TARGET
USING dbo.staging AS SOURCE
ON (TARGET.[id] = SOURCE.[id])
WHEN MATCHED
THEN
UPDATE
SET TARGET.[shortName] = SOURCE.[shortName]
WHEN NOT MATCHED BY TARGET
THEN
INSERT (
[Id]
,[Name]
,[shortName]
)
VALUES (
SOURCE.[Id]
,SOURCE.[Name]
,SOURCE.[shortName]
)
Related
I have query operation like below. How can I write single merge query where DWB_ACCT_CLSS_STG2 is stage table and DWB_ACCT_CLASS is the target table?
INSERT INTO DWB_ACCT_CLSS_STG2
SELECT
STG1.ACCT_CLSS_CD
,STG1.ACCT_CLSS_NME
,STG1.ACCT_CLSS_DSC
,STG1.PROCESS_NAME
,STG1.EXECUTION_ID
,STG1.FILE_ID
,STG1.FILE_DATE
,STG1.DATA_DATE
,STG1.LOAD_USER
,STG1.LOAD_DATE
,STG1.DW_LAST_UPDATE_TIME
FROM DWB_ACCT_CLSS_STG1 STG1
LEFT OUTER JOIN DWB_ACCT_CLASS TRGT
ON STG1.ACCT_TYP_CD = TRGT.ACCT_TYP_CD
WHERE TRGT.ACCT_TYP_CD IS NULL;
INSERT INTO DWB_ACCT_TYP_STG2
SELECT
STG1.ACCT_TYP_CD
,STG1.ACCT_TYP_DSC
,STG1.PROCESS_NAME
,STG1.EXECUTION_ID
,STG1.FILE_ID
,STG1.FILE_DATE
,STG1.DATA_DATE
,STG1.LOAD_USER
,STG1.LOAD_DATE
,STG1.DW_LAST_UPDATE_TIME
FROM DWB_ACCT_CLASS_STG1 STG1
INNER JOIN DWB_ACCT_CLASS TRGT
ON STG1.ACCT_TYP_CD = TRGT.ACCT_TYP_CD
WHERE (
STG1.ACCT_TYP_DSC <> TRGT.ACCT_TYP_DSC
);
DELETE FROM DWB_ACCT_class WHERE (ACCT_TYP_CD) IN (SELECT ACCT_TYP_CD FROM DWB_ACCT_CLASS_STG2 STG2);
INSERT INTO DWB_ACCT_CLASS SELECT * FROM DWB_ACCT_CLASS_STG2;
I think you don't even need staging table DWB_ACCT_CLSS_STG2. You can use the following MERGE command:
MERGE INTO DWB_ACCT_CLASS TRGT
USING (SELECT STG1.ACCT_CLSS_CD,
STG1.ACCT_CLSS_NME,
STG1.ACCT_CLSS_DSC,
STG1.PROCESS_NAME,
STG1.EXECUTION_ID,
STG1.FILE_ID,
STG1.FILE_DATE,
STG1.DATA_DATE,
STG1.LOAD_USER,
STG1.LOAD_DATE,
STG1.DW_LAST_UPDATE_TIME
FROM DWB_ACCT_CLSS_STG1 STG1) STG1
ON (STG1.ACCT_TYP_CD = TRGT.ACCT_TYP_CD
AND STG1.ACCT_TYP_DSC <> TRGT.ACCT_TYP_DSC)
WHEN MATCHED THEN
UPDATE SET TRGT.ACCT_CLSS_CD = STG1.ACCT_CLSS_CD, ..... -- EXCEPT ACCT_TYP_CD AND ACCT_TYP_DSC COLUMN
WHEN NOT MATCHED THEN
INSERT (... COLUMNS OF THE DWB_ACCT_CLASS TABLE ....)
VALUES (STG1.ACCT_CLSS_CD,STG1.ACCT_CLSS_NME,.....STG1.DW_LAST_UPDATE_TIME) -- ORDER MUST MATCH WITH THE INSERT CLASUE COLUMN LIST
How to solve the error:
The MERGE statement attempted to UPDATE or DELETE the same row more than once. This happens when a target row matches more than one source row. A MERGE statement cannot UPDATE/DELETE the same row of the target table multiple times. Refine the ON clause to ensure a target row matches at most one source row, or use the GROUP BY clause to group the source rows.
merge CARD_ALERTS as t
using #tblAlerts as s
on (t.Id = s.AlertId and t.CardId = s.CardId)
when not matched by target
then insert(Id, ExternalCodeHolder, CardId, IsCardOwner, IBAN, PAN, MinAmount, Currency, ByEmail, BySMS, IssueDate, IsActive)
values(s.AlertId, s.ExternalCodeHolder, s.CardId, s.IsCardOwner, s.IBAN, s.PAN, s.MinAmount, s.Currency, s.ByEmail, s.BySMS, getdate(), 1)
when matched
then update set t.ByEmail = s.ByEmail, t.BySMS = s.BySMS, IsActive = 1, t.MinAmount = s.MinAmount
when not matched by source and t.Id=#AlertId
then update set t.IsActive = 3
As explained in the error message and in the comments, multiple rows of the source table correspond to the same row in the target one.
This will show you which rows of the target table have multiple rows from the source table that try to update them:
select t.Id,t.CardId,count(*) as [count]
from CARD_ALERTS as t
inner join #tblAlerts as s
on (t.Id = s.AlertId and t.CardId = s.CardId)
group by t.Id,t.CardId
having count(*)>1
This will also show you the multiple rows of the source table:
select t.*,s.*
from CARD_ALERTS as t
inner join #tblAlerts as s on (t.Id = s.AlertId and t.CardId = s.CardId)
inner join
(
select ca.Id,ca.CardId
from CARD_ALERTS as ca
inner join #tblAlerts as s
on (ca.Id = s.AlertId and ca.CardId = s.CardId)
group by ca.Id,ca.CardId
having count(*)>1
)tkey on t.Id=tkey.Id and t.CardId=tkey.CardId
So, i have two tables, the target table and the source one. I need to delete the rows that exists in the target table, but doesn't exists in the source table.
And the code:
MERGE INTO (SELECT id_car_bk, car_brand_bk, car_type_bk, new_car
FROM car_catalog_backup) CB
USING (SELECT id_car, car_brand, car_type FROM car_catalog) C
ON (CB.id_car_bk = b.id_car)
WHEN NOT MATCHED THEN
INSERT
(CB.id_car_bk, CB.car_brand_bk, CB.car_type_bk)
VALUES
(C.id_car, C.car_brand, C.car_type)
WHEN MATCHED THEN
UPDATE SET CB.car_brand_bk = C.car_brand;
You can use
DELETE car_catalog_backup b
WHERE not exists
( SELECT 0
FROM car_catalog c
WHERE b.id_car_bk = c.id_car );
or
DELETE car_catalog_backup b
WHERE b.id_car_bk not in
( SELECT c.id_car
FROM car_catalog c );
assuming car_catalog is the source, and car_catalog_backup is the target. The First one is preferable, since it's more performant.
If your aim is to find out with a MERGE statement similar to your case, then use the following
MERGE INTO car_catalog_backup a
USING (SELECT id_car, car_brand, car_type, car_brand_bk
FROM car_catalog
JOIN car_catalog_backup
ON id_car_bk = id_car
) b
ON (a.id_car_bk = b.id_car)
WHEN MATCHED THEN
UPDATE SET a.new_car = 1
DELETE
WHERE a.car_brand_bk != b.car_brand
WHEN NOT MATCHED THEN
INSERT
(id_car_bk, car_brand_bk, car_type_bk)
VALUES
(b.id_car, b.car_brand, b.car_type)
to delete the records matched for id columns ( a.id_car_bk = b.id_car ) but not matched for brand code columns ( a.car_brand_bk != car_brand ) as an example.
Demo
Delete from target
Where not exists
(
Select 1
From source
Where join of source and target
)
With a left join:
DELETE target
FROM target LEFT JOIN source
ON target.someid = source.otherid
WHERE source.otherid IS NULL;
I am going to explain again what I am trying to do in hopes that you can help.
Table 1 has 4061 rows with columns that include
[Name],[Address1],[Address2],[Address3],[City],[State],[Zip],[Country],[Phone]
and 20 other columns. Table 1 is data that needs to be deidentified. Table 1 has 1534 distinct [Name] rows out of 4061 rows total.
Table 2 has auto generated data which includes the same columns. I would like to replace the above mentioned columns in table 1 with data from table 2. I want to select distinct based on [Name] from table one and then [Name],[Address1],[Address2],[Address3],[City],[State],[Zip],[Country],[Phone] with a new set of distinct data from table 2.
I do not want to just update each row with a new address as that will screw up the data consistency. By replacing only distinct this will allow me to preserve the data consistency while changing the row data in table 1. When I am done I would like to have 1534 distinct new de-identified [Name] [Address1],[Address2],[Address3],[City],[State],[Zip],[Country],[Phone] in table 1 from table 2.
You would use join in the update. You can generate a join key for 1500 rows using row_number():
update toupdate
set t.address = f.address
from (select t.*, row_number() over (order by newid()) as seqnum
from table t
) toupdate join
(select f.*, row_number() over (order by newid()) as seqnum
fake f
) f
on toupdate.seqnum = f.seqnum and t.seqnum <= 1500;
Here is how I ended up doing it.
First I ran a statement to select distinct and inserted it into a table.
Select Distinct [Name],[Address1],[City],[State],[Zip],[Country],[Phone]
INTO APMAST2
FROM APMAST
I then added name2 column in APMAST2 and used a statement to create a sequential id field into APMAST2.
DECLARE #id INT
SET #id = 0
UPDATE APMAST2
SET #id = id = #id + 1
GO
Now I have my distinct info plus a blank name field and a sequential ID field in APMAST2. Now I can join this date with my fakenames table which I generated from. HERE using their bulk tool.
Using a Join Statement I joined my fake data with APMAST2
Update dbo.APMAST2
SET dbo.APMAST2.Name = dbo.fakenames.company,
dbo.APMAST2.Address1 = dbo.fakenames.streetaddress,
dbo.APMAST2.City = dbo.fakenames.City,
dbo.APMAST2.State = dbo.fakenames.State,
dbo.APMAST2.Zip = dbo.fakenames.zipcode,
dbo.APMAST2.Country = dbo.fakenames.countryfull,
dbo.APMAST2.Phone = dbo.fakenames.telephonenumber
FROM
dbo.APMAST2
INNER JOIN
dbo.fakenames
ON dbo.fakenames.number = dbo.APMAST2.id
Now I have my fake data loaded but I kept my original Name field so I could reload this data into my full table ARMAST so now I can do a join between ARMAST2 and ARMAST.
Update dbo.APMAST
SET dbo.APMAST.Name = dbo.APMAST2.Name,
dbo.APMAST.Address1 = dbo.APMAST2.Address1,
dbo.APMAST.City = dbo.APMAST2.City,
dbo.APMAST.State = dbo.APMAST2.State,
dbo.APMAST.Zip = dbo.APMAST2.Zip,
dbo.APMAST.Country = dbo.APMAST2.Country,
dbo.APMAST.Phone = dbo.APMAST2.Phone
FROM
dbo.APMAST
INNER JOIN
dbo.apmast2
ON dbo.apmast.name = dbo.APMAST2.name2
Now my original table has all fake data in it but it keeps the integrity it had , well most of it, so the data looks good when reported on but is de-identified. You can now remove APMAST2 or keep it if you need to match this with other data later on. I know this is long and I am sure there is a better way to do it but this is how I did it, suggestions welcome.
I have two tables. user and user_new
the user contains the old data.
the user_new contains the new data.
I want to sync the user_new to user.
if the data exist in user_new and not exist in user,then insert to user.
if the data exist in user and user_new, then update.(compare with the column id)
what's the fast sql to do it?
This works on any server version -
-- 1) Insert new record
INSERT INTO old_table(id, column)
SELECT n.id, n.column
FROM new_table n
LEFT JOIN old_table o ON n.id = o.id
WHERE o.id IS NULL
-- 2) Update existed record
UPDATE o
SET column = n.column
FROM old_table o
JOIN new_table n ON n.id = o.id
From Sql Server 2008 onwards you can use Merge syntax
MERGE user target
USING user_new source
ON taget.ID = source.ID
WHEN MATCHED THEN
UPDATE
SET target.Column= source.Column1,target.column2=source.column2
WHEN NOT MATCHED BY TARGET THEN
INSERT (ID,Column1,Column2)
VALUES (source.ID,source.column1,source.column2);
or you can use the below query
INSERT INTO user(ID,column1,column2)
SELECT ID,column1,column2 FROM user_new AS source
WHERE NOT EXISTS (SELECT * FROM user WHERE ID = source.ID);
UPDATE target SET ...
FROM user AS target
INNER JOIN user_new AS source
ON target.ID = source.ID;
Sounds like you might need a Merge if you have sql server 2008+.
You can't do insert and update in a single query you have to do in seperate
select * from user where user_id not in (select user_new.user_id from user_new )
this query results the data for insert query similarly u have to update by replacing not in to in