Compare data between two tables and update one table data - sql

I have two tables and i need to compare data and update one table records. Please let me know how this can be done, i am trying to not use merge. This is the scenario
Proj1 Table
This is the first table where data needs to be synchronized
ID Text
1 R1
2 R2
3 R3
Proj2 Table
This is the table where data updates are taking place
ID Text Active
3 R1 1
4 R3 1
5 R4 1
After a compare is done on Text field between both these tables result should be similar to below. We are syncing data in Proj2 to similar to Proj1.
ID Text Active
3 R1 1 (Ignore as it exists in both tables)
4 R3 1 (Ignore as it exists in both tables)
5 R4 0 (Update to inactive as it does not exist Proj1 table)
6 R2 1 (Insert as it does not exist in Proj2 table)

If you really can't use MERGE, you can simply split it into an update and an insert query :
INSERT INTO #Proj2(Text, Active)
SELECT Text,1 FROM #Proj1 p1
WHERE NOT EXISTS(
SELECT *
FROM #Proj2 p2
WHERE p2.Text = p1.Text
);
UPDATE
p2
SET
p2.Active = CASE WHEN p1.id is null THEN 0 ELSE 1 END
FROM
#Proj2 p2
LEFT JOIN
#Proj1 p1
ON
p2.Text = p1.Text;
This assumes that your ID is an auto-increment.
Thsi is pretty much like Zak's new answer, but with the 2 update queries merged.

merge [Proj2Table] target
using ( select [id], [text] from [Proj1Table] ) source ([id], [text] )
on target.[id] = source.[id]
when not matched by source
then update
set target.[Active] = 0
when not matched by target
then insert
([id], [text] )
values( source.[id], source.[text] )

-- update records in table 2 that cannot be found in table 1
update P2
set P2.[Active] = 0
from [Proj2Table] as P2
left join [Proj1TAble] as P1 on P1.[text] = P2.[text]
where P1.[id] is null;
-- update records in table 2 that can be found in table 1
update P2
set P2.[Active] = 1
from [Proj2Table] as P2
join [Proj1TAble] as P1 on P1.[text] = P2.[text];
-- insert missing records from table 1 into table 2 with active bit set
insert into [Proj2Table] ([id] , [text] , [active] )
select [id] , [text] , [active] = 1
from [Proj1Table] as P1
where not exists (select 1 from [Proj2Table] as P2 where P2.[text] = P1.[text])
;
Not sure if [id] column should match or is a pk/identity/sequence

Another approach would be to use a cursor, if you really don't want to use MERGE
-declare a cursor on the first table and scroll the row, then for each row selct the corresponding row in the second table and ac accordingly, if found/not found...
not the best performance-related suggestion though...

Related

how to properly merge these 2 query into one update?

This currently work but I would like to change the update statement to include the action of the insert below it, is it posssible?
UPDATE cas
SET [Locked] = CASE WHEN cas.Locked <> #TargetState AND cas.LastChanged = filter.SourceDateTime THEN #TargetState ELSE cas.[Locked] end,
OUTPUT inserted.Id, inserted.Locked, CASE WHEN inserted.Locked = #TargetState AND
inserted.LastChanged = filter.SourceDateTime THEN 1
WHEN inserted.LastChanged <> filter.SourceDateTime THEN -1 -- out of sync
WHEN deleted.Locked = #TargetState THEN -2 -- was not in a good state
ELSE 0 END --generic failure
INTO #OUTPUT
FROM dbo.Target cas WITH(READPAST, UPDLOCK, ROWLOCK) INNER JOIN #table filter ON cas.Id = filter.Id
INSERT INTO #OUTPUT
SELECT filter.id, NULL, when cas.id is not null -3 -- row was/is locked
else -4 end --not found
FROM #table filter left join dbo.target cas with(nolock) on filter.id = cas.id
WHERE NOT EXISTS (SELECT 1 FROM #OUTPUT result WHERE filter.id = result.UpdatedId)
I do not think what you want is possible.
You start with a table to be updated. Let’s say this table contains a set of IDs, say, 1 to 6
You join onto a temp table containing a different set of IDs that may partially overlap (say, 4 to 9)
You issue the update using an inner join. Only rows 4 to 6 are updated
The output clause picks up data only for modified rows, so you only get data for rows 4 to 6
If you flipped this to an outer join (such that all temp table rows are selected), you still only update rows 4 to 6, and the output clause still only kicks out data for rows 4 to 6
So, no, I see no way of achieving this goal in a single SQL statement.

How to convert this Access inner join query into SQL Server?

I want to write an update statement that compares the new code table to the old code by joining on the MASTER_ID. Then If the NEW_CODE.CODE = 1234 AND OLD_CODE.CODE_ID is not in the CODE_MAPPING table then SET NEW_CODE.CODE_ID = OLD_CODE.CODE_ID AND NEW_CODE.SEQ_NO = OLD_CODE.SEQ_NO. This is what I have so far but it does not seem to correctly update if I get a null value in the CODE_ID column. I think I need to do a self join but nothing I do seems to work any help would be greatly appreciated.
I have three tables with the following data:
**NEW_CODE**
MASTER_ID SEQ_NO CODE_ID CODE
100 0 XX 1234
200 0 5555
300 0 XX 1234
300 0 XX 1234
**OLD_CODE**
MASTER_ID SEQ_NO CODE_ID
100 1 D1
200 1 A1
300 1
300 2 Z1
**CODE_MAPPING**
CODE_ID
A1
B1
C1
D1
UPDATE STATEMENT:
UPDATE NEW
SET SEQ_NO = OLD.SEQ_NO,
CODE_ID = OLD.CODE_ID
FROM NEW_CODE NEW
INNER JOIN OLD_CODE OLD
ON NEW.MASTER_ID = OLD.MASTER_ID
LEFT JOIN CODE_MAPPING CM
ON OLD.CODE_ID = CM.CODE_ID
WHERE NEW.CODE = 1234
AND CM.CODE_ID IS NULL
The updated table should look like this:
**UPDATED NEW_CODE**
MASTER_ID SEQ_NO CODE_ID CODE
100 0 XX 1234
200 0 5555
300 1 1234
300 2 Z1 1234
So the only thing that should update is the two rows with a MASTER_ID of 300. But for some reason I am getting the following result instead:
**CURRENT RESULT NEW_CODE**
MASTER_ID SEQ_NO CODE_ID CODE
100 0 XX 1234
200 0 5555
300 2 Z1 1234
300 2 Z1 1234
As you have two rows with same identifier (MASTER_ID), tsql is needed instead of a simple statement.
Precondition for rows to update:
Every row in NEW could be matched to one row in OLD and reverse.
We need a criteria to identify updated rows in NEW. I use SEQ_NO=0 here.
a unique key for each row in OLD and NEW is needed. Your example data don't contain a unique key. So I add one to OLD and one to NEW. It can be dropped in the end.
alter table OLD add IDX int IDENTITY(1,1)
alter table NEW add IDX int IDENTITY(1,1)
declare a cursor on OLD (only rows to be updated)
DECLARE #IdxOld int
DECLARE #IdxNew int
DECLARE MY_CURSOR CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT idx
FROM OLD
WHERE NOT EXISTS (SELECT * FROM CODE_MAPPING CM
WHERE OLD.CODE_ID = CM.CODE_ID)
do foreach on OLD and update NEW
OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO #IdxId
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #IdxNew = Min(Idx) -- min to get only one row
FROM NEW
INNER JOIN OLD_CODE OLD ON NEW.MASTER_ID = OLD.MASTER_ID
WHERE NEW.CODE = 1234
AND OLD.IDX = #IdxOld -- current row from old
AND SEQ_NO=0 -- row not updated yet
UPDATE NEW
SET SEQ_NO = OLD.SEQ_NO,
CODE_ID = OLD.CODE_ID
FROM NEW_CODE NEW
INNER JOIN OLD_CODE OLD ON NEW.MASTER_ID = OLD.MASTER_ID
WHERE NEW.IDX = #IdxNew
AND OLD.IDX = #IdxOld
FETCH NEXT FROM MY_CURSOR INTO #IdxId
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR
Drop IDX columns
alter table OLD drop COLUMN IDX
alter table NEW drop COLUMN IDX
The aliases do not match in the update . . . that is new is not new_code. Try this:
UPDATE NC
SET SEQ_NO = OC.SEQ_NO,
CODE_ID = OC.CODE_ID
FROM NEW_CODE NC INNER JOIN
OLD_CODE OC
ON NC.MASTER_ID = OC.MASTER_ID LEFT JOIN
CODE_MAPPING CM
ON OC.CODE_ID = CM.CODE_ID
WHERE NC.CODE = 1234 AND CM.CODE_ID IS NULL;

SQL Update Skipping duplicates

Table 1 looks like the following.
ID SIZE TYPE SERIAL
1 4 W-meter1 123456
2 5 W-meter2 123456
3 4 W-meter 585858
4 4 W-Meter 398574
As you can see. Items 1 and 2 both have the same Serial Number. I have an innerjoin update statement that will update the UniqueID on these devices based on linking their serial number to the list.
What I would like to do. Is modify by hand the items with duplicate serial numbers and scripted update the ones that are unique. Im presuming I have to reference the distinct command here somewhere buy not sure.
This is my update statement as is. Pretty simple and straight forward.
update UM00400
Set um00400.umEquipmentID = tb2.MIUNo
from UM00400 tb1
inner join AA_Meters tb2 on
tb1.umSerialNumber = tb2.Old_Serial_Num
where tb1.umSerialNumber <> tb2.New_Serial_Num
;WITH CTE
AS
(
SELECT * , rn = ROW_NUMBER() OVER (PARTITION BY SERIAL ORDER BY SERIAL)
FROM UM00400
)
UPDATE CTE
SET CTE.umEquipmentID = tb2.MIUNo
inner join AA_Meters tb2
on CTE.umSerialNumber = tb2.Old_Serial_Num
where tb1.umSerialNumber <> tb2.New_Serial_Num
AND CTE.rn = 1
This will update the 1st record of multiple records with the same SERIAL.
If i understand your question correctly below query will help you out :
;WITH CTE AS
(
// getting those serial numbers which are not duplicated
SELECT umSerialNumber,COUNT(umSerialNumber) as CountOfSerialNumber
FROM UM00400
GROUP BY umSerialNumber
HAVING COUNT(umSerialNumber) = 1
)
UPDATE A SET A.umEquipmentID = C.MIUNo
FROM UM00400 A
INNER JOIN CTE B ON A.umSerialNumber = B.umSerialNumber
INNER JOIN AA_Meters C ON A.umSerialNumber = C.Old_Serial_Num

modify value on a table with some conditions

I need some help with mssql. I dont know anything from mssql query, but I know some programing.
I have 2 tables, TableA and TableB each one has 2 columns
ColumnA1, ColumnA2, ColumnB1 and ColumnB2
Something like this
create table DB.dbo.TableA
(ColumnA1 varchar(10),
ColumnA2 int)
create table DB.dbo.TableB
(ColumnB1 varchar(10),
ColumnB2 int)
And I need to check if exist a row in TableA where ColumnA2>0
if so, then,
IF any of those possible rows also exists in ColumnB1,
update ColumnB2=ColumnB2+ColumnA2 and set ColumnA2=0
ELSE
insert a new row in TableB with ColumnB1=ColumnA1 and ColumnB2=ColumnA1 and set ColumnA2=0
I don't even know where to start, I tryed to explain it the best way I could.
EDIT:
Before the script
TableA:
ColumnA1 ColumnA2
John 0
Sam 1
Mark 1
TableB:
ColumnB1 ColumnB2
Sam 5
After the script should be something like this:
TableA:
ColumnA1 ColumnA2
John 0
Sam 0
Mark 0
TableB:
ColumnB1 ColumnB2
Sam 6
Mark 1
You should use the MERGE syntax
merge TableB
using
(select * from tablea where ColumnA2>0) source
ON source.ColumnA1 = tableb.ColumnB1
when matched then
update
set tableb.ColumnB2 = tableb.ColumnB2 + source.ColumnA2
when not matched by target Then
insert(ColumnB1,ColumnB2)
values (source.ColumnA1,source.ColumnA2)
I couldn't get your requirement properly but the below code using Merge will atleast help you in the right direction .
MERGE TableB B
USING TableA A
ON A.ColumnA1 = B.ColumnB1 //The columns which related to both the tables
WHEN MATCHED AND
A.ColumnA2>0 THEN
UPDATE
SET B.ColumnB2 = B.ColumnB2 + A.ColumnA2
when not matched by target Then
Insert(ColumnB1,ColumnB2)
values (A.ColumnA1,A.ColumnB2)
Output A.ColumnA1 into #t;
Update TableA
Set ColumnA2=0
WHERE ColumnA1 in (SELECT ColumnA1
FROM #t);

What is the correct pattern for working without deferred constraints?

I am using databases that aren't Oracle or Postgresql, which means I don't have access to deferred constraints, which means that constraints must be valid at all times (instead of just on commit).
Let's say I'm storing a linked list type structure in a database like so:
id parentId
---------------
1 null
2 1
3 2
4 3
5 4
6 5
parentId is a foreign key reference to id, and is required to be unique via a constraint.
Let's say I wanted to move item 5 to sit just before item 1, so our DB would look like this:
id parentId
---------------
1 null
2 5 <-- different
3 2
4 3
5 1 <-- different
6 4 <-- different
Three rows need to be altered, which is three update statements. Any one of these update statements will cause a constraint violation: all three statements must be complete before the constraint would be valid again.
My question is: what is the best way of not violating the uniqueness constraint?
I can currently conceive of two different solutions, neither of which I like:
Set each affected parentId to null and then perform the three updates
Completely change my data model so it's more of a 'copy on write' style versioned database, where these sorts of issues are not a problem.
You can do this in a single query. I'm sure there are many variations of this, but here is what I would use...
DECLARE
#node_id INT,
#new_parent_id INT
SELECT
#node_id = 5,
#new_parent = 1
UPDATE
yourTable
SET
parent_id = CASE WHEN yourTable.id = target_node.id THEN new_antiscendant.id
WHEN yourTable.id = descendant.id THEN target_node.parent_id
WHEN yourTable.id = new_descendant.id THEN target_node.id
END
FROM
yourTable AS target_node
LEFT JOIN
yourTable AS descendant
ON descendant.parent_id = target_node.id
LEFT JOIN
yourTable AS new_antiscendant
ON new_antiscendant.id = #new_parent_id
LEFT JOIN
yourTable AS new_descendant
ON COALESCE(new_descendant.parent_id, -1) = COALESCE(new_antiscendant.id, -1)
INNER JOIN
yourTable
ON yourTable.id IN (target_node.id, descendant.id, new_descendant.id)
WHERE
target_node.id = #node_id
This will work even if the #new_parent_id is NULL or the last record in the list.
MySQL doesn't like self joins in updates, so the approach would probably be to do the LEFT JOINs into a temporary table to get the new mapping. Then join on that table to update all three recors in a single query.
INSERT INTO
yourTempTable
SELECT
yourTable.id AS node_id,
CASE WHEN yourTable.id = target_node.id THEN new_antiscendant.id
WHEN yourTable.id = descendant.id THEN target_node.parent_id
WHEN yourTable.id = new_descendant.id THEN target_node.id
END AS new_parent_id
FROM
yourTable AS target_node
LEFT JOIN
yourTable AS descendant
ON descendant.parent_id = target_node.id
LEFT JOIN
yourTable AS new_antiscendant
ON new_antiscendant.id = #new_parent_id
LEFT JOIN
yourTable AS new_descendant
ON COALESCE(new_descendant.parent_id, -1) = COALESCE(new_antiscendant.id, -1)
INNER JOIN
yourTable
ON yourTable.id IN (target_node.id, descendant.id, new_descendant.id)
WHERE
target_node.id = #node_id
UPDATE
yourTable
SET
parent_id = yourTempTable.newParentID
FROM
yourTable
INNER JOIN
yourTempTable
ON yourTempTamp.node_id = yourTable.id
(The exact syntax depends on your RDBMS.)