Insert is not happening in Merge where update is happening - sql

I have a table FlowTrack:
CREATE TABLE [dbo].[FlowTrack]
(
[RowID] [tinyint] IDENTITY(1,1) NOT NULL,
[ApplicationNo] [varchar](50) NOT NULL,
[StatusID] [tinyint] NOT NULL,
[UpdateType] [varchar](100) NULL,
[CreatedDate] [datetime] NULL,
[CreatedBy] [varchar](50) NULL
) ON [PRIMARY]
Sample data:
RowID ApplicationNo StatusID UpdateType CreatedDate CreatedBy
1 BPS/ANA/MO/7/0146215 1 3 2015-06-17 12:59:56.387 Tests
2 BPS/BHI/20/0164615 1 3 2015-06-17 12:57:57.727 Tester
3 BPS/BHI/6/0204815 1 3 2015-06-17 12:57:57.727 Tester
My procedure :
ALTER procedure Flowtrack
(#APPNO varchar(50),
#vc_status CHAR(1),
#CreatedBy varchar(50) )
AS
BEGIN
MERGE INTO [dbo].Flowtrack AS target
USING (SELECT ApplicationNo, StatusID
FROM Flowtrack
WHERE ApplicationNo = #APPNO AND [StatusID] = #vc_status) AS source
ON target.ApplicationNo = Source.ApplicationNo
WHEN MATCHED THEN
UPDATE
SET
target.ApplicationNo = source.ApplicationNo,
target.StatusID = source.StatusID,
target.UpdateType = 3,
target.CreatedDate = getdate(),
target.CreatedBy = #CreatedBy
WHEN NOT MATCHED BY TARGET THEN
INSERT
([ApplicationNo], [StatusID], [UpdateType],
[CreatedDate], [CreatedBy])
VALUES (source.ApplicationNo, source.StatusID, 3,
getdate(), #CreatedBy);
end
When I'm doing update it is updating
EXEC Flowtrack 'BPS/ANA/MO/7/0146215',3,'Tests'
but when trying to insert, it is not inserting a new record
I'm unable to find out why it is not inserting
To insert new application no
EXEC Flowtrack 'BPS/ANA/MO/7',3,'Test'

Here is the problem:
MERGE INTO [dbo].Flowtrack AS target
USING (SELECT ApplicationNo, StatusID
FROM Flowtrack
WHERE ApplicationNo = #APPNO AND [StatusID] = #vc_status) AS source
ON target.ApplicationNo = Source.ApplicationNo
AND target.StatusID = Source.StatusID
The problem is when you pass new AppNo, then source is empty and there is nothing to insert.
Change to:
MERGE INTO [dbo].Flowtrack AS target
USING (SELECT ApplicationNo, StatusID
FROM (VALUES(#APPNO, #vc_status, #CreatedBy)) t(ApplicationNo, StatusID , CreatedBy)
) AS source
ON target.ApplicationNo = Source.ApplicationNo
AND target.StatusID = Source.StatusID

Related

SQL Merge Statement Assistance

Good afternoon,
I'm trying to do a SQL merge with a few steps before this one.
Drop Temp Table (If it exists)
Create a temp table (Source)
USE Kronos Create table #tempGlobal --Source ( [800#] [bigint] NOT NULL, [FirstName] [varchar](20) NOT NULL, [MI] CHAR(20) NULL, [LastName] [nvarchar](30) NOT NULL, [NickName] CHAR(15) NULL, [Position] [nvarchar](50) NULL, [Department] [varchar](30) NULL, [EmployeeStatus] [char](20) NOT NULL, [CredentialDocument] VARCHAR(10) NOT NULL, [RehireDate] [date] NULL, [DateHired] [date] NOT NULL, [DateTerminated] [date] NULL)
Bulk insert from CSV into the temp table
Merge source table and target table
I don't get any job errors, but the data doesn't get updated in my test sessions.
Step 4:
` Select t.[800#], t.[FirstName], t.[MI], t.[LastName], t.[NickName], t.[Position], t.[Department], t.[EmployeeStatus], t.[CredentialDocument], t.[RehireDate], t.[DateHired], t.[DateTerminated]
From [dbo].[Employees] t --Target
Merge [dbo].[Employees] t
using [dbo].[#tempGlobal] s --Source Temp Table
on t.[800#] = s.[800#] --Stays the same even if rehired
WHEN MATCHED
AND
(
s.[LastName] <> t.[LastName]
OR s.[Position] <> t.[Position]
OR s.[Department] <> t.[Department]
OR s.[EmployeeStatus] <> t.[EmployeeStatus]
OR s.[RehireDate] <> t.[RehireDate]
OR s.[DateTerminated] <> t.[LastName]
)
THEN
UPDATE SET LastName = s.LastName,
Position = s.Position,
Department = s.Department,
EmployeeStatus = s.EmployeeStatus,
RehireDate = s.RehireDate,
DateTerminated = s.DateTerminated
WHEN NOT MATCHED --Source table doesn't match target table so we need to update the target table to match
THEN INSERT
(
[800#],
[FirstName],
[MI],
[LastName],
[NickName],
[Position],
[Department],
[EmployeeStatus],
[CredentialDocument],
[RehireDate],
[DateHired],
[DateTerminated])
VALUES
(
s.[800#],
s.FirstName,
s.MI,
s.LastName,
s.NickName,
s.Position,
s.Department,
s.EmployeeStatus,
s.CredentialDocument,
s.RehireDate,
s.DateHired,
s.DateTerminated);`

Get Identity of destination table when using **DELETE FROM ... OUTPUT ... INTO**

I use bellow code to archive old data in ArchiveTable and delete archived data from SourceTable
DELETE FROM SourceTable
OUTPUT
DELETED.[ID],
DELETED.[Code],
DELETED.[Title]
INTO ArchiveTable([OldID], [Code], [Title])
WHERE Condition
Structure of tables:
CREATE TABLE [SourceTable](
[ID] [INT] IDENTITY(1,1) NOT NULL,
[Code] [VARCHAR](16) NULL,
[Title] [NVARCHAR](128) NULL,
CONSTRAINT [PK_SourceTable] PRIMARY KEY CLUSTERED ([ID] ASC)
)
GO
CREATE TABLE [ArchiveTable](
[ID] [INT] IDENTITY(1,1) NOT NULL,
[OldID] [INT] NOT NULL,
[Code] [VARCHAR](16) NULL,
[Title] [NVARCHAR](128) NULL,
CONSTRAINT [PK_ArchiveTable] PRIMARY KEY CLUSTERED ([ID] ASC)
)
GO
I need to return deleted records and ArchiveTable.[ID] to application. I change the code like this:
DELETE FROM SourceTable
OUTPUT
DELETED.[ID],
DELETED.[Code],
DELETED.[Title]
INTO ArchiveTable([OldID], [Code], [Title])
OUTPUT DELETED.*
WHERE Condition
This code return deleted records but I don't know how to get ID of ArchiveTable for this records. Look at ArchiveTable structure, It has OldID column that refer to SourceTable.ID and ID column that it is an Identity column of ArchiveTable. I need to ArchiveTable.ID in final result.
You can use a temporary table
CREATE TABLE #DeletedRows(
[ID] [INT] NOT NULL,
[Code] [VARCHAR](16) NULL,
[Title] [NVARCHAR](128) NULL
)
DELETE SourceTable
OUTPUT
DELETED.[ID],
DELETED.[Code],
DELETED.[Title]
INTO #DeletedRows([ID], [Code], [Title])
WHERE Condition
INSERT ArchiveTable([OldID], [Code], [Title])
OUTPUT INSERTED.*
SELECT [ID], [Code], [Title]
FROM #DeletedRows
DROP TABLE #DeletedRows
A variant with a table variable
DECLARE #DeletedRows TABLE(
[ID] [INT] NOT NULL,
[Code] [VARCHAR](16) NULL,
[Title] [NVARCHAR](128) NULL
)
DELETE SourceTable
OUTPUT
DELETED.[ID],
DELETED.[Code],
DELETED.[Title]
INTO #DeletedRows([ID], [Code], [Title])
WHERE Condition
INSERT ArchiveTable([OldID], [Code], [Title])
OUTPUT INSERTED.*
SELECT [ID], [Code], [Title]
FROM #DeletedRows
I found an interesting variant using DML with OUTPUT in SP and INSERT...EXEC... after that:
Test tables:
CREATE TABLE TestTable(
ID int NOT NULL PRIMARY KEY,
Title varchar(10) NOT NULL
)
CREATE TABLE TestTableLog(
LogID int NOT NULL IDENTITY,
OperType char(1) NOT NULL,
CHECK(OperType IN('I','U','D')),
ID int NOT NULL,
Title varchar(10) NOT NULL
)
DML procedures:
CREATE PROC InsTestTable
#ID int,
#Title varchar(10)
AS
INSERT TestTable(ID,Title)
OUTPUT inserted.ID,inserted.Title,'I' OperType
VALUES(#ID,#Title)
GO
CREATE PROC UpdTestTable
#ID int,
#Title varchar(10)
AS
UPDATE TestTable
SET
Title=#Title
OUTPUT inserted.ID,inserted.Title,'U' OperType
WHERE ID=#ID
GO
CREATE PROC DelTestTable
#ID int
AS
DELETE TestTable
OUTPUT deleted.ID,deleted.Title,'D' OperType
WHERE ID=#ID
GO
Tests:
-- insert test
INSERT TestTableLog(ID,Title,OperType)
EXEC InsTestTable 1,'A'
INSERT TestTableLog(ID,Title,OperType)
EXEC InsTestTable 2,'B'
INSERT TestTableLog(ID,Title,OperType)
EXEC InsTestTable 3,'C'
-- update test
INSERT TestTableLog(ID,Title,OperType)
EXEC UpdTestTable 2,'BBB'
-- delete test
INSERT TestTableLog(ID,Title,OperType)
EXEC DelTestTable 3
GO
-- show resutls
SELECT *
FROM TestTableLog
Maybe it'll be interesting to someone.

replace a computed column with a logic that works with INSERT

I have a table called tblPacks.
CREATE TABLE [dbo].[tblPacks]
(
[ID] [int] NOT NULL,
[BatchNumber] [varchar](30) NULL,
[PackID] VARCHAR(50),
[Status] [int] NULL
)
And a stored procedure spInsertPacks.
CREATE PROCEDURE spInsertPacks
#ID INT,
#BatchNumber VARCHAR(30),
#Count INT
AS
BEGIN
INSERT INTO tblPacks
Values
(
#ID,
#BatchNumber,
CONVERT([varchar](50),
'PK'+
case
when len(#ID)<=(3) then CONVERT([varchar](20),right((0.001)*#ID,(3)),0)
else CONVERT([varchar](20),#ID,0)
end,0),0)
END
If ID of data type INT inserted in an order like 1,2,3,4,5... the above logic works fine. But there is no restriction for a user to enter random numbers. I want a stored procedure to generate PackID(PK001,PK002..) sequence in order, irrespective of #ID and ID. Cannot be an identity Column. How can I do that?
Actually This PackID is a barcode If barcode already existed for Pack then that sequence may not be same with the sequence we used and Newly generated barcodes which we are generating will be in seuquence PK001
Sample Output:-
ID BatchNumber PackID Status
1 b1 PK001 0
1 b2 Pk002 0
5 b7 ABC768 0
3 b2 PK003 0
I have simplified the logic a bit for generating PackID
Add a new column(identifier) for identifying the code and use it for PackID generation and for sequence use Identity column
CREATE TABLE [dbo].[tblPacks]
(
Iden_ID INT IDENTITY(1, 1),
[ID] [INT] NOT NULL,
[BatchNumber] [VARCHAR](30) NULL,
[Identifier] [VARCHAR](50),
[PackID] AS [Identifier]
+ CASE
WHEN Iden_ID <= 999 THEN RIGHT('00' + CONVERT(VARCHAR(3), ID), 3)
ELSE CONVERT([VARCHAR](20), ID, 0)
END,
[Status] [INT] NULL
)
To check the working
INSERT INTO [dbo].[tblPacks]
([ID],identifier,[BatchNumber],[Status])
VALUES (1,'pk','bat',1)
SELECT *
FROM [tblPacks]

updated record is inserting into the history table not the old record

i have two tables Test and TestHistory
CREATE TABLE [dbo].[TEST](
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Test_History](
[ID] [int] NULL,
[Name] [varchar](10) NULL,
[Status] [char](1) NULL,
[CreatedDate] [datetime] NULL
) ON [PRIMARY]
GO
INSERT INTO TEST ([ID],[Name],[Status],[CreatedDate])values (1,'Mohan','A',GETDATE())
Created Trigger :
ALTER TRIGGER [dbo].[trg_Test]
ON [dbo].[TEST]
FOR UPDATE
AS
Declare #ID INT;
Declare #Name varchar(10);
Declare #Status CHAR(2);
Declare #CreatedDate DATETIME;
Select #ID = I.ID from INSERTED I
Select #Name = I.Name from INSERTED I
Select #Status = I.Status from INSERTED I
Select #CreatedDate = I.CreatedDate from INSERTED I
INSERT INTO [dbo].[Test_history]
([ID]
,[Name]
,[Status]
,[CreatedDate]
)
SELECT #ID,
#Name,
#Status,
GETDATE()
FROM INSERTED I
WHERE #ID = [ID]
When I'm updating the record like
Update [TEST] SET Status = 'I' then the old record with Status = 'A' should inserted but the what ever i'm updating it has been inserting into Testhistory table not the old record
where i'm doing wrong and how to insert old value
like if i updating Status = 'I' and in history table Status = 'A' shoul be inserted
You need to INSERT from DELETED not from INSERTED.
See examples here Understanding SQL Server inserted and deleted tables for DML triggers.
Like Karl mentioned, you need to refer deleted table for old updated row information. Apart from that your existing code doesn't work when you update more than a single row. What you need is something like this.
ALTER TRIGGER [dbo].[trg_Test]
ON [dbo].[TEST]
FOR UPDATE
AS
INSERT INTO [dbo].[Test_history]
(
[ID],
[Name],
[Status],
[CreatedDate]
)
SELECT ID,
Name,
Status,
GETDATE()
FROM deleted d

Sql stored procedure taking lot of time for execution

I am facing a big issue, the below stored procedure taking lot of time for execution. Please help me to find issues with following stored procedure .
We need to insert bulk subscriber list from excel in to database. But only 60 subscribers are getting inserted in to db in one minute.
Please help me to resolve the issue.
USE [SMS]
GO
/****** Object: StoredProcedure [dbo].[SP_ProcessFile] Script Date: 01/30/2015 12:56:59 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SP_ProcessFile]
#JobCode varchar(25)
WITH RECOMPILE
AS
declare
#jobCode1 Varchar(50),
#count int,#Code varchar(50),#Name [varchar](50),#Date Datetime,#Status int,
#i int,#EUCount int,#SubCount int,
#Add1 nvarchar(3000) ,
#Add2 nvarchar(500) ,
#Add3 nvarchar(500)
,#refdate [varchar](50) ,
#reference [varchar](50) ,
#Joined [varchar](50),
#Joinmonth [nvarchar](50),
#Activated [varchar](50),
#ActivMonth [nvarchar](50),
#Center [varchar](50) ,
#Region [varchar](50) ,
#Area [varchar](50) ,
#Modem [varchar](50) ,
#Adomstatus [varchar](50)
, #AddCode [varchar](50)
set #i = 1
Set #jobCode1 =#JobCode
BEGIN
SET NOCOUNT ON
Set #Status = (Select Distinct(status) from TSMST1005 where Jobcode = #jobCode1)
if (#Status = 0)
begin
Select '1' as res
end
else
begin
CREATE TABLE #tblSMS(pID int identity(1,1),
[Reference] [nvarchar](50) NULL,
[Date] [varchar](50) NOT NULL,
[Code] [nvarchar](50) NULL,
[Subname] [varchar](500) NULL,
[Address1] [nvarchar](3000) NULL,
[Address2] [nvarchar](500) NULL,
[Address3] [nvarchar](500) NULL,
[Joined] [varchar](50) NULL,
[Joinmonth] [varchar](50) NULL,
[Activated] [varchar](50) NULL,
[ActivMonth] [nvarchar](50) NULL,
[Center] [varchar](50) NULL,
[Region] [varchar](50) NULL,
[Area] [varchar](50) NULL,
[Modem] [varchar](50) NULL,
[Adomstatus] [varchar](50) NULL,
[RefDate] [varchar](50) NOT NULL)
insert into #tblSMS
SELECT Reference,[Date],
Code,Subname ,Address1 ,Address2 ,Address3 ,
Joined , Joinmonth ,Activated, ActivMonth ,
Center,Region,Area,Modem,Adomstatus ,refdate FROM TSMST1005 where jobcode = #jobCode1 and Status =1
WHILE #i <= (SELECT COUNT(*) FROM #tblSMS)
BEGIN
SELECT
#Code =Code,
#Name = Subname,
#Date =[Date],
#Add1 =Address1 ,
#Add2 =Address2 ,
#Add3= Address3,
#reference =Reference ,
#Joined = Joined,
#Joinmonth =Joinmonth,
#Activated =Activated,
#ActivMonth =ActivMonth,
#Center = Center,
#Region = Region,
#Area= Area,
#Modem = Modem ,
#Adomstatus =Adomstatus,
#refdate = RefDate
From #tblSMS where pID = #i
Insert into TCMST5001 (CompanyCode , Address1,Address2 ,Address3 ,CreatedDate ,Status) values('001',#Add1 ,#Add2,#Add3,GETDATE(),1)
Set #count = SCOPE_IDENTITY()
Set #AddCode = 'ADD' + Cast(#count As Varchar(10))
Update TCMST5001 Set Code =#AddCode Where AddressID =#count
Set #EUCount = (Select COUNT(*) from TCCOM0005 where EnterpriseUnitCode = #Center)
if (#EUCount = 0)
Begin
Insert into TCCOM0005(AddressCode,CompanyCode,EnterpriseUnitCode,EnterpriseUnitName,Status) values(#count ,'001',#Center,#Center ,1)
END
Set #SubCount = (Select COUNT(*) from TSMST1001 where Subscriber = #Code)
if (#SubCount =0)
begin
Insert into TSMST1001(ActivationDate ,refdate , Address ,AlternateName ,Area ,Region ,Subscriber,Name ,date ,CreatedDate ,EnterpriseUnit ,Status)
values(#Activated,#refdate ,#count ,#Name,#Area,#Region,#Code,#Name ,#Joined ,GETDATE(),#Center,#Adomstatus)
end
Insert into TSMST1003 (Device ,CreatedDate ,Subscriber,StartDate) values
(#Modem,GETDATE(),#Code,#Activated)
SET #i = #i + 1
Update TSMST1005 Set Status = 0 where Jobcode = #jobCode1
Select '3' as res
END
END
Drop table #tblSMS
end
It is quite hard to give you 100% working procedure. However your problem is that you are inserting records row by row. Instead of doing that you need to insert records in BULK. That would work A LOT faster. There is rewritten procedure. You still need to rewrite 2 inserts by the same logic. Of course there could be bugs as it is fully untested. Anyway, here we are:
USE [SMS]
GO
/****** Object: StoredProcedure [dbo].[SP_ProcessFile] Script Date: 01/30/2015 12:56:59 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SP_ProcessFile] #JobCode VARCHAR(25)
WITH RECOMPILE
AS
BEGIN
SET NOCOUNT ON
SET #Status = ( SELECT TOP 1 status
FROM TSMST1005
WHERE Jobcode = #jobCode
)
IF ( #Status = 0 )
BEGIN
SELECT '1' AS res
END
ELSE
BEGIN
CREATE TABLE #tblSMS
(
pID INT IDENTITY(1, 1) ,
[Reference] [NVARCHAR](50) NULL ,
[Date] [VARCHAR](50) NOT NULL ,
[Code] [NVARCHAR](50) NULL ,
[Subname] [VARCHAR](500) NULL ,
[Address1] [NVARCHAR](3000) NULL ,
[Address2] [NVARCHAR](500) NULL ,
[Address3] [NVARCHAR](500) NULL ,
[Joined] [VARCHAR](50) NULL ,
[Joinmonth] [VARCHAR](50) NULL ,
[Activated] [VARCHAR](50) NULL ,
[ActivMonth] [NVARCHAR](50) NULL ,
[Center] [VARCHAR](50) NULL ,
[Region] [VARCHAR](50) NULL ,
[Area] [VARCHAR](50) NULL ,
[Modem] [VARCHAR](50) NULL ,
[Adomstatus] [VARCHAR](50) NULL ,
[RefDate] [VARCHAR](50) NOT NULL
)
INSERT INTO #tblSMS
SELECT Reference ,
[Date] ,
Code ,
Subname ,
Address1 ,
Address2 ,
Address3 ,
Joined ,
Joinmonth ,
Activated ,
ActivMonth ,
Center ,
Region ,
Area ,
Modem ,
Adomstatus ,
RefDate
FROM TSMST1005
WHERE jobcode = #jobCode1
AND Status = 1
WHILE #i <= ( SELECT COUNT(*)
FROM #tblSMS
)
BEGIN
DECLARE #minPK INT;
SELECT #minPK = MAX(AddressID ) FROM TCMST5001; -- I believe that it is identity column. If not change it to the proper one
INSERT INTO TCMST5001
( CompanyCode ,
Address1 ,
Address2 ,
Address3 ,
CreatedDate ,
Status
)
SELECT '001', Address1, Address2, Address3, GETDATE(), 1 FROM #tblSMS;
SET #AddCode = 'ADD' + CAST(#count AS VARCHAR(10))
UPDATE TCMST5001
SET Code = 'ADD' + CAST(AddressID AS VARCHAR(10))
WHERE AddressID > #minPK ;
INSERT INTO TCCOM0005
SELECT ee.cnt, t.center, t.Center, 1
FROM #tblSMS t
CROSS APPLY ( SELECT COUNT(*) AS cnt FROM TCCOM0005 e WHERE e.EnterpriseUnitCode = t.Center) ee
WHERE ee.cnt > 0
-- THE SAME LOGIC MUST BE DONE WITH THESE 2 INSERTS
SET #SubCount = ( SELECT COUNT(*)
FROM TSMST1001
WHERE Subscriber = #Code
)
IF ( #SubCount = 0 )
BEGIN
INSERT INTO TSMST1001
( ActivationDate ,
refdate ,
Address ,
AlternateName ,
Area ,
Region ,
Subscriber ,
Name ,
date ,
CreatedDate ,
EnterpriseUnit ,
Status
)
VALUES ( #Activated ,
#refdate ,
#count ,
#Name ,
#Area ,
#Region ,
#Code ,
#Name ,
#Joined ,
GETDATE() ,
#Center ,
#Adomstatus
)
END
INSERT INTO TSMST1003
( Device ,
CreatedDate ,
Subscriber ,
StartDate
)
VALUES ( #Modem ,
GETDATE() ,
#Code ,
#Activated
)
UPDATE t
FROM TSMST1005 t
SET Status = 0
JOIN #tblSMS tmp
ON tmp.jobCode1 = t.Jobcode
SELECT '3' AS res
END
END
DROP TABLE #tblSMS
END
some tips will be. Avoid doing this in every step.
SELECT COUNT(*) FROM #tblSMS
instead assign the count value to a local variable, and check the i against the same.
Also you are selecting values from these tables ( TCCOM0005, TSMST1001, TSMST1003 ) frequently , It will be good to check if these tables have proper indexes.