SQL Compare two tables update and set flag - sql

I have two tables, the first one is called table1, the structure and contents are shown below
+--+-----+------+------+
|ID|fname|Lname |Status|
+--+-----+------+------+
|1 |Pat |Test | |
+--+-----+------+------+
|2 |Su |Test2 | |
+--+-----+------+------+
|3 |Bri |Test4 | |
+--+-----+------+------+
|4 |Mel |Gi | |
+--+-----+------+------+
|5 |Good |Record| |
+--+-----+------+------+
|6 |Tr |BL | |
+--+-----+------+------+
The second table has updates that need to be committed to table1.
+--+-------+-----+
|ID|Fname |Lname|
+--+-------+-----+
|1 |Patrick|Cool |
+--+-------+-----+
|2 |Susun |Smart|
+--+-------+-----+
|6 |True |Blood|
+--+-------+-----+
I would like to compare both tables and update table1 with the contents of table 2. (compare using ID) If any update is done on any row, i would like the status column to be marked as UPDATED. If a row exists in table1 but it doesnt exist in table2 i would like the status of that row in table1 marked as DELETE. Rows with no changes should have no status
The after the update the final output should resemble
+--+-------+------+------+
|ID|fname |Lname |Status|
+--+-------+------+------+
|1 |Patrick|Cool |UPDATE|
+--+-------+------+------+
|2 |Susun |Smart |UPDATE|
+--+-------+------+------+
|3 |Brian |Brown |DELETE|
+--+-------+------+------+
|4 |Mel |Gibson|DELETE|
+--+-------+------+------+
|5 |Good |Record||
+--+-------+------+------+
|6 |True |Blood |UPDATE|
+--+-------+------+------+
Any help will be appreciated

maybe try a nested query?
for the deleted updated
UPDATE table_1 SET status = "DELETED" WHERE id NOT IN (
SELECT id FROM table_2
)
for update
UPDATE table_1, table_2
SET table_1.status = "UPDATED",
table_1.fName = table_2.FName,
table_1.lName = table_2.LName
WHERE table_1.id IN (
SELECT id FROM table_1, table_2
WHERE table_1.id = table_2.id AND
(table_1.fName!= table_2.FName OR table_1.lName!=table_2.LName)
)
(edited)update with join
UPDATE table_1
SET table_1.fName = t2.FName, table_1.lName = t2.LName, table_1.status = "UPDATED"
FROM table_1 AS t1 JOIN table_2 AS t2 USING (id)
WHERE t1.fName!=t2.FName OR t1.lName!=t2.LName

hashbrown ->
the code above is just not showing the "DELETED" , "UPDATED" , sorry about that (:
So I'll add this code which can help
Read more in Simple-talk-> the merge statement in sql server 2008
IF OBJECT_ID ('BookInventory', 'U') IS NOT NULL
DROP TABLE dbo.BookInventory;
CREATE TABLE dbo.BookInventory -- target
(
TitleID INT NOT NULL PRIMARY KEY,
Title NVARCHAR(100) NOT NULL,
Quantity INT NOT NULL
CONSTRAINT Quantity_Default_1 DEFAULT 0
);
IF OBJECT_ID ('BookOrder', 'U') IS NOT NULL
DROP TABLE dbo.BookOrder;
CREATE TABLE dbo.BookOrder -- source
(
TitleID INT NOT NULL PRIMARY KEY,
Title NVARCHAR(100) NOT NULL,
Quantity INT NOT NULL
CONSTRAINT Quantity_Default_2 DEFAULT 0
);
INSERT BookInventory VALUES
(1, 'The Catcher in the Rye', 6),
(2, 'Pride and Prejudice', 3),
(3, 'The Great Gatsby', 0),
(5, 'Jane Eyre', 0),
(6, 'Catch 22', 0),
(8, 'Slaughterhouse Five', 4);
INSERT BookOrder VALUES
(1, 'The Catcher in the Rye', 3),
(3, 'The Great Gatsby', 0),
(4, 'Gone with the Wind', 4),
(5, 'Jane Eyre', 5),
(7, 'Age of Innocence', 8);
--1 Implementing the WHEN MATCHED Clause
MERGE BookInventory bi
USING BookOrder bo
ON bi.TitleID = bo.TitleID
WHEN MATCHED THEN
UPDATE
SET bi.Quantity = bi.Quantity + bo.Quantity;
SELECT * FROM BookInventory;
--2 Implementing the WHEN MATCHED Clause (Delete)
MERGE BookInventory bi
USING BookOrder bo
ON bi.TitleID = bo.TitleID
WHEN MATCHED AND
bi.Quantity + bo.Quantity = 0 THEN
DELETE
WHEN MATCHED THEN
UPDATE
SET bi.Quantity = bi.Quantity + bo.Quantity;
SELECT * FROM BookInventory;
--3 Implementing the WHEN NOT MATCHED [BY TARGET] Clause
MERGE BookInventory bi
USING BookOrder bo
ON bi.TitleID = bo.TitleID
WHEN MATCHED AND
bi.Quantity + bo.Quantity = 0 THEN
DELETE
WHEN MATCHED THEN
UPDATE
SET bi.Quantity = bi.Quantity + bo.Quantity
WHEN NOT MATCHED BY TARGET THEN
INSERT (TitleID, Title, Quantity)
VALUES (bo.TitleID, bo.Title,bo.Quantity);
SELECT * FROM BookInventory;
--4 Implementing the WHEN NOT MATCHED BY SOURCE Clause
MERGE BookInventory bi
USING BookOrder bo
ON bi.TitleID = bo.TitleID
WHEN MATCHED AND
bi.Quantity + bo.Quantity = 0 THEN
DELETE
WHEN MATCHED THEN
UPDATE
SET bi.Quantity = bi.Quantity + bo.Quantity
WHEN NOT MATCHED BY TARGET THEN
INSERT (TitleID, Title, Quantity)
VALUES (bo.TitleID, bo.Title,bo.Quantity)
WHEN NOT MATCHED BY SOURCE
AND bi.Quantity = 0 THEN
DELETE;
SELECT * FROM BookInventory;
--5 Implementing the OUTPUT Clause
DECLARE #MergeOutput TABLE
(
ActionType NVARCHAR(10),
DelTitleID INT,
InsTitleID INT,
DelTitle NVARCHAR(50),
InsTitle NVARCHAR(50),
DelQuantity INT,
InsQuantity INT
);
MERGE BookInventory bi
USING BookOrder bo
ON bi.TitleID = bo.TitleID
WHEN MATCHED AND
bi.Quantity + bo.Quantity = 0 THEN
DELETE
WHEN MATCHED THEN
UPDATE
SET bi.Quantity = bi.Quantity + bo.Quantity
WHEN NOT MATCHED BY TARGET THEN
INSERT (TitleID, Title, Quantity)
VALUES (bo.TitleID, bo.Title,bo.Quantity)
WHEN NOT MATCHED BY SOURCE
AND bi.Quantity = 0 THEN
DELETE
OUTPUT
$action,
DELETED.TitleID,
INSERTED.TitleID,
DELETED.Title,
INSERTED.Title,
DELETED.Quantity,
INSERTED.Quantity
INTO #MergeOutput;
SELECT * FROM BookInventory;
SELECT * FROM #MergeOutput
--where ActionType = 'UPDATE'

You can use the Merge Clause and OUTPUT
IF OBJECT_ID (N'dbo.Purchases', N'U') IS NOT NULL
DROP TABLE dbo.Purchases;
GO
CREATE TABLE dbo.Purchases (
ProductID int, CustomerID int, PurchaseDate datetime,
CONSTRAINT PK_PurchProdID PRIMARY KEY(ProductID,CustomerID));
GO
INSERT INTO dbo.Purchases VALUES(707, 11794, '20060821'),
(707, 15160, '20060825'),(708, 18529, '20060821'),
(711, 11794, '20060821'),(711, 19585, '20060822'),
(712, 14680, '20060825'),(712, 21524, '20060825'),
(712, 19072, '20060821'),(870, 15160, '20060823'),
(870, 11927, '20060824'),(870, 18749, '20060825');
GO
IF OBJECT_ID (N'dbo.FactBuyingHabits', N'U') IS NOT NULL
DROP TABLE dbo.FactBuyingHabits;
GO
CREATE TABLE dbo.FactBuyingHabits (
ProductID int, CustomerID int, LastPurchaseDate datetime,
CONSTRAINT PK_FactProdID PRIMARY KEY(ProductID,CustomerID));
GO
INSERT INTO dbo.FactBuyingHabits VALUES(707, 11794, '20060814'),
(707, 18178, '20060818'),(864, 14114, '20060818'),
(866, 13350, '20060818'),(866, 20201, '20060815'),
(867, 20201, '20060814'),(869, 19893, '20060815'),
(870, 17151, '20060818'),(870, 15160, '20060817'),
(871, 21717, '20060817'),(871, 21163, '20060815'),
(871, 13350, '20060815'),(873, 23381, '20060815');
GO
select * from Purchases;
select * from FactBuyingHabits;
--Now this is the Place where you do the manipulation you want
MERGE dbo.FactBuyingHabits AS Target
USING (SELECT CustomerID, ProductID, PurchaseDate FROM dbo.Purchases) AS Source
ON (Target.ProductID = Source.ProductID AND Target.CustomerID = Source.CustomerID)
WHEN MATCHED THEN
UPDATE SET Target.LastPurchaseDate = Source.PurchaseDate
WHEN NOT MATCHED BY TARGET THEN
INSERT (CustomerID, ProductID, LastPurchaseDate)
VALUES (Source.CustomerID, Source.ProductID, Source.PurchaseDate)
OUTPUT $action, Inserted.ProductId InsertedProductId,
Inserted.CustomerId InsertedCustomerId,
Inserted.LastPurchaseDate InsertedLastPurchaseDate,
Deleted.ProductId DeletedProductId,
Deleted.CustomerId DeletedCustomerId,
Deleted.LastPurchaseDate DeletedLastPurchaseDate;
select * from FactBuyingHabits;
drop table FactBuyingHabits;
drop table Purchases;

Related

I need to do retrofit query using update or merge

I have two tables A and B. In A, I have a column called fetch_year. I need to consider table B from these two columns
primary_date
secondary_date
These columns have JSON values like {"lock":"true","date":"01/01/1990"}
So from this, I need to get the date and I need to extract the year and should save it in table A column called fetch_year. Will always consider primary_date first then secondary_date(if primary_date is null)
The final result should be 1990 in the fetch_year column
Table A is empty as of now( only one column with cal_id)
cal_id fetch_year
1 null
n null
Table B
|B_id|Cal_id | primary_date | secondary_date |
|----|-------|-----------------------------------|------------------------|
|11 | 1 |{"lock":"true","date":"01/01/1990"}|Null|
|12 | 2 | Null | {"lock":"true","date":"01/01/1980"} |
|13 | 3 | Null | Null |
|14 | 4 | {"lock":"true","date":"01/01/1995"} |{"lock":"true","date":"01/01/1997"} |
In table B
So I have n number of records in both the tables
I need results like this in A table
Cal_id fetch_year.
1 1990
2 1980
3 Null
4 1995
n n-values
In cal_id =4 in this case we have value in both columns so we are considering primary_date not secondary_date
Please help me with this problem
You could make use of either JSON_VALUE or OPENJSON here to extract the date from your JSON blobs.
I tend to prefer OPENJSON because it allows you to extract multiple values simultaneously and they don't have to be at the same level in a nested JSON structure. With the "squirelly" dates in your example data, though, you may prefer the JSON_VALUE version with TRY_CONVERT so that you have more control over date deserialization.
--Data setup
create table dbo.A (
Cal_id int,
fetch_year int
);
create table dbo.B (
B_id int not null identity(11,1),
Cal_id int,
primary_date nvarchar(max),
secondary_date nvarchar(max)
);
insert dbo.A (Cal_id, fetch_year)
values
(1, null),
(2, null),
(3, null),
(4, null);
insert dbo.B (Cal_id, primary_date, secondary_date)
values
(1, N'{"lock":"true","date":"01/01/1990"}', null),
(2, null, N'{"lock":"true","date":"01/01/1980"}'),
(3, null, null),
(4, N'{"lock":"true","date":"01/01/1995"}', N'{"lock":"true","date":"01/01/1997"}');
--JSON_VALUE example
update Table_A
set fetch_year = year(coalesce(
-- REF: CAST and CONVERT / Date and time styles
-- https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql#date-and-time-styles
try_convert(date, json_value(primary_date, '$.date'), 101), --mm/dd/yyyy
try_convert(date, json_value(secondary_date, '$.date'), 101) --mm/dd/yyyy
))
from dbo.A Table_A
join dbo.B Table_B on Table_B.Cal_id = Table_A.Cal_id
--OPENJSON example
update Table_A
set fetch_year = year(coalesce(
Primary_JSON.date,
Secondary_JSON.date
))
from dbo.A Table_A
join dbo.B Table_B on Table_B.Cal_id = Table_A.Cal_id
outer apply openjson(Table_B.primary_date) with ([date] date) Primary_JSON
outer apply openjson(Table_B.secondary_date) with ([date] date) Secondary_JSON;

TSQL group by and combine remaining columns in json array

I want to group a result set by a column and combine the remaining columns into a json array, but I'm not sure how to aggregate the results for this.
I want the following output:
A_ID | Translations
--------------------
1 | [{"Name": "english_1","LCID": "en-gb"},{"Name": "french_1","LCID": "fr-fr"}]
2 | [{"Name": "english_2","LCID": "en-gb"},{"Name": "french_2","LCID": "fr-fr"}]
But I cannot group the results by A_ID without an aggregator so I get the following
A_ID | Translations
--------------------
1 | [{"Name": "english_1","LCID": "en-gb"}]
1 | [{"Name": "french_1","LCID": "fr-fr"}]
2 | [{"Name": "english_2","LCID": "en-gb"}]
2 | [{"Name": "french_2","LCID": "fr-fr"}]
Here is an example:
DROP TABLE IF EXISTS #tabA;
DROP TABLE IF EXISTS #tabB;
DROP TABLE IF EXISTS #tabC;
go
CREATE TABLE #tabA
(
Id int not null
);
CREATE TABLE #tabTranslations
(
translationId int not null,
Name nvarchar(32) not null,
aId int not null, -- Foreign key.
languageId int not null --Foreign key
);
CREATE TABLE #tabLanguages
(
languageId int not null,
LCID nvarchar(32) not null
);
go
INSERT INTO #tabA (Id)
VALUES
(1),
(2);
INSERT INTO #tabTranslations (translationId, Name, aId, languageId)
VALUES
(1, 'english_1', 1, 1),
(2, 'french_1', 1, 2),
(3, 'english_2', 2, 1),
(4, 'french_2', 2, 2);
INSERT INTO #tabLanguages (languageId, LCID)
VALUES
(1, 'en-gb'),
(2, 'fr-fr');
go
select
_a.Id as A_ID,
(
select
_translation.Name,
_language.LCID
for json path
)
from #tabA as _a
inner join #tabTranslations as _translation ON _translation.aId = _a.Id
inner join #tabLanguages as _language ON _language.languageId = _translation.languageId
-- group by _a.Id ??
;
go
DROP TABLE IF EXISTS #tabA;
DROP TABLE IF EXISTS #tabTranslations;
DROP TABLE IF EXISTS #tabLanguages;
go
Alternative solution:
I know I can do this, but I obviously don't want to hard code the available LCIDs (maybe I could generate the sql query and exec it? but this feels too complex), also I would prefer an array
select
_a.Id as A_ID,
(
SELECT
MAX(CASE WHEN [LCID] = 'en-gb' THEN [Name] END) 'en-gb',
MAX(CASE WHEN [LCID] = 'fr-fr' THEN [Name] END) 'fr-fr'
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
) as b
from #tabA as _a
inner join #tabTranslations as _translation ON _translation.aId = _a.Id
inner join #tabLanguages as _language ON _language.languageId = _translation.languageId
group by _a.Id;
result:
A_ID | Translations
--------------------
1 | { "en-Gb": "english_1", "fr-FR": "french_1"}
2 | { "en-Gb": "english_2", "fr-FR": "french_2"}
If I understand you correctly, next approach may help. Use additional CROSS APPLY operator and FOR JSON PATH to get your expected results:
Statement:
SELECT *
FROM #tabA AS t
CROSS APPLY (
SELECT _translation.Name AS Name, _language.LCID AS LCID
FROM #tabA _a
inner join #tabTranslations as _translation ON _translation.aId = _a.Id
inner join #tabLanguages as _language ON _language.languageId = _translation.languageId
WHERE _a.Id = t.Id
for json path
) _c(Translations)
Output:
Id Translations
1 [{"Name":"english_1","LCID":"en-gb"},{"Name":"french_1","LCID":"fr-fr"}]
2 [{"Name":"english_2","LCID":"en-gb"},{"Name":"french_2","LCID":"fr-fr"}]

Nested while loop in SQL Server is not showing the expected result

I am trying to connect records from two different tables so I can display the data in a tabular format in an SSRS tablix.
The code below does not return the expected results.
As is, for each item in Temp_A the loop updates everything with the last item in Temp_C. Here is the code:
CREATE TABLE #Temp_A
(
[ID] INT,
[Name] VARCHAR(255)
)
INSERT INTO #Temp_A ([ID], [Name])
VALUES (1, 'A'), (2, 'B')
CREATE TABLE #Temp_C
(
[ID] INT,
[Name] VARCHAR(255)
)
INSERT INTO #Temp_C ([ID], [Name])
VALUES (1, 'C'), (2, 'D')
CREATE TABLE #Temp_Main
(
[Temp_A_ID] INT,
[Temp_A_Name] VARCHAR(255),
[Temp_C_ID] INT,
[Temp_C_Name] VARCHAR(255),
)
DECLARE #MIN_AID int = (SELECT MIN(ID) FROM #Temp_A)
DECLARE #MAX_AID int = (SELECT MAX(ID) FROM #Temp_A)
DECLARE #MIN_DID int = (SELECT MIN(ID) FROM #Temp_C)
DECLARE #MAX_DID int = (SELECT MAX(ID) FROM #Temp_C)
WHILE #MIN_AID <= #MAX_AID
BEGIN
WHILE #MIN_DID <= #MAX_DID
BEGIN
INSERT INTO #Temp_Main([Temp_A_ID], [Temp_A_Name])
SELECT ID, [Name]
FROM #Temp_A
WHERE ID = #MIN_AID
UPDATE #Temp_Main
SET [Temp_C_ID] = ID, [Temp_C_Name] = [Name]
FROM #Temp_C
WHERE ID = #MIN_DID
SET #MIN_DID = #MIN_DID + 1
END
SET #MIN_AID = #MIN_AID + 1
SET #MIN_DID = 1
END
SELECT * FROM #Temp_Main
DROP TABLE #Temp_A
DROP TABLE #Temp_C
DROP TABLE #Temp_Main
Incorrect result:
Temp_A_ID | Temp_A_Name | Temp_C_ID | Temp_C_Name
----------+-------------+-----------+---------------
1 A 2 D
1 A 2 D
2 B 2 D
2 B 2 D
Expected results:
Temp_A_ID | Temp_A_Name | Temp_C_ID | Temp_C_Name
----------+-------------+-----------+---------------
1 A 1 C
1 A 2 D
2 B 1 C
2 B 2 D
What am I missing?
You seem to want a cross join:
select a.*, c.*
from #Temp_A a cross join
#Temp_C c
order by a.id, c.id;
Here is a db<>fiddle.
There is no need to write a WHILE loop to do this.
You can use insert to insert this into #TempMain, but I don't se a need to have a temporary table for storing the results of this query.

Add new value to multiple row

I have an exsisting database with 700 row where are stored users, let's call this users. I will have a new column which is "time_code". Each user have a time_code.
I will have these code from an other table where each user_name is associate to a time_code, I call this table time code users. Is there a way to add these code from time code users to users at the corresponding name ?
And can I do this only with SQL ?
Assume you initial dataset
CREATE TABLE USERS (USER_NO NUMBER (3), NAME VARCHAR(5));
CREATE TABLE TIME_CODE_USERS (NAME VARCHAR(5), TIME_CODE VARCHAR(3));
INSERT INTO USERS VALUES (1,'A');
INSERT INTO USERS VALUES (2,'B');
INSERT INTO USERS VALUES (3,'C');
INSERT INTO TIME_CODE_USERS VALUES ('A', 'GMT');
INSERT INTO TIME_CODE_USERS VALUES ('B', 'IST');
INSERT INTO TIME_CODE_USERS VALUES ('C', 'CET');
Now alter the target table to add your column
ALTER TABLE USERS ADD TIME_CODE VARCHAR(3);
Run the update to populate the target
UPDATE USERS
SET TIME_CODE = (SELECT TIME_CODE
FROM TIME_CODE_USERS
WHERE USERS.NAME = TIME_CODE_USERS.NAME)
WHERE EXISTS (SELECT TIME_CODE
FROM TIME_CODE_USERS
WHERE USERS.NAME = TIME_CODE_USERS.NAME);
Check the update
SELECT * FROM USERS;
| USER_NO | NAME | TIME_CODE |
|---------|------|-----------|
| 1 | A | GMT |
| 2 | B | IST |
| 3 | C | CET |
UPDATE u
SET u.time_code = tcu.time_code
FROM users u INNER JOIN time_code_users tcu
ON u.user_name = tcu.user_name
ALTER TABLE users ADD time_code varchar(40);
UPDATE users SET users.time_code = (
SELECT time_code FROM time_code_users
WHERE users.username = time_code_users.username
);

copy a column from one table to another where table1.col = table2.col

Suppose there are two tables which have the data mentioned in the insert query. There is no foreign key references between the two table.
create table uref.slave (
SLAVE_ID SMALLINT NOT NULL PRIMARY KEY,
DESC VARCHAR(20)
);
INSERT INTO uref.SLAVE values (1, null)
INSERT INTO uref.SLAVE values (2, null)
create table uref.master (
MASTER_ID SMALLINT NOT NULL PRIMARY KEY,
SLAVE_ID SMALLINT,
DESC VARCHAR(20)
);
INSERT INTO uref.MASTER values (1,1,'value1')
INSERT INTO uref.MASTER values (2,2,'value2')
Now I need a query which will copy uref.master.DESC into uref.slave.DESC based on uref.master.SLAVE_ID = uref.slave.SLAVE_ID.
The simplest solution may be to use MERGE.
MERGE INTO uref.SLAVE s
USING uref.MASTER m
ON (s.SLAVE_ID = m.SLAVE_ID)
WHEN MATCHED
THEN UPDATE SET Desc = m.Desc
It could be refined to update only when there is a change to be made
MERGE INTO uref.SLAVE s
USING uref.MASTER m
ON (s.SLAVE_ID = m.SLAVE_ID)
WHEN MATCHED
and ( s.Desc <> m.Desc
or (s.Desc is null and m.Desc is not null)
)
THEN UPDATE SET Desc = m.Desc
UPDATE uref.SLAVE t1
SET Desc =
(
SELECT t2.Desc
FROM uref.MASTER t2
WHERE t1.SLAVE_ID = t2.SLAVE_ID
)
WHERE EXISTS
(
SELECT *
FROM uref.MASTER t2
WHERE t1.SLAVE_ID = t2.SLAVE_ID
AND NOT t1.Desc=t2.Desc
)
AND t1.Desc IS NULL
if sql server, Try below sql: (recheck the table name and fields)
declare #urefSlave table (
SLAVE_ID SMALLINT ,
[DESC] VARCHAR(20)
);
INSERT INTO #urefSlave values (1, null)
INSERT INTO #urefSlave values (2, null)
Declare #urefMaster table (
MASTER_ID SMALLINT,
SLAVE_ID SMALLINT,
[DESC] VARCHAR(20)
);
INSERT INTO #urefMaster values (1,1,'value1')
INSERT INTO #urefMaster values (2,2,'value2')
select * from #urefMaster
select * from #urefSlave
update #urefSlave
set [DESC] = b.[DESC]
from #urefSlave a inner join #urefMaster b on a.SLAVE_ID = b.SLAVE_ID
select * from #urefSlave
REsult:
MASTER_ID SLAVE_ID DESC
--------- -------- --------------------
1 1 value1
2 2 value2
SLAVE_ID DESC
-------- --------------------
1 value1
2 value2
Updated
cannot help much in db2, because i don't have the tools to run the syntax
but from this link db2 update help
you can modify an example in there to meet your requirement:
UPDATE EMPLOYEE EU
SET (EU.SALARY, EU.COMM)
=
(SELECT AVG(ES.SALARY), AVG(ES.COMM)
FROM EMPLOYEE ES
WHERE ES.WORKDEPT = EU.WORKDEPT)
WHERE EU.EMPNO = '000120'
Hope this help.