SQL Merge and output in the same table - sql-server

I'm merging 2 tables and I want that if the cell is update the field would be marked as "updated" my code:
MERGE [ITWORKS].[dbo].[Testine2] te
USING [ITWORKS].[dbo].[Testinus] bo
ON te.itemid = bo.itemid
AND te.itemname <> bo.itemname
WHEN MATCHED THEN
UPDATE
SET te.itemname = bo.itemname
OUTPUT
$action
into [ITWORKS].[dbo].[Testine2] (busena);
SELECT * FROM [ITWORKS].[dbo].[Testine2];
Result I get:
Itemid Itemname Busena
100001 TEST NULL
NULL Null UPADTE
The result I want:
Itemid Itemname Busena
100001 TEST UPDATE

I want that if the cell is update the field would be marked as
"updated"
There is no reason to use output. Just set the column value in the update.
MERGE [ITWORKS].[dbo].[Testine2] te
USING [ITWORKS].[dbo].[Testinus] bo
ON te.itemid = bo.itemid
AND te.itemname <> bo.itemname
WHEN MATCHED THEN
UPDATE
SET te.itemname = bo.itemname,
te.Busena = 'UPDATE';
SELECT * FROM [ITWORKS].[dbo].[Testine2];

Related

How to perform dynamic update through MERGE statement in Snowflake?

I am performing update from JSON data using MERGE statement. The data contains primary key and the column that was updated from the source system. Since the data contains just the updated column along with primary key, update performed through MERGE is automatically updating other column too to null value. Is there any way through which we could build the update statement dynamically for every row and execute it through MERGE?
create or replace table source_data as
select parse_json(COLUMN1)::variant datacol
from values
('{
"metadata":{"OperationName":"UPDATE"},
"data":{"id":"1234","status":"Active"}
}'),
('{
"metadata":{"OperationName":"UPDATE"},
"data":{"id":"1235","name":"Johny"}
}');
create or replace table employee_destination as
select column1::text as id,
column2::text as name,
column3::text as status
from values
('1234','John','Inactive'),
('1235','Jack','Active');
MERGE into employee_destination as Target using (select datacol:data:id as id,datacol:data:status as status,datacol:data:name name,datacol:metadata:OperationName as operation_name from SOURCE_DATA)
AS Source
ON Target.id = Source.id
when matched AND Source.operation_name = 'UPDATE'
THEN
update set Target.id = Source.id, Target.name = Source.name, Target.status = Source.status;
Current output:
1234 null Active
1235 Johny null
Expected output:
1234 John Active
1235 Johny Inactive
thanks.
Try Below
AS Source
ON Target.id = Source.id
when matched AND Source.operation_name = 'UPDATE'
THEN
update set
--Target.id = Source.id, Why update your PK_ID
Target.name = COALESCE(Source.name,Target.name),
Target.status = COALESCE(Source.status,Target.status);

Incremental load in T-SQL with recorded history

Please help me, I need do a incremental process to my dimensions, to store history data too by T-SQL. I am trying use the MERGE statement, but it doesn't work, because this process deletes data that exists in the target but not in the source table.
Does someone have a suggestion ?
For exemple I have the source table: The source table is my STAGE,
Cod Descript State
AAA Desc1 MI
BBB Desc 2 TX
CCC Desc 3 MA
In the first load my dimension will be equal STAGE
However I can change the value in source table for exemple
AAA CHANGEDESCRIPTION Mi
So, I need update my dimension like this:
Cod Descript State
AAA Desc1 Mi before
AAA CHANGEDESCRIPTION MI actual
BBB Desc 2 TX actual
CCC Desc 3 MA actual
This is my DW and I need the information actual and all history
Try this. Column Aging is always "0" for current record and indicates change generation:
SELECT * INTO tbl_Target FROM (VALUES
('AAA','Desc1','MI',0),('BBB','Desc 2','TX',0),('CCC','Desc 3','MA',0)) as X(Cod, Descript, State, Aging);
GO
SELECT * INTO tbl_Staging FROM (VALUES ('AAA','Desc4','MI')) as X(Cod, Descript, State);
GO
UPDATE t SET Aging += 1
FROM tbl_Target as t
INNER JOIN tbl_Staging as s on t.Cod = s.Cod;
GO
INSERT INTO tbl_Target(Cod, Descript, State, Aging)
SELECT Cod, Descript, State, 0
FROM tbl_Staging;
GO
SELECT * FROM tbl_Target;
Please note that if you have records in staging table, which are "unchanged", you'll get false changes. If so, you have to filter them out in both queries.
I just commented the clause DELETE...tell me what do you think please
MERGE DimTarget AS [Target] --— begin merge statements (merge statements end with a semi-colon)
USING TableSource AS [Source]
ON [Target].ID = [Source].ID AND [Target].[IsCurrentRow] = 1
WHEN MATCHED AND --— record exists but values are different
(
[Target].Dscript <> [Source].Descript
)
THEN UPDATE SET --— update records (Type 1 means record values are overwritten)
[Target].[IsCurrentRow] = 0
-- , [Target].[ValidTo] = GETDATE()
WHEN NOT MATCHED BY TARGET --— record does not exist
THEN INSERT --— insert record
(
Descritp
, [IsCurrentRow]
)
VALUES
(
Descript
, 1
)
--WHEN NOT MATCHED BY SOURCE --— record exists in target but not source
--THEN DELETE -- delete from target
OUTPUT $action AS Action, [Source].* --— output results

JOIN ON subselect returns what I want, but surrounding select is missing records when subselect returns NULL

I have a table where I am storing records with a Created_On date and a Last_Updated_On date. Each new record will be written with a Created_On, and each subsequent update writes a new row with the same Created_On, but an updated Last_Updated_On.
I am trying to design a query to return the newest row of each. What I have looks something like this:
SELECT
t1.[id] as id,
t1.[Store_Number] as storeNumber,
t1.[Date_Of_Inventory] as dateOfInventory,
t1.[Created_On] as createdOn,
t1.[Last_Updated_On] as lastUpdatedOn
FROM [UserData].[dbo].[StoreResponses] t1
JOIN (
SELECT
[Store_Number],
[Date_Of_Inventory],
MAX([Created_On]) co,
MAX([Last_Updated_On]) luo
FROM [UserData].[dbo].[StoreResponses]
GROUP BY [Store_Number],[Date_Of_Inventory]) t2
ON
t1.[Store_Number] = t2.[Store_Number]
AND t1.[Created_On] = t2.co
AND t1.[Last_Updated_On] = t2.luo
AND t1.[Date_Of_Inventory] = t2.[Date_Of_Inventory]
WHERE t1.[Store_Number] = 123
ORDER BY t1.[Created_On] ASC
The subselect works fine...I see X number of rows, grouped by Store_Number and Date_Of_Inventory, some of which have luo (Last_Updated_On) values of NULL. However, those rows in the sub-select where luo is null do not appear in the overall results. In other words, where I get 6 results in the sub-select, I only get 2 in the overall results, and its only those rows where the Last_Updated_On is not NULL.
So, as a test, I wrote the following:
SELECT 1 WHERE NULL = NULL
And got no results, but, when I run:
SELECT 1 WHERE 1 = 1
I get back a result of 1. Its as if SQL Server is not relating NULL to NULL.
How can I fix this? Why wouldn't two fields compare when both values are NULL?
You could use Coalesce (example assuming Store_Number is an integer)
ON
Coalesce(t1.[Store_Number],0) = Coalesce(t2.[Store_Number],0)
The ANSI Null comparison is not enabled by default; NULL doesn't equal NULL.
You can enable this (if your business case and your Database design usage of NULL requires this) by the Hint:
SET ansi_nulls off
Another alternative basic turn around using:
ON ((t1.[Store_Number] = t2.[Store_Number]) OR
(t1.[Store_Number] IS NULL AND t2.[Store_Number] IS NULL))
Executing your POC:
SET ansi_nulls off
SELECT 1 WHERE NULL = NULL
Returns:
1
This also works:
AND EXISTS (SELECT t1.Store_Number INTERSECT SELECT t2.Store_Number)

Assigning NULL value to column name using Case Statement of where is SQL Server 2008

I have a table GrowDaysLocation. In this table there can be two records with respect to DistrictId and RegionId
i.e;
RegionId=1 and DistrictId = NULL
RegionId=1 and DistrictId = 1
I have a condition that if the row doesn't exist for RegionId = 1 and DistrictId = 1 then get the row for RegionId = 1 and DistrictId = NULL.
How can I accomplish this using a single query?
The below is the query I have tried out.
In this query, I have used CASE in Where clause and Use sub-query to find out the row's existence but the problem is when I return NULL from the case it will not return any rows.
==================================================
SET ANSI_NULLS OFF
Select * From GrowDaysLocations
Where DistrictId =
( CASE WHEN (Select Count(*) From GrowDaysLocations
Where RegionId = '38D95A68-4A92-4D11-9A88-464CF1492880' AND DistrictId = 'F4B67A07-1BF7-42F5-9F19-77329A215D8B' AND
GrowDaysProfileId = '79F8BDBF-67D3-44A7-A790-1C10EE8B2AD0') > 0 THEN DistrictId
ELSE
NULL
END
)
AND RegionId = '38D95A68-4A92-4D11-9A88-464CF1492880' AND GrowDaysProfileId = '79F8BDBF-67D3-44A7-A790-1C10EE8B2AD0'
===========================================
The RANK function may give you the results you are looking for:
SELECT RegionId, DistrictId, GrowDaysProfileId
FROM
(SELECT RegionId
,DistrictId
,GrowDaysProfileId
,RANK() OVER(PARTITION BY RegionId, GrowDaysProfileId
ORDER BY DistrictId DESC) AS rankVal
From GrowDaysLocation) sub
WHERE rankVal = 1
This query will give you a result set with one row for each distinct RegionId and GrowDaysProfileId. If a RegionId/GrowDaysProfileId combination has more than one row in the table, the query will select a result based on the value of DistrictId. The row with the highest DistrictId value will be used first and the row with the lowest DistrictId (NULL being the lowest) last.
This is because when evaluating the condition
WHERE ColumnName = NULL
all rows are filtered out because when checking for equality, if any side is NULL, the row is immediately filtered out regardless of the condition (see Data Manipulation Language section) . You have to use
WHERE ColumnName IS NULL
In this case you can use
WHERE ISNULL(DistrictID, 'Empty') = ...
And use the 'Empty' string instead of null in the case statement.

sql server trigger to update time field when there is real change in another field

I need a trigger that updates a table row field if one or more fields of that row is updated.
Suppose you have an Employees table that may look as follows:
EmployeeId Name Address ModificationDate
1 Spears 27 Sober Road
2 Jagger 65 Straight Street
If there is a real change in the value of any field except the EmployeeId and ModificationDate fields, the trigger should generate a time value and update the ModificationDate.
Example 1 of a real change:
update dbo.Employees
set Name = 'Beggar'
where EmployeeId = 2
Example 2 of no real change:
update dbo.Employees
set Name = 'Jagger'
where EmployeeId = 2
If an update in Example 2 executes, the trigger should not update the ModificationDate field.
In a trigger, you have access to the 'inserted' and 'deleted' system tables.
Those tables contains the records in the table that have been updated by the statement that caused the trigger to execute.
For an 'UPDATE' trigger, the 'inserted' table contains the records like they are in the new state, the 'deleted' table contains the records with the old values.
You'll have to make use of those 2 tables to find out which records have really changed, and update the ModificationDate for those records.
I think the statement inside the trigger will look something like this. (I haven't tested it)
UPDATE myTable
SET ModificationDate = getdate()
FROM inserted, deleted
WHERE inserted.EmployeeId = deleted.EmployeeId
AND (inserted.Name <> deleted.Name OR inserted.Address <> deleted.Address)
Edit:
I've played around a bit:
create trigger upd_employee on [employee] after update
as
begin
update employee
set modifdate = getdate()
where employee.empid in
( select i.empid
from inserted i
inner join deleted d on i.empid = d.empid
where (i.name <> d.name or i.address <> d.address )
)
end
insert into employee
values
(1, 'Frederik' , '', null)
insert into employee
values
(2, 'User', '', null)
update employee
set [address] = 'some address'
select * from employee
update employee set [name] = 'test' where empid = 2
select * from employee
update employee set [name] = 'test' where empid = 2
select * from employee

Resources