MERGE and condition on the match - sql-server

I have a table type that I am sending as a variable in my stored procedure.
CREATE TYPE OperationKeysTableType AS TABLE
(
[KeysId] [int] NOT NULL,
[OperationId] [int] NOT NULL,
[IsChecked] [bit] NOT NULL
)
And then I want to use this table to do a search, if they match or not, insert or delete but only if it is not checked like so:
MERGE INTO dbo.tblOperationKeys AS T
USING (SELECT VT.KeysId, VT.OperationId as OperationId FROM #ValuesTable AS VT) AS S
ON T.KeysId = S.KeysId AND T.OperationId = S.OperationId
WHEN MATCHED AND S.IsChecked = 0 THEN --DELETE
DELETE
WHEN NOT MATCHED THEN--INSERT
INSERT (KeysId, OperationId) VALUES (S.KeysId, S.OperationId)
OUTPUT $action AS ChangesMade, inserted.OperationKeysId AS new_OperationKeysId, deleted.OperationKeysId AS old_OperationKeysId;
My issue is that SQL tells me IsChecked is invalid. Any idea on what I'm doing wrong?

You aren't SELECTING IsChecked.
At the line:
USING (SELECT VT.KeysId, VT.OperationId as OperationId FROM #ValuesTable AS VT) AS S
You need:
USING (SELECT VT.KeysId, VT.OperationId as OperationId, VT.IsChecked as IsChecked FROM #ValuesTable AS VT) AS S

Related

Create a table with data validation based on 2 or more columns

I have been trying to create a production ERP using C# and SQL Server.
I want to create a table where the insert statement should only occur when at least one of the 3 main columns have a different value.
The main columns are prod_date, order_no, mach_no, shift_no, prod_type. If all the values are repeated a second time the data must not be entered.
create table p1_order(id int not null,
order_no int not null,
prod_date date notnull,
prod_type nvarchar(5),
shift_no int not null,
mach_no nvarchar(5) not null,
prod_qty,
float not null)
Based on the information you provided, You should check for the identical values when executing insert query, while writing your code. for example you can write:
if(prod_date == order_no == mach_no)// or any other 3 variables
{
//error of identical values
}
else{
// your insert query
}
The best way to implement this is by creating a unique constraint on the table.
alter table p1_order
add constraint UC_Order unique (prod_date,order_no,mach_no,shift_no,prod_type);
Due to some reason, if you are not able to create a unique constraint, you can write your query like the following using NOT EXISTS
insert into p1_order (order_no , prod_date , prod_type <remaining columns>)
select 123, '2022-09-20 15:11:43.680', 't1' <remaining values>
where not exists
(select 1
from p1_order
where order_no = 123 AND prod_date = '2022-09-20 15:11:43.680' <additional condition>)

How to use MERGE-statement with VARBINARY data

I'm stuck trying to figure out how to get one of the MERGE statements to work. See below code snippet:
DECLARE #PipelineRunID VARCHAR(100) = 'testestestestest'
MERGE [TGT].[AW_Production_Culture] as [Target]
USING [SRC].[AW_Production_Culture] as [Source]
ON [Target].[MD5Key] = [Source].[MD5Key]
WHEN MATCHED AND [Target].[MD5Others] != [Source].[MD5Others]
THEN UPDATE SET
[Target].[CultureID] = [Source].[CultureID]
,[Target].[ModifiedDate] = [Source].[ModifiedDate]
,[Target].[Name] = [Source].[Name]
,[Target].[MD5Others] = [Source].[MD5Others]
,[Target].[PipelineRunID] = #PipelineRunID
WHEN NOT MATCHED BY TARGET THEN
INSERT VALUES (
[Source].[AW_Production_CultureKey]
,[Source].[CultureID]
,[Source].[ModifiedDate]
,[Source].[Name]
,#PipelineRunID
,[Source].[MD5Key]
,[Source].[MD5Others]);
When I try and run this query I receive the following error:
Msg 257, Level 16, State 3, Line 16
Implicit conversion from data type varchar to varbinary is not allowed. Use the CONVERT function to run this query.
The only VARBINARY column types are MD5Key and MD5Others. As they are both linked to their corresponding columns I don't understand why my error message indicates there is a VARCHAR problem involved. Does anybody understand how and why I should use a CONVERT() function here?
Thanks!
--EDIT: Schema definitions
CREATE VIEW [SRC].[AW_Production_Culture]
WITH SCHEMABINDING
as
SELECT
CAST(CONCAT('',[CultureID]) as VARCHAR(100)) as [AW_Production_CultureKey]
,CAST(HASHBYTES('MD5',CONCAT('',[CultureID])) as VARBINARY(16)) as [MD5Key]
,CAST(HASHBYTES('MD5',CONCAT([ModifiedDate],'|',[Name])) as VARBINARY(16)) as [MD5Others]
,[CultureID],[ModifiedDate],[Name]
FROM
[SRC].[tbl_AW_Production_Culture]
CREATE TABLE [TGT].[AW_Production_Culture](
[AW_Production_CultureKey] [varchar](100) NOT NULL,
[CultureID] [nchar](6) NULL,
[ModifiedDate] [datetime] NULL,
[Name] [nvarchar](50) NULL,
[MD5Key] [varbinary](16) NOT NULL,
[MD5Others] [varbinary](16) NOT NULL,
[RecordValidFrom] [datetime2](7) GENERATED ALWAYS AS ROW START NOT NULL,
[RecordValidUntil] [datetime2](7) GENERATED ALWAYS AS ROW END NOT NULL,
[PipelineRunID] [varchar](36) NOT NULL,
PRIMARY KEY CLUSTERED
(
[MD5Key] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY],
PERIOD FOR SYSTEM_TIME ([RecordValidFrom], [RecordValidUntil])
) ON [PRIMARY]
WITH
(
SYSTEM_VERSIONING = ON ( HISTORY_TABLE = [TGT].[AW_Production_Culture_History] )
)
Reposting my comment as an answer for the sweet, sweet, internet points:
You're getting that error because your varbinary value is being inserted into a varchar column. As your columns have the correct types already then it means your INSERT clause has mismatched columns.
As it is, your MERGE statement is not explicitly listing the destination columns - you should always explicitly list columns in production code so that your DML queries won't break if columns are added or reordered or marked HIDDEN.
So to fix this, change your INSERT clause to explicitly list destination column names.
Also, when using MERGE you should use HOLDLOCK (Or a more suitable lock, if applicable) - otherwise you’ll run into concurrency issues. MERGE is not concurrency-safe by default!
Minor nit-picks that are largely subjective:
I personally prefer avoiding [escapedName] wherever possible and prefer using short table aliases.
e.g. use s and t instead of [Source] and [Target].
"Id" (for "identity" or "identifier") is an abbreviation, not an acronym - so it should be cased as Id and not ID.
Consider using an OUTPUT clause to help diagnose/debug issues too.
So I'd write it like so:
DECLARE #PipelineRunId VARCHAR(100) = 'testestestestest'
MERGE INTO
tgt.AW_Production_Culture WITH (HOLDLOCK) AS t
USING
src.AW_Production_Culture AS s ON t.MD5Key = s.MD5Key
WHEN MATCHED AND t.MD5Others != s.MD5Others THEN UPDATE SET
t.CultureId = s.CultureId,
t.ModifiedDate = s.ModifiedDate,
t.Name = s.Name,
t.MD5Others = s.MD5Others,
t.PipelineRunID = #PipelineRunId
WHEN NOT MATCHED BY TARGET THEN INSERT
(
AW_Production_CultureKey,
CultureId,
ModifiedDate,
[Name],
PipelineRunId,
MD5Key,
MD5Others
)
VALUES
(
s.AW_Production_CultureKey,
s.CultureId,
s.ModifiedDate,
s.[Name],
#PipelineRunId,
s.MD5Key,
s.MD5Others
)
OUTPUT
$action AS [Action],
inserted.*,
deleted.*;

Temp tables, Column name or number of supplied values does not match table definition

Even though this tends to look as a duplicate, I had to post it as I can't seem to spot the error.
I don't know if I am mad or what but I can't seem to spot why there is a mismatch in the number of supplied values.
Here are they:
CREATE TABLE #TIPSTOPE_TS
(
TIP INT NULL,
SIFVAL VARCHAR(5),
GRUPA INT NULL,
DATUMOD VARCHAR(15),
PASIVNA DECIMAL(15,4) NULL DEFAULT(0),
REDOVNA DECIMAL(15,4) NULL DEFAULT(0),
ZATEZNA DECIMAL(15,4) NULL DEFAULT(0),
STOPA DECIMAL(15,4) NULL DEFAULT(0),
DATUMDO VARCHAR(15),
KONTO VARCHAR(15),
)
INSERT INTO #TIPSTOPE_TS
SELECT TS.TIP,
TS.SIFVAL,
TS.GRUPA,
CASE WHEN ISDATE(MAX(TS.DATUMOD)) = 0 THEN '2017.12.31' ELSE MAX(TS.DATUMOD) END AS DATUMOD,
CAST (2 AS DECIMAL(10,4)) AS PASIVNA,
CAST (1 AS DECIMAL(10,4)) AS REDOVNA,
CAST (3 AS DECIMAL(10,4)) AS ZATEZNA,
TS.REDOVNA,
TS.DATUMDO,
TP.M1 AS KONTO
FROM TIPSTOPE TS WITH(NOLOCK)
JOIN TIPPART TP WITH(NOLOCK) ON TP.TIP = TS.TIP
WHERE TS.DATUMOD <= '2017.12.31'
GROUP BY TS.TIP,TS.SIFVAL,TS.GRUPA,TP.M1,TS.DATUMDO,TS.REDOVNA
CREATE NONCLUSTERED INDEX IX_TIPSTOPE_TS ON #TIPSTOPE_TS (TIP, GRUPA, SIFVAL)
INCLUDE (DATUMOD)
And the second one...
CREATE TABLE #UNPVT_TIPSTOPE_TS
(
TIP INT NULL,
SIFVAL VARCHAR(5) NULL,
GRUPA INT NULL,
DATUMOD VARCHAR(10) NULL,
TIP_KS VARCHAR(15) NULL,
KAMATNA_STOPA DECIMAL(15,4) NULL DEFAULT(0),
DATUMDO VARCHAR(10) NULL,
)
INSERT INTO #UNPVT_TIPSOPE_TS
SELECT TIP, SIFVAL, GRUPA, DATUMOD, TIP_KS, KAMATNA_STOPA,DATUMDO
FROM
(
SELECT TIP, SIFVAL, GRUPA, DATUMOD, ISNULL(REDOVNA,0) AS REDOVNA, ISNULL(PASIVNA,0) AS PASIVNA, ISNULL(ZATEZNA,0) AS ZATEZNA,STOPA,DATUMDO
FROM #TIPSTOPE_TS
) P
UNPIVOT (KAMATNA_STOPA FOR TIP_KS IN (REDOVNA, PASIVNA, ZATEZNA)) AS UNPVT
The second temp tables is taking data from the first one.
When I try to create the second one error is thrown:
Insert error: Column name or number of supplied values does not match table definition
You are specifying the exact number of values that are needed. If you copy the whole code in new query window and execute it, it will work. Or in your current window drop the table table:
DROP TABLE #TIPSTOPE_TS;
DROP TABLE #UNPVT_TIPSTOPE_TS;
I mean execute only the above statements, and the execute the rest of the code. It should work again.
Sometime, when are debugging we forgot that the temporary table meta data is cached. For example, you can have the following code:
DROP TABLE IF EXISTS #TEST;
CREATE TABLE #TEST
(
[A] INT
);
INSERT INTO #TEST ([A])
SELECT 1;
And its valid. If we change it to this:
DROP TABLE IF EXISTS #TEST;
CREATE TABLE #TEST
(
[A] INT
,[B] INT
);
INSERT INTO #TEST ([A], [B])
SELECT 1, 2;
We will get:
Msg 207, Level 16, State 1, Line 9 Invalid column name 'B'.
Because, in the current session the #TEST table already exists and the engine is able to check that the B column does not exists. So, we need to drop the table manually, after the columns are changed, or we need to drop the tables at the end of our code statements.

Violation of PRIMARY KEY constraint when using cross apply and outer apply together

I was using Microsoft SQL Server. I want to use both functions to parse data going into my table. So I use cross apply and outer apply together.
CROSS APPLY CA_Parse_CorpActnDtls_fn(MessageID) ent
outer apply CA_Parse_CorpActnOptnDtls_fn(ev.MessageID) cod
But when I did this it complain about the following erro:
Violation of PRIMARY KEY constraint 'PK_AfterParse_CA_Events'. Cannot insert duplicate key in object 'dbo.AfterParse_CA_Events'. The duplicate key value is (105818432, 37819929).
The statement has been terminated.
The whole T-sql code looks like :
insert into AfterParse_CA_Events (
EventID
,MessageID
,cdtprFunction
,CreationDate
,MsgDefIdr
,EventType
,CFI
,EventProcessingType
,MndtryVlntryEvtTp
,RecordDate
,EffectiveDate
,DueBillRdmDate
,CUSIP
,LSCI_DateOfRecord
,RoundingDesc
)
SELECT ent.EventID
,ent.MessageID
,ent.cdtprFunction
,ent.CreationDate
,ent.MsgDefIdr
,ent.EventType
,ent.CFI
,ent.EventProcessingType
,ent.MndtryVlntryEvtTp
,ent.RecordDate
,ent.EffectiveDate_Cmpny
,ent.DueBillRdmDate
,ent.CUSIP
,ROXSQL.dbo.GetNthTradeDay_fn(
case when ent.EventProcessingType = 'DISN'
then COALESCE (ent.ExDividendDate, ent.RecordDate)
ELSE COALESCE(ent.EffectiveDate_Xchg, ent.EffectiveDate_Cmpny,cod.EarliestPaymentDate_Secu,cod.PaymentDate_Secu ,cod.PaymentDate_Cash)
END,-1) AS LSCI_DateOfRecord
,cod.RoundingDesc
FROM #EventsToDo ev
CROSS APPLY CA_Parse_CorpActnDtls_fn(MessageID) ent
outer apply CA_Parse_CorpActnOptnDtls_fn(ev.MessageID) cod
you can see that I need the second function CA_Parse_CorpActnOptnDtls_fn(ev.MessageID)
Because I want to compose an LSCI_DateOfRecord data using my user defined function. so is there any way to avoid the duplicate when I using the two functions together?
or is there any ways to build a temp list for the LSCI_DateOfRecord and RoundingDesc from the second function CA_Parse_CorpActnOptnDtls_fn(ev.MessageID) separately? And then I can update the table.
Any help is greatly appreciated.
Looking at the error and your SQL code, it's not APPLY operators causing the problem per se. It's the fact that one or both of the functions is returning more than a single row for set of EvenID & MessageID, and THAT is what's causing the PK violation.
Below is a simplified demonstration, using a string splitter function (DelimitedSplit8K)
IF OBJECT_ID('tempdb..#EventsToDo ', 'U') IS NOT NULL
DROP TABLE #EventsToDo ;
GO
CREATE TABLE #EventsToDo (
EventID BIGINT NOT NULL,
MessageID BIGINT NOT NULL,
MessageText VARCHAR(1000) NOT NULL
);
GO
INSERT #EventsToDo (EventID, MessageID, MessageText) VALUES
(105818432, 37819929, 'Part 1,Part 2,Part 3,Part 4,Part 5');
GO
-----------------------------------------------------------------
-- create the AfterParse_CA_Events table with PRIMARY KEY (EvenID, MessageID)...
IF OBJECT_ID('tempdb..#AfterParse_CA_Events', 'U') IS NOT NULL
DROP TABLE #AfterParse_CA_Events;
GO
CREATE TABLE #AfterParse_CA_Events (
EvenID BIGINT NOT NULL,
MessageID BIGINT NOT NULL,
MessagePart VARCHAR(1000) NULL
PRIMARY KEY (EvenID, MessageID)
);
GO
--===============================================================
-- see what happens when we try to insert the parsed message values
-- into AfterParse_CA_Events while it has a PK of (EvenID, MessageID)...
INSERT #AfterParse_CA_Events (EvenID, MessageID, MessagePart)
SELECT
etd.EventID,
etd.MessageID,
dsk.Item
FROM
#EventsToDo etd
CROSS APPLY dbo.DelimitedSplit8K(etd.MessageText, ',') dsk;
GO
--===============================================================
-- execute the code below in a separate execution
--===============================================================
-- now, let's modify the AfterParse_CA_Events table so that we have "MessagePartID"
-- and make that part of the PK
IF OBJECT_ID('tempdb..#AfterParse_CA_Events', 'U') IS NOT NULL
DROP TABLE #AfterParse_CA_Events;
GO
CREATE TABLE #AfterParse_CA_Events (
EvenID BIGINT NOT NULL,
MessageID BIGINT NOT NULL,
MessagePartID INT NOT NULL,
MessagePart VARCHAR(1000) NOT NULL
PRIMARY KEY (EvenID, MessageID, MessagePartID)
);
GO
--===============================================================
-- Now let's try the insertion again...
INSERT #AfterParse_CA_Events (EvenID, MessageID, MessagePartID, MessagePart)
SELECT
etd.EventID,
etd.MessageID,
dsk.ItemNumber,
dsk.Item
FROM
#EventsToDo etd
CROSS APPLY dbo.DelimitedSplit8K(etd.MessageText, ',') dsk;
GO
--===============================================================
-- check the inserted values...
SELECT
*
FROM
#AfterParse_CA_Events apce;
HTH, Jason

I want to merge two tables without any primary keys

We cannot create any additional column
Please keep that in mind
The whole intention of this script is to merge data into my temp table
when the data is matching don't have to do any thing.
if some data is present in #temp_cqm_class_template_xref and not in cqm_class_template_xref_temp then those data's has to be deleted from #temp_cqm_class_template_xref table
if it is the other way it has to be inserted into the #temp_cqm_class_template_xref table
IF OBJECT_ID('tempdb..#temp_cqm_class_template_xref') IS NOT NULL
DROP TABLE #temp_cqm_class_template_xref;
CREATE TABLE #temp_cqm_class_template_xref (
[template_name] [VARCHAR](30) NOT NULL
,[measure_id] [INT] NOT NULL
,[cqm_item_mstr_id] [INT] NOT NULL
,[created_by] [INT] NOT NULL
,[modified_by] [INT] NOT NULL
,[create_timestamp] [DATETIME] NOT NULL
,[modify_timestamp] [DATETIME] NOT NULL
);
MERGE INTO #temp_cqm_class_template_xref AS t
USING cqm_class_template_xref_temp AS s
ON (
t.template_name = s.template_name
AND t.measure_id = s.measure_id
AND t.cqm_item_mstr_id = s.cqm_item_mstr_id
)
WHEN NOT MATCHED
THEN
INSERT (
template_name
,measure_id
,cqm_item_mstr_id
,created_by
,modified_by
,create_timestamp
,modify_timestamp
)
VALUES (
s.template_name
,s.measure_id
,s.cqm_item_mstr_id
,s.created_by
,s.modified_by
,s.create_timestamp
,s.modify_timestamp
)
WHEN NOT MATCHED BY target
THEN
DELETE;
When I run this script, I get the following error:
Msg 10711, Level 15, State 1, Procedure
ngdev_cqm_class_template_xref_bcp_upld, Line 88 An action of type
'INSERT' is not allowed in the 'WHEN MATCHED' clause of a MERGE
statement
Merge is not a good technique to use. see:
https://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/
Merge is hard to debug and very hard to maintain later when you have problems with the data that it is trying to merge. Don't ever use it.
Instead write an insert using a select instead of a values clause and write a delete.
You may try something like left join and figure out what columns are null
in the opposite table;
According to your logic This might help you
update t
set t.template_name=null,
t.measure_id = null,
t.cqm_item_mstr_id = null
#temp_cqm_class_template_xref t Left join cqm_class_template_xref_temp s on
t.template_name = s.template_name
AND t.measure_id = s.measure_id
AND t.cqm_item_mstr_id = s.cqm_item_mstr_id
delete from #temp_cqm_class_template_xref
where measure_id is null and cqm_item_mstr_id is null and and template_name is null

Resources