I am trying to use a MERGE statement to update 2 tables using a single source (CTE). It works when I use only 1 merge, but when I add the 2nd one, it returns an error:
Msg 208, Level 16, State 1, Procedure mn_SeoUrl_UpdateBulk, Line 46
Invalid object name 'cte'.
Is it possible to update 2 tables with a merge? If it is possible, I am doing it incorrectly and I hope someone can show me what is the correct method of doing this.
Any help is much appreciated.
Thank you.
Here is my failing code (false col names):
WITH cte AS
(
SELECT
[u].[col1], [u].[col2], [u].[col3],
CASE
WHEN [u].[col1] LIKE 'L%'
THEN 'c/' + [u].[col2] + '/' + [u].[col3]
WHEN [u].[col1] LIKE 'M%'
THEN 'm/' + [u].[col2] + '/' + [u].[col3]
END [col4]
FROM
(SELECT
[st1].[col1], [st1].[col2], [st1].[col3]
FROM
[dbo].[sourcetable1] [st1]
INNER JOIN
[dbo].[sourcetable2] [st2] ON [st1].[ID] = [st2].[ID]
WHERE
[pd].[col2] <> 0) [u]
)
MERGE [dbo].[table1] AS [Target]
USING [cte] AS [Source] ON [Target].[ID] = [Source].[ID]
WHEN MATCHED THEN
UPDATE
SET [Target].[col] = [Source].[col]
WHEN NOT MATCHED BY TARGET THEN
INSERT ([col])
VALUES ([Source].[col]);
MERGE [dbo].[tabl2] AS [Target]
USING [cte] AS [Source] ON [Target].[id] = [Source].[id]
WHEN MATCHED THEN
UPDATE
SET [Target].[col] = [Source].[col]
WHEN NOT MATCHED BY TARGET THEN
INSERT ([col])
VALUES ([Source].[col]);
END;
Is it possible to update 2 tables with a merge?
No, it is not possible to update two tables with a single merge. You have to do two separate merge statements.
And as others have pointed out in comments, a single CTE can only be used for one statement, so if you do two merge statements, they can't share the same CTE. My suggestion would be to use your CTE query to populate a table variable. Then you can use the same table variable in two merge statements.
Related
I want to achieve this merge statement using SSIS. I know this can be achieved using a merge join in SSIS but I am not sure if I can fit COALESCE statement anywhere. I wanted merge to happen between two tables only when the target table has NULL records.
MERGE TargetTable AS TARGET
USING SourceTable AS SOURCE
ON ISNULL(TARGET.ColA, '') = ISNULL(SOURCE.ColA, '')
AND ISNULL(TARGET.ColB, '') = ISNULL(SOURCE.ColB, '')
WHEN MATCHED THEN UPDATE SET
TARGET.ColC = COALESCE(TARGET.ColC, SOURCE.ColC)
TARGET.ColD = COALESCE(TARGET.ColD, SOURCE.ColD)
TARGET.ColE = COALESCE(TARGET.ColE, SOURCE.ColE)
WHEN NOT MATCHED [BY TARGET] THEN INSERT
(ColA, ColB, ColC, ColD, ColE)
Values(SOURCE.ColA, SOURCE.ColB, SOURCE.ColC, SOURCE.ColD, SOURCE.ColE)
As mentioned in the comments, you can simply use derived columns transformations to achieve the ISNULL() and COALESCE() functions.
There are two approaches:
Using REPLACENULL() function:
REPLACENULL(ColA,"")
Using the conditional operator ? : with the ISNULL() function:
ISNULL([ColA]) == True ? "" : [ColA]
I have a CTE I am using to pull some data from two tables then stick in an intermediate table called cte_list, something like
with cte_list as (
select pl.col_val from prune_list pl join employees.employee emp on pl.col_val::uuid = emp.id
where pl.col_nm = 'employee_ref_id' limit 100
)
Then, I am doing an insert to move records from the cte_list to another archive table (if they don't exist) called employee_arch_test
insert into employees.employee_arch_test (
select * from employees.employee where id in (select col_val::uuid from cte_list)
and not exists (select 1 from employees.employee_arch_test where employees.employee_arch_test.id=employees.employee.id)
);
This seems to work fine. The problem is when I add another statement after, to do some deletions from the main employee table using this aforementioned cte_list - the cte_list apparently no longer exists?
SQL Error [42P01]: ERROR: relation "cte_list" does not exist
the actual delete query:
delete from employees.employee where id in (select col_val::uuid from cte_list);
Can the cte_list CTE table only be used once or something? I'm running these statements in a LOOP and I need to run the exact same calls for about 2 or 3 other tables but hit a sticking point here.
A CTE only exists for the duration of the statement of which it's a part. I gather you have an INSERT statement with the CTE preceding it:
with cte_list
as (select pl.col_val
from prune_list pl
join employees.employee emp
on pl.col_val::uuid = emp.id
where pl.col_nm = 'employee_ref_id'
limit 100
)
insert into employees.employee_arch_test
(select *
from employees.employee
where id in (select col_val::uuid from cte_list)
and not exists (select 1
from employees.employee_arch_test
where employees.employee_arch_test.id = employees.employee.id)
);
The CTE is part of the INSERT statement - it is not a separate statement by itself. It only exists for the duration of the INSERT statement.
If you need something which lasts longer your options are:
Add the same CTE to each of your following statements. Note that because data may be changing in your database each invocation of the CTE may return different data.
Create a view which performs the same operations as the CTE, then use the view in place of the CTE. Note that because data may be changing in your database each invocation of the view may return different data.
Create a temporary table to hold the data from your CTE query, then use the temporary table in place of the CTE. This has the advantage of providing a consistent set of data to all operations.
I'm looking for some views on how to go about resolving this challenge. I have a variable say #Var1 which holds a SQL statement within it.
Example
#Var1 = `SELECT * from another_table WHERE City IS NOT NULL AND Address IS NOT NULL`
When I execute this variable
EXECUTE sp_executesql #Var1
I get the desired result, City and Address excluding NULL values.
I am hoping to update a existing table (tbl1) based on execution result of #Var1:
Something like:
UPDATE TABLE tbl1 AS (EXECUTE sp_executesql #Var1)
Is something like this even possible? Or what approach can I take to get the result of #Var1 into tbl1?
Thanks in advance.
My apologies for including links for each step but they deserve the credit.
There are 2 steps. Insert into a temp-table then merge the data from that temp-table into your final table.
You will have to insert into a Temp Table first.
INSERT INTO #TABLE EXEC #query with SQL Server 2000
Then you have to merge that data into you main table.
SQL MERGE statement to update data
try This
UPDATE tbl1
SET Column1= b.Column1,
SET Column2= b.Column2,
SET Column3= b.Column3,
FROM tbl1 a
INNER JOIN (
SELECT Column1,Column2,Column3
FROM another_table WHERE City IS NOT NULL AND Address IS NOT NULL ) b ON a.city_Id = b.city_Id
I want to perform a "soft delete". I.e. insert a new entry when matched and when not matched just insert an entry.
For example:
MERGE TargetTable AS targetT
USING SourceTable AS sourceT ON sourceT.Npi = targetT.Npi
WHEN MATCHED AND IsNull(targetT.SPI, '') <> '' THEN
UPDATE SET targetT.Isdelete = 1 --Update Only One Column
--And also insert a new row
WHEN NOT MATCHED
--Perform insert operation
The merge statement will not work this way. You would have to do it the old fashioned way (using a stored procedure would work).
It looks like you are trying to create an archive record. If this is true I suggest you use the merge as it is intended to be used and use an after update trigger to do your auditing needs.
I having the scenario of loading the data from source table to target table. If the data from source is not present in target, then i need to insert. If it is present in the target table already, then update the status of the row to 'expire' and insert the column as new row. I used Merge query to do this. I can do insert if not exists and i can do update also. But while trying to insert when matched, it says insert not allowed in 'when matched' clause.
Please help me.. Thanks in advance
If you want to perform multiple actions for a single row of source data, you need to duplicate that row somehow.
Something like the following (making up table names, etc):
;WITH Source as (
SELECT Col1,Col2,Col3,t.Dupl
FROM SourceTable,(select 0 union all select 1) t(Dupl)
)
MERGE INTO Target t
USING Source s ON t.Col1 = s.Col1 and s.Dupl=0 /* Key columns here */
WHEN MATCHED THEN UPDATE SET Expired = 1
WHEN NOT MATCHED AND s.Dupl=1 THEN INSERT (Col1,Col2,Col3) VALUES (s.Col1,s.Col2,s.Col3);
You always want the s.Dupl condition in the not matched branch, because otherwise source rows which don't match any target rows would be inserted twice.
From the example you posted as a comment, I'd change:
MERGE target AS tar USING source AS src ON src.id = tar.id
WHEN MATCHED THEN UPDATE SET D_VALID_TO=#nowdate-1, C_IS_ACTIVE='N', D_LAST_UPDATED_DATE=#nowdate
WHEN NOT MATCHED THEN INSERT (col1,col2,col3) VALUES (tar.col1,tar.col2,tar.col3);
into:
;WITH SourceDupl AS (
SELECT id,col1,col2,col3,t.Dupl
FROM source,(select 0 union all select 1) t(Dupl)
)
MERGE target AS tar USING SourceDupl as src on src.id = tar.id AND Dupl=0
WHEN MATCHED THEN UPDATE SET D_VALID_TO=#nowdate-1, C_IS_ACTIVE='N', D_LAST_UPDATED_DATE=#nowdate
WHEN NOT MATCHED AND Dupl=1 THEN INSERT (col1,col2,col3) VALUES (src.col1,src.col2,src.col3);
I've changed the values in the VALUES clause, since in a NOT MATCHED branch, the tar table doesn't have a row to select values from.
Check out one of those many links:
Using SQL Server 2008's MERGE Statement
MERGE on Technet
Introduction to MERGE statement
SQL Server 2008 MERGE
Without actually knowing what your database tables look like, we cannot be of more help - you need to read those articles and figure out yourself how to apply this to your concrete situation.