SQL Server cursor - cannot assign variable properly [closed] - loops

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I use SQL Server 2016. I need to move users from a legacy system to a new one. But before bringing all users from the legacy system, I need to check if they do not already exist in our new system. Such a determination is done based on comparison of users' email addresses in each of the system.
So if a user does not exist in the new system, I need to insert such a record into the new system from the legacy one. But if user does exist, I need to update some columns in the new system with data from the legacy one.
To accomplish it I'm using a cursor with SCOPE_IDENTITY().
Also I need to log newly inserted and existing updated records in each cursor iteration. The problem is that my code does not differentiate new and existing records hence it inserts all user id in the Log table twice. One time in case the userID (#ContactID) IS NULL (not existing in the new system) and another when userID (#ContactID) IS NOT NULL - existing in the new system.
In my sample below I have 1 records that does not exists in the new system and 3 do match already. So in the IF (#CoantactID IS NULL) I meant to insert only that newly inserted into the MyDB.dbo.Contact table record and in the IF (#CoantactID IS NOT NULL) insert into the Log table only the tree records matched (existing) in the new system.
But the problem is that it inserts all four ContactIDs into the log table even in the this IF (#ContactID IS NULL) amd then again for in IF (#ContactID IS NOT NULL)
;USE [MyDB];
GO
-- exec dbo.sp_UserMigration_Users_Copy
DROP PROCEDURE IF EXISTS dbo.sp_UserMigration_Users_Copy
GO
;USE [MyDB];
GO
CREATE PROCEDURE dbo.sp_UserMigration_Users_Copy
#UserID UNIQUEIDENTIFIER = NULL
--,#ContactID INT
AS
DECLARE #Email nvarchar(100),
#ContactID INT;
-- #UserID UNIQUEIDENTIFIER,
DECLARE #SysID INT
SET #SysId = 17511; -- system contactID
---- creating log table
--CREATE TABLE #T
--(
-- UserID UNIQUEIDENTIFIER NOT NULL,
-- Email NVARCHAR(50) NOT NULL
--);
SELECT * INTO #T
FROM MyDB.dbo.User
WHERE UserID IN (
'0604C514',
'C1FDAF34',
'23BABE2D',
'EBA21D10'
);
IF NOT EXISTS (select * from MyDB.sys.tables where name = N'UserIDContactIDMigrationLog')
CREATE TABLE MyDB.dbo.UserIDContactIDMigrationLog
(
UserID UNIQUEIDENTIFIER /* CONSTRAINT [PK_UserIDContactIDMigrationLog_UserID] PRIMARY KEY */ NOT NULL, -- somehow inserts duplicate UserIDs
ContactId INT /*UNIQUE CONSTRAINT [UNIQUE_ContactId] */ NOT NULL,
CreatedDt DATETIME2 NULL,
UpdatedDt DATETIME2 NULL,
UpdatedFlag BIT /*CONSTRAINT [DF_MigratedFlag] DEFAULT(0) */ NULL
);
-- -----------------------------------------------------
-- Cursor: For all contacts to be migrated
-- -----------------------------------------------------
IF (SELECT CURSOR_STATUS('global','user_cursor')) >= -1
BEGIN
IF (SELECT CURSOR_STATUS('global','user_cursor')) > -1
BEGIN
CLOSE user_cursor
END
DEALLOCATE user_cursor
END
DECLARE user_cursor CURSOR
FOR
SELECT a.UserID, LTRIM(RTRIM(a.Email)) AS Email
FROM #T a WITH (NOLOCK)
--WHERE a.UserID = #UserID
-- begin cursor loop
OPEN user_cursor
FETCH NEXT FROM user_cursor INTO #UserID, #Email
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #ContactID = (
SELECT c.ContactId
FROM MyDB.dbo.Contact c
LEFT OUTER JOIN MyDB.dbo.Login l ON c.ContactId = l.ContactId
WHERE (LTRIM(RTRIM(c.ContactEmailTx)) = #Email
OR LTRIM(RTRIM(l.ContactLoginNameTx)) = #Email
--OR l.ContactLoginNameTx LIKE #Email+'%' -- this concept does not work in case of leading space at the ContactLoginNameTx so need to use the code below
OR SUBSTRING(LTRIM(RTRIM(l.ContactLoginNameTx)), -1, CHARINDEX('.INACTIVE.', l.ContactLoginNameTx)) = #Email)
AND (c.ContactEmailTx != '' OR c.ContactEmailTx IS NOT NULL OR l.ContactLoginNameTx != '' OR l.ContactLoginNameTx IS NOT NULL)
)
-- check if a contact is new
IF (#ContactID IS NULL)
BEGIN
-- -----------------------------------------------------
-- MyDB.dbo.Contact
-- -----------------------------------------------------
INSERT INTO [MyDB].[dbo].[Contact]
(
[ContactFirstNameTx],
[ContactLastNameTx],
[ContactEmailTx],
[ContactTitleTx],
[ContactCreateDt],
[ContactCreateByID],
[ContactCreateLoginTypeID]
)
SELECT
[FirstName],
[LastName],
[Email],
[Title],
--[AccountId],
[UserCreatedDate],
#SysId AS [ContactCreateByID], -- as as flag for import [ContactCreateLoginTypeID]
-10 AS [ContactCreateLoginTypeID]
FROM #T o WITH (NOLOCK)
WHERE UserID = #UserID
SET #ContactID = SCOPE_IDENTITY()
END
-- select * from [MyDB].[dbo].[Contact] where contactid in ( 1051364, 466440, 560466, 618576)
-- -----------------------------------------------------
--MyDB.dbo.Login
-- -----------------------------------------------------
INSERT INTO [MyDB].[dbo].[Login]
(
ContactID,
[ContactLoginNameTx], -- V.C. this is email. the field is has a unique constraint
[ContactLoginActiveIn],
[ContactLoginCreateDt],
[ContactLoginCreateByID],
[ContactLoginCreateLoginTypeID],
[ContactLoginPasswordLastChangeDt],
[ContactLoginLastLoginDt],
[ContactLoginPasswordExclusionIn] -- this is not nullable and BIT
)
SELECT
#ContactID AS ContactID,
[Email],
CASE WHEN [UserStatusID] IN (1, 3) THEN 1 ELSE 0 END AS [ContactLoginActiveIn], -- v.c. in Onvia 1 is Active, 2 - Inactive, 3 - Invited, 4 - Inactive By System, 5 - Pending Registration
[UserCreatedDate],
#SysId AS ContactLoginCreateByID, -- as as flag for Onvia import[ContactCreateLoginTypeID]
-10 AS [ContactLoginCreateLoginTypeID],
[UserLastPasswordChangedDate],
[UserLastLoginDate],
0 as ContactLoginPasswordExclusionIn -- this is not nullable and BIT
FROM #T o WITH (NOLOCK)
WHERE UserID = #UserID
AND NOT EXISTS (select * from MyDB.dbo.Login z where z.ContactID = #ContactID)
-- -----------------------------------------------------
-- Activate any inactive users
-- -----------------------------------------------------
IF EXISTS (select * from MyDB.dbo.Login where ContactLoginActiveIn = 0 and ContactID = #ContactID)
AND NOT EXISTS (select * from MyDB.dbo.Login where ContactLoginActiveIn = 1 and ContactID = #ContactID)
BEGIN
UPDATE [MyDB].[dbo].[Login]
SET ContactLoginNameTx = #Email,
ContactLoginActiveIn = (select case when [UserStatusID] IN (1, 3) then 1 else 0 end
from #T o where o.UserID = #UserID)
WHERE ContactID = #ContactID
END
-- -----------------------------------------------------
--MyDB.dbo.ContactPhoneNumber
-- -----------------------------------------------------
INSERT INTO [MyDB].[dbo].[PhoneNumber]
(
ContactID,
PhoneNumberTypeID,
ContactPhoneNumberValueTx
)
SELECT
#ContactID AS ContactID,
1 AS PhoneNumberTypeID,
ISNULL([PhoneNumber1],'') AS ContactPhoneNumberValueTx
FROM #T o WITH (NOLOCK)
WHERE o.UserID = #UserID
AND NOT EXISTS (select * from [MyDB].[dbo].[PhoneNumber] z where z.ContactID = #ContactID);
-- -----------------------------------------------------
--MyDB.dbo.ContactOrg
-- -----------------------------------------------------
INSERT INTO [MyDB].[dbo].[Org]
(
ContactID,
OrgID
)
SELECT
#ContactID AS ContactID,
b.OrgID AS OrgID
FROM #T o WITH (NOLOCK)
INNER JOIN ImportQueue.dbo.OnviaAccountIDOrgIDMigrationLog b WITH (NOLOCK) ON o.AccountId = b.OnviaAccountID
WHERE o.UserID = #UserID
AND NOT EXISTS (select * from [MyDB].[dbo].[Org] z where z.ContactID = #ContactID);
-- -----------------------------------------------------
---- MyDB.dbo.UserIDContactIDMigrationLog
---- log output for new users
-- -----------------------------------------------------
INSERT INTO MyDB.dbo.UserIDContactIDMigrationLog
(
UserID,
ContactId,
CreatedDt,
UpdatedFlag
)
VALUES
(
#UserID,
#ContactID,
GETDATE(),
0
)
-- -----------------------------------------------------
-- EXISTING MATCHED USERS
-- -----------------------------------------------------
-- check if a contact is existing
IF (#ContactID IS NOT NULL)
BEGIN
-- -----------------------------------------------------
-- MyDB.dbo.Contact
-- -----------------------------------------------------
UPDATE MyDB.dbo.Contact
SET ContactFirstNameTx =
CASE
WHEN ContactFirstNameTx NOT IN ('', NULL) THEN c.FirstName
ELSE ContactFirstNameTx
END,
ContactLastNameTx =
CASE
WHEN ContactLastNameTx IN ('', NULL) THEN c.LastName
ELSE ContactLastNameTx
END,
ContactEmailTx =
CASE
WHEN ContactEmailTx IN ('', NULL) THEN c.Email
ELSE ContactEmailTx
END,
ContactTitleTx =
CASE
WHEN ContactTitleTx IN ('', NULL) THEN c.Title
ELSE ContactTitleTx
END,
[ContactCreateDt] =
CASE
WHEN ContactCreateDt IN ('', NULL) THEN c.UserCreatedDate
ELSE ContactCreateDt
END,
[ContactModifyByID] = #SysId,
[ContactModifyLoginTypeID] = - 10
FROM #T c
WHERE UserID = #UserID
AND ContactID = #ContactID;
-- -----------------------------------------------------
---- MyDB.dbo.UserIDContactIDMigrationLog
---- log output for existing matched users
-- -----------------------------------------------------
INSERT INTO MyDB.dbo.UserIDContactIDMigrationLog
(
UserID,
ContactId,
UpdatedDt,
UpdatedFlag
)
VALUES
(
#UserID,
#ContactID,
GETDATE(),
1
);
FETCH NEXT FROM user_cursor INTO #UserID, #Email
-- end cursor loop
END
END
CLOSE user_cursor
DEALLOCATE user_cursor
GO

Instead of Cursors try the below code.
- exec dbo.sp_UserMigration_Users_Copy
DROP PROCEDURE IF EXISTS dbo.sp_UserMigration_Users_Copy
GO
;USE [MyDB];
GO
CREATE PROCEDURE dbo.sp_UserMigration_Users_Copy
#UserID UNIQUEIDENTIFIER = NULL
--,#ContactID INT
AS
DECLARE #Email nvarchar(100),
#ContactID INT;
DECLARE #SysID INT
SET #SysId = 17511;
CREATE TABLE #T
(
ID INT IDENTITY(1,1) NOT NULL,
UserID NVARCHAR(50)NOT NULL,
Email NVARCHAR(50) NOT NULL
);
INSERT INTO #T
SELECT *
FROM MyDB.dbo.User
WHERE UserID IN (
'0604C514',
'C1FDAF34',
'23BABE2D',
'EBA21D10'
);
IF NOT EXISTS (select * from MyDB.sys.tables where name = N'UserIDContactIDMigrationLog')
CREATE TABLE MyDB.dbo.UserIDContactIDMigrationLog
(
UserID UNIQUEIDENTIFIER /* CONSTRAINT [PK_UserIDContactIDMigrationLog_UserID] PRIMARY KEY */ NOT NULL, -- somehow inserts duplicate UserIDs
ContactId INT /*UNIQUE CONSTRAINT [UNIQUE_ContactId] */ NOT NULL,
CreatedDt DATETIME2 NULL,
UpdatedDt DATETIME2 NULL,
UpdatedFlag BIT /*CONSTRAINT [DF_MigratedFlag] DEFAULT(0) */ NULL
);
DECLARE #COUNT INT
DECLARE #I INT = 1
DECLARE #UserID NVARCHAR(50) ;
DECLARE #Email NVARCHAR(50)
SELECT #COUNT = COUNT(*) FROM #T
WHILE(#I <= #COUNT)
BEGIN
SELECT #UserID = UserID, #Email= LTRIM(RTRIM(Email)) FROM #T WHERE ID = #I
SELECT #ContactID = (
SELECT c.ContactId
FROM MyDB.dbo.Contact c
LEFT OUTER JOIN MyDB.dbo.Login l ON c.ContactId = l.ContactId
WHERE (LTRIM(RTRIM(c.ContactEmailTx)) = #Email
OR LTRIM(RTRIM(l.ContactLoginNameTx)) = #Email
--OR l.ContactLoginNameTx LIKE #Email+'%' -- this concept does not work in case of leading space at the ContactLoginNameTx so need to use the code below
OR SUBSTRING(LTRIM(RTRIM(l.ContactLoginNameTx)), -1, CHARINDEX('.INACTIVE.', l.ContactLoginNameTx)) = #Email)
AND (c.ContactEmailTx != '' OR c.ContactEmailTx IS NOT NULL OR l.ContactLoginNameTx != '' OR l.ContactLoginNameTx IS NOT NULL)
)
-- check if a contact is new
IF (#ContactID IS NULL)
BEGIN
-- -----------------------------------------------------
-- MyDB.dbo.Contact
-- -----------------------------------------------------
INSERT INTO [MyDB].[dbo].[Contact]
(
[ContactFirstNameTx],
[ContactLastNameTx],
[ContactEmailTx],
[ContactTitleTx],
[ContactCreateDt],
[ContactCreateByID],
[ContactCreateLoginTypeID]
)
SELECT
[FirstName],
[LastName],
[Email],
[Title],
--[AccountId],
[UserCreatedDate],
#SysId AS [ContactCreateByID], -- as as flag for Onvia import[ContactCreateLoginTypeID]
-10 AS [ContactCreateLoginTypeID]
FROM #T o WITH (NOLOCK)
WHERE UserID = #UserID
SET #ContactID = SCOPE_IDENTITY()
END
-- select * from [MyDB].[dbo].[Contact] where contactid in ( 1051364, 466440, 560466, 618576)
-- -----------------------------------------------------
--MyDB.dbo.Login
-- -----------------------------------------------------
INSERT INTO [MyDB].[dbo].[Login]
(
ContactID,
[ContactLoginNameTx], -- V.C. this is email. the field is has a unique constraint
[ContactLoginActiveIn],
[ContactLoginCreateDt],
[ContactLoginCreateByID],
[ContactLoginCreateLoginTypeID],
[ContactLoginPasswordLastChangeDt],
[ContactLoginLastLoginDt],
[ContactLoginPasswordExclusionIn] -- this is not nullable and BIT
)
SELECT
#ContactID AS ContactID,
[Email],
CASE WHEN [UserStatusID] IN (1, 3) THEN 1 ELSE 0 END AS [ContactLoginActiveIn], -- v.c. in Onvia 1 is Active, 2 - Inactive, 3 - Invited, 4 - Inactive By System, 5 - Pending Registration
[UserCreatedDate],
#SysId AS ContactLoginCreateByID, -- as as flag for Onvia import[ContactCreateLoginTypeID]
-10 AS [ContactLoginCreateLoginTypeID],
[UserLastPasswordChangedDate],
[UserLastLoginDate],
0 as ContactLoginPasswordExclusionIn -- this is not nullable and BIT
FROM #T o WITH (NOLOCK)
WHERE UserID = #UserID
AND NOT EXISTS (select * from MyDB.dbo.Login z where z.ContactID = #ContactID)
-- -----------------------------------------------------
-- Activate any inactive users
-- -----------------------------------------------------
IF EXISTS (select * from MyDB.dbo.Login where ContactLoginActiveIn = 0 and ContactID = #ContactID)
AND NOT EXISTS (select * from MyDB.dbo.Login where ContactLoginActiveIn = 1 and ContactID = #ContactID)
BEGIN
UPDATE [MyDB].[dbo].[Login]
SET ContactLoginNameTx = #Email,
ContactLoginActiveIn = (select case when [UserStatusID] IN (1, 3) then 1 else 0 end
from #T o where o.UserID = #UserID)
WHERE ContactID = #ContactID
END
-- -----------------------------------------------------
--MyDB.dbo.ContactPhoneNumber
-- -----------------------------------------------------
INSERT INTO [MyDB].[dbo].[PhoneNumber]
(
ContactID,
PhoneNumberTypeID,
ContactPhoneNumberValueTx
)
SELECT
#ContactID AS ContactID,
1 AS PhoneNumberTypeID,
ISNULL([PhoneNumber1],'') AS ContactPhoneNumberValueTx
FROM #T o WITH (NOLOCK)
WHERE o.UserID = #UserID
AND NOT EXISTS (select * from [MyDB].[dbo].[PhoneNumber] z where z.ContactID = #ContactID);
-- -----------------------------------------------------
--MyDB.dbo.ContactOrg
-- -----------------------------------------------------
INSERT INTO [MyDB].[dbo].[Org]
(
ContactID,
OrgID
)
SELECT
#ContactID AS ContactID,
b.OrgID AS OrgID
FROM #T o WITH (NOLOCK)
INNER JOIN ImportQueue.dbo.OnviaAccountIDOrgIDMigrationLog b WITH (NOLOCK) ON o.AccountId = b.OnviaAccountID
WHERE o.UserID = #UserID
AND NOT EXISTS (select * from [MyDB].[dbo].[Org] z where z.ContactID = #ContactID);
-- -----------------------------------------------------
---- MyDB.dbo.UserIDContactIDMigrationLog
---- log output for new users
-- -----------------------------------------------------
INSERT INTO MyDB.dbo.UserIDContactIDMigrationLog
(
UserID,
ContactId,
CreatedDt,
UpdatedFlag
)
VALUES
(
#UserID,
#ContactID,
GETDATE(),
0
)
-- -----------------------------------------------------
-- EXISTING MATCHED USERS
-- -----------------------------------------------------
-- check if a contact is existing
IF (#ContactID IS NOT NULL)
BEGIN
-- -----------------------------------------------------
-- MyDB.dbo.Contact
-- -----------------------------------------------------
UPDATE MyDB.dbo.Contact
SET ContactFirstNameTx =
CASE
WHEN ContactFirstNameTx NOT IN ('', NULL) THEN c.FirstName
ELSE ContactFirstNameTx
END,
ContactLastNameTx =
CASE
WHEN ContactLastNameTx IN ('', NULL) THEN c.LastName
ELSE ContactLastNameTx
END,
ContactEmailTx =
CASE
WHEN ContactEmailTx IN ('', NULL) THEN c.Email
ELSE ContactEmailTx
END,
ContactTitleTx =
CASE
WHEN ContactTitleTx IN ('', NULL) THEN c.Title
ELSE ContactTitleTx
END,
[ContactCreateDt] =
CASE
WHEN ContactCreateDt IN ('', NULL) THEN c.UserCreatedDate
ELSE ContactCreateDt
END,
[ContactModifyByID] = #SysId,
[ContactModifyLoginTypeID] = - 10
FROM #T c
WHERE UserID = #UserID
AND ContactID = #ContactID;
-- -----------------------------------------------------
---- MyDB.dbo.UserIDContactIDMigrationLog
---- log output for existing matched users
-- -----------------------------------------------------
INSERT INTO MyDB.dbo.UserIDContactIDMigrationLog
(
UserID,
ContactId,
UpdatedDt,
UpdatedFlag
)
VALUES
(
#UserID,
#ContactID,
GETDATE(),
1
);
END
SET #I = #I+1;
END

Related

Update but only what is different (what've changed)

I've tried something like this:
UPDATE Person
SET Name = #Name, Phone = #Phone, Email = #Email, Status = #Status
WHERE Id = #Id AND Name != #Name AND Phone != #Phone AND Email != #Email AND Status != #Status;
But isn't working.
If Id is your primary key then you probably want:
UPDATE Person
SET Name = #Name, Phone = #Phone, Email = #Email, Status = #Status
WHERE Id = #Id AND
(Name != #Name OR Phone != #Phone OR Email != #Email OR Status != #Status);
But there generally isn't any harm in updating columns to the same value unless you have some sort of trigger that you don't want to run. Even with the code above, if only one of the column values changes, you're going to "update" the other three to their same value.
Here is another way to prevent unnecessary updates.
It is using set based operator INTERSECT, and it won't trip on NULL values in comparison:
AND (Name != #Name OR Phone != #Phone OR Email != #Email)
The solution is using UpdatedOn DATETIMEOFFSET(3) column to track the updated DateTime.
SQL
-- DDL and sample data population, start
DECLARE #person TABLE (
ID INT PRIMARY KEY,
Name VARCHAR(20),
Phone VARCHAR(15),
Email VARCHAR(128),
UpdatedOn DATETIMEOFFSET(3)
);
INSERT #person (ID, Name, Phone, Email, UpdatedOn) VALUES
(1, 'Peter', '1-305-803-1234', 'peter#gmail.com', NULL),
(2, 'Paul', NULL, 'paul#gmail.com', NULL);
-- DDL and sample data population, end
-- before
SELECT * FROM #person;
DECLARE #ID INT = 1
, #Name VARCHAR(20) = 'Peter' -- try 'PeterZ'
, #Phone VARCHAR(15) = '1-305-803-1234'
, #Email VARCHAR(128) = 'peter#gmail.com';
;WITH rs AS
(
SELECT #ID AS ID
, #Name AS NAme
, #Phone AS Phone
, #Email AS Email
)
UPDATE T
SET Name = S.Name, Phone = S.Phone, Email = S.Email
, T.UpdatedOn = SYSDATETIMEOFFSET()
FROM #person AS T -- target
INNER JOIN rs AS S -- source
ON T.ID = S.ID
WHERE NOT EXISTS (SELECT S.*
INTERSECT SELECT T.ID, T.Name, T.Phone, T.Email);
-- test
SELECT * FROM #person;

Stored Procedure works when executed manually but not via trigger

So I created a Stored Procedure in SQL that does 4 things
Create Order into Customer Order table
Create Order lines on Customer Order Line table
Update Auto number column by taking current value and adding 1
Update Purchase Order with Customer Order ID
Sends email to notify that an order has been created.
It works awesome if I execute each part separately or execute the whole Stored Procedure manually by sending command EXEC dbo.POIMPORT_UK #PO_NUM = 123456
But when I create a PO and it triggers the inserts, query 1,3,4,5 work but 2 doesn't for some reason. I can only get it to go if I fire off that procedure manually. Any thoughts?
USE [RIPTST]
GO
/****** Object: StoredProcedure [dbo].[POIMPORT_UK] Script Date: 8/20/2018 11:21:23 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER proc [dbo].[POIMPORT_UK]
#PO_NUM NVARCHAR(15)
AS
SET NOCOUNT ON
--For synchronization machine id placement
--Query 1
INSERT INTO CUSTOMER_ORDER(ID, CUSTOMER_ID, SITE_ID, SELL_RATE, BUY_RATE,
TERMS_NET_TYPE, TERMS_DISC_TYPE, FREIGHT_TERMS, BACK_ORDER, STATUS,
POSTING_CANDIDATE, MARKED_FOR_PURGE, CURRENCY_ID, CUSTOMER_PO_REF,
WAREHOUSE_ID, DESIRED_SHIP_DATE) SELECT (SELECT Next_number FROM
NEXT_NUMBER_GEN where rowid =
'72'),'ABECO','101RIPUS','1.0','1.0','A','A','P','N','F','N','N','(USD)
$',ID,'CROM',Getdate() FROM PURCHASE_ORDER WHERE ID = #PO_NUM
--Query 2
INSERT INTO CUST_ORDER_LINE
(CUST_ORDER_ID,LINE_NO,PART_ID,LINE_STATUS,ORDER_QTY,USER_ORDER_QTY,
SELLING_UM,UNIT_PRICE,SITE_ID,MISC_REFERENCE,PRODUCT_CODE,COMMODITY_CODE,
DRAWING_ID,DRAWING_REV_NO,MAT_GL_ACCT_ID,LAB_GL_ACCT_ID,BUR_GL_ACCT_ID,
SER_GL_ACCT_ID,GL_REVENUE_ACCT_ID,TOTAL_AMT_ORDERED,ENTERED_BY,WAREHOUSE_ID)
SELECT(select id from customer_order where CUSTOMER_PO_REF = #PO_NUM),
LINE_NO,PURC_ORDER_LINE.PART_ID,'A',ORDER_QTY,USER_ORDER_QTY,
PURCHASE_UM,PURC_ORDER_LINE.UNIT_PRICE,'101RIPUS',PART.DESCRIPTION,
PART.PRODUCT_CODE,PART.COMMODITY_CODE,PART.DRAWING_ID,PART.DRAWING_REV_NO,
PART_SITE.MAT_GL_ACCT_ID,PART_SITE.LAB_GL_ACCT_ID,PART_SITE.BUR_GL_ACCT_ID,
PART_SITE.SER_GL_ACCT_ID,
(SELECT REV_GL_ACCT_ID FROM PRODUCT WHERE CODE = PART.PRODUCT_CODE),
USER_ORDER_QTY*PURC_ORDER_LINE.UNIT_PRICE,'SYSADM',
PART_SITE.PRIMARY_WHS_ID From PURC_ORDER_LINE
Inner Join PART On PART.ID = PURC_ORDER_LINE.PART_ID
Inner Join PART_SITE On PART_SITE.PART_ID = PART.ID
WHERE PURC_ORDER_ID = #PO_NUM AND PART_SITE.sITE_ID = '101RIPUS'
--Query 3
Update NEXT_NUMBER_GEN Set Next_number = Next_number + 1 where rowid = '72'
--Query 4
Update PURCHASE_ORDER Set SALES_ORDER_ID = (select id from customer_order where CUSTOMER_PO_REF = #PO_NUM) where id = #PO_NUM
--Query 5
Declare #PO NVARCHAR(15) = #PO_NUM
Declare #CO NVARCHAR(15) = (select id from customer_order where CUSTOMER_PO_REF = #PO)
Declare #MYBODY VARCHAR(MAX) = 'Hello Test CSR,
A new internal order has been created for Ripley UK.
Test US Customer Order Number: ' + #CO +
'
Test UK Purchase Order Number: ' + #PO
EXEC msdb.dbo.sp_send_dbmail #profile_name='office365',
#recipients='test#test.com',
#subject='Test UK - Internal Order',
#body= #MYBODY
SET NOCOUNT OFF
Insert Trigger
USE [RIPTST]
GO
/****** Object: Trigger [dbo].[INSERT_PURCHASE_ORDER] Script Date: 8/20/2018 1:42:15 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER trigger [dbo].[INSERT_PURCHASE_ORDER] ON [dbo].[PURCHASE_ORDER] FOR INSERT AS
DECLARE
#nRcd INT,
#ID NVARCHAR(15),
#VENDOR_ID NVARCHAR(15),
#SITE_ID NVARCHAR(15),
#STATUS NCHAR,
#TOTAL_AMT_ORDERED DEC(15,2),
#TOTAL_AMT_RECVD DEC(15,2),
#SELL_RATE DEC(17,8),
#nCurrDigits INT,
#sEntityID NVARCHAR(15),
#ORDER_DATE DATETIME
SELECT #nRcd = 0
SET NOCOUNT ON
DECLARE PURCHASE_ORDER_INS CURSOR LOCAL FOR SELECT ID, VENDOR_ID, STATUS, TOTAL_AMT_ORDERED, TOTAL_AMT_RECVD, SELL_RATE, ORDER_DATE, SITE_ID FROM INSERTED
OPEN PURCHASE_ORDER_INS
FETCH PURCHASE_ORDER_INS INTO #ID, #VENDOR_ID, #STATUS, #TOTAL_AMT_ORDERED, #TOTAL_AMT_RECVD, #SELL_RATE, #ORDER_DATE, #SITE_ID
WHILE (#nRcd = 0 and ##FETCH_STATUS <> -1)
BEGIN
EXEC EXE_DETECT_EVENT 'P', #ID, NULL, NULL, 'I'
If #TOTAL_AMT_ORDERED + #TOTAL_AMT_RECVD != 0
SELECT #nRcd = 30881
If #nRcd = 0 And (#STATUS = 'R' Or #STATUS = 'F') And #TOTAL_AMT_ORDERED > #TOTAL_AMT_RECVD
BEGIN
select #nCurrDigits = c.i_curr_digits, #sEntityID = s.entity_id from CURRENCY c, ACCOUNTING_ENTITY ae, SITE s where s.id = #SITE_ID and ae.id = s.entity_id and ae.functional_currency_id = c.id
IF NOT EXISTS ( SELECT VENDOR_ID FROM VENDOR_ENTITY WHERE vendor_id = #VENDOR_ID and entity_id = #sEntityID )
INSERT INTO VENDOR_ENTITY ( VENDOR_ID, ENTITY_ID ) VALUES ( #VENDOR_ID, #sEntityID )
update VENDOR_ENTITY set
total_open_orders = total_open_orders + ROUND((#TOTAL_AMT_ORDERED - #TOTAL_AMT_RECVD) * #SELL_RATE, #nCurrDigits),
open_order_count = open_order_count + 1
where vendor_id = #VENDOR_ID and entity_id = #sEntityID
END
UPDATE VENDOR set last_order_date = #ORDER_DATE where id = #VENDOR_ID
FETCH PURCHASE_ORDER_INS INTO #ID, #VENDOR_ID, #STATUS, #TOTAL_AMT_ORDERED, #TOTAL_AMT_RECVD, #SELL_RATE, #ORDER_DATE, #SITE_ID
END
BEGIN
IF #VENDOR_ID = 'RIP01' AND #SITE_ID = '111RIPUK'
EXEC dbo.POIMPORT_UK #PO_NUM = #ID
END
DEALLOCATE PURCHASE_ORDER_INS
IF (#nRcd <> 0) RAISERROR('VMFG-%d error in trigger INSERT_PURCHASE_ORDER', 16, -1, #nRcd)
IF (#nRcd <> 0 Or ##ERROR <> 0) ROLLBACK TRANSACTION

Trigger did not run?

I have a trigger "after insert/update/delete/". It is supposed to count Balance on Account table based on transactions in Transaction table. It is on Transaction table. I am getting Balance discrepancies rarely, so have decided to add some logging into it. It dumps inserted+deleted tables (they are combined into a table var) and tsql statement which fired it. Judging from my log, it looks like the trigger did not fire for some inserts into Transaction table. Can this happen ? Are there any TSQL statement which change table data without firing trigger (except truncate table etc)?
Here is the trigger :
CREATE TRIGGER [dbo].[trg_AccountBalance]
ON [dbo].[tbl_GLTransaction]
AFTER INSERT, UPDATE, DELETE
AS
set nocount on
begin try
declare #OldOptions int = ##OPTIONS
set xact_abort off
declare #IsDebug bit = 1
declare #CurrentDateTime datetime = getutcdate()
declare #TriggerMessage varchar(max), #TriggerId int
if #IsDebug = 1
begin
select #TriggerId = isnull(max(TriggerId), 0) + 1
from uManageDBLogs.dbo.tbl_TriggerLog
declare #dbcc_INPUTBUFFER table(EventType nvarchar(30), Parameters Int, EventInfo nvarchar(4000) )
declare #my_spid varchar(20) = CAST(##SPID as varchar(20))
insert #dbcc_INPUTBUFFER
exec('DBCC INPUTBUFFER ('+#my_spid+')')
select #TriggerMessage = replace(EventInfo, '''', '''''') from #dbcc_INPUTBUFFER
insert into uManageDBLogs.dbo.tbl_TriggerLog (TriggerId, "Message", CreateDate)
values (#TriggerId, #TriggerMessage, #CurrentDateTime)
end
declare #Oper int
select #Oper = 0
-- determine type of sql statement
if exists (select * from inserted) select #Oper = #Oper + 1
if exists (select * from deleted) select #Oper = #Oper + 2
if #IsDebug = 1
begin
select #TriggerMessage = '#Oper = ' + convert(varchar, #Oper)
insert into uManageDBLogs.dbo.tbl_TriggerLog (TriggerId, "Message", CreateDate)
values (#TriggerId, #TriggerMessage, #CurrentDateTime)
end
if #Oper = 0 return -- No data changed
declare #TomorrowDate date = dateadd(day, 1, convert(date, getdate()))
declare #CurrentDate date = convert(date, getdate())
-- transactions from both inserted and deleted tables
declare #tbl_Trans table (FirmId int, GLAccountId int,
AmountDebit money, AmountCredit money, "Status" char(1), TableType char(1))
declare #tbl_AccountCounters table (FirmId int, GLAccountId int, Balance money)
declare #IsChange bit = null
insert into #tbl_Trans (FirmId, GLAccountId, AmountDebit, AmountCredit, "Status", TableType)
select FirmId, GLAccountId, AmountDebit, AmountCredit, "Status", 'I'
from inserted
union
select FirmId, GLAccountId, AmountDebit, AmountCredit, "Status", 'D'
from deleted
if #IsDebug = 1
begin
select #TriggerMessage = (select * from #tbl_Trans for xml path ('tbl_Trans'))
insert into uManageDBLogs.dbo.tbl_TriggerLog (TriggerId, "Message", CreateDate)
values (#TriggerId, #TriggerMessage, #CurrentDateTime)
end
insert into #tbl_AccountCounters (FirmId, GLAccountId, Balance)
select FirmId, GLAccountId, 0
from #tbl_Trans
group by FirmId, GLAccountId
if #Oper = 1 or #Oper = 2 -- insert/delete
begin
update #tbl_AccountCounters
set Balance = cnt.TransSum
from #tbl_AccountCounters as ac join
(
select trans.FirmId, trans.GLAccountId,
isnull(sum((trans.AmountDebit - trans.AmountCredit) * iif(trans.TableType = 'I', 1, -1)), 0) as TransSum
from #tbl_Trans as trans
where trans.Status = 'A'
group by trans.FirmId, trans.GLAccountId
) as cnt on ac.FirmId = cnt.FirmId and ac.GLAccountId = cnt.GLAccountId
select #IsChange = 1
end
else
begin
if update(AmountDebit) or update(AmountCredit) or update(Status) or update(GLAccountId)
begin
update #tbl_AccountCounters
set Balance = cnt.TransBalance
from #tbl_AccountCounters as ac join
(select trans.FirmId, trans.GLAccountId, isnull(sum(trans.AmountDebit - trans.AmountCredit), 0) as TransBalance
from dbo.tbl_GLTransaction as trans
where trans."Status" = 'A' and exists (select 1 from #tbl_AccountCounters as ac
where ac.GLAccountId = trans.GLAccountId and ac.FirmId = trans.FirmId)
group by trans.FirmId, trans.GLAccountId) as cnt on
ac.FirmId = cnt.FirmId and ac.GLAccountId = cnt.GLAccountId
select #IsChange = 0
end
end
if #IsDebug = 1
begin
select #TriggerMessage = '#IsChange = ' + isnull(convert(varchar, #IsChange), 'null')
insert into uManageDBLogs.dbo.tbl_TriggerLog (TriggerId, "Message", CreateDate)
values (#TriggerId, #TriggerMessage, #CurrentDateTime)
select #TriggerMessage = (select * from #tbl_AccountCounters for xml path ('tbl_AccountCounters'))
insert into uManageDBLogs.dbo.tbl_TriggerLog (TriggerId, "Message", CreateDate)
values (#TriggerId, #TriggerMessage, #CurrentDateTime)
end
if #IsChange is not null
begin
update tbl_GLAccount
set tbl_GLAccount.Balance = iif(#IsChange = 1, cnt.Balance + acc.Balance, cnt.Balance),
tbl_GLAccount.LastUpdate = getutcdate(),
tbl_GLAccount.LastUpdatedBy = 1
from #tbl_AccountCounters as cnt join dbo.tbl_GLAccount as acc on
cnt.FirmId = acc.FirmId and cnt.GLAccountId = acc.GLAccountId
end
if (16384 & #OldOptions) = 16384 set xact_abort on
end try
begin catch
declare #ErrorLine varchar(max)
select #ErrorLine = uManageDb.dbo.udf_GetErrorInfo()
insert into uManageDb.dbo.tbl_TriggerError ("Name", "Message", CreateDate)
values ('AccountingDB..trg_AccountBalance', #ErrorLine, GETUTCDATE())
end catch
I think I've found it. I have this line:
select .. from inserted
union
select .. from deleted
and they inserted 5 trans for $300 and 4 trans $100. I've got 2 records (300 and 100) in my #tbl_Trans (it was in the log). That's probably was the bug. So log hellps and trigger run as it had to.
I'll replace union with union all.

SQL Server create Triggers on INSERT and SELECT OUTCOME

I try to create a Trigger that will insert a Unique ID in a table and then get the created ID and use it on the table that "Triggered the trigger"
So I insert some data in my table and then I want the trigger to insert some data in another table (master ID table), it then needs to get the just created ID (auto increment INT) and add this ID to the table ID that the trigger fired on.
I tried quite some different options but it just does not insert the ID and it will give me an error.
Hope you can help me out.
CREATE TRIGGER [dbo].[TROidMemoDiaryHeader]
ON [dbo].[InvoiceDiaryHeader]
FOR INSERT
AS
DECLARE #TEMP uniqueidentifier, #Lock INT, #Object INT, #User V ARCHAR(MAX), #Delay NCHAR(12), #ID INT
SET #TEMP = NEWID()
SET #Lock = '0' --staat geen wijziging meer toe
SET #Object = '5' -- aanduiding bron
SET #User = CURRENT_USER
--SET #Delay = '00:00:00:125' -- Wacht voor een kwart seconde voor verder te gaan
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
--SET NOCOUNT ON;
INSERT INTO [dbo].[AbstractMBOBase] ([Uid], [OptimisticLockField], [ObjectType], [User])
SELECT #TEMP, #Lock, #Object, #User
IF EXISTS (SELECT [Uid] FROM [dbo].AbstractMBOBase WHERE [Uid] = #TEMP)
BEGIN
INSERT INTO [InvoiceDiaryHeader](Oid)
SELECT Oid FROM inserted
/*
SELECT * INTO #tmpOID FROM AbstractMBOBase WHERE [Uid] = #TEMP
UPDATE [dbo].InvoiceDiaryHeader SET Oid = (SELECT a.[Oid] FROM [dbo].[AbstractMBOBase] AS a
WHERE [Uid] = #TEMP)
--CREATE TABLE [dbo].#TempOID()
--SELECT * INTO #TempOID FROM INSERTED
*/
/*
--UPDATE [MemoDiaryHeader]
SET #ID = (SELECT a.[Oid] FROM [dbo].[AbstractMBOBase] AS a
WHERE [Uid] = #TEMP)
INSERT INTO [MemoDiaryHeader]([Oid])
SELECT #ID
--FROM inserted
--select [Oid]=i.[Oid] from inserted i
*/
END
--END
END
GO

How to DISPLAY horizontal data into vertical columns in SQL?

I have tables Employees, Documents, Transactions, from_to table.
Employee can send a document to other employee and send a copy of the document to other employee.
data will be displayed in transaction table as follow:
TransId - Document Id - EmployeeId - from_toId
1 1 5 1 (From)
2 1 6 2 (To)
3 1 10 2 (CC)
Now; I want the data above to be displayed as follow:
DocId From To CC
1 Jo(5) Fo(6) Do(10)
I know that we need something to do with "Pivot Table". BUT I DON'T KNOW HOW.
Waiting for your feedback.
I have a solution with a Cursor which is dynamic:
CREATE TABLE #PRERESULT(
[TransId] int,
DocumentID int ,
EmployeeId int ,
from_toId [nvarchar](10)
)
INSERT INTO #PRERESULT ([TransId],DocumentID,EmployeeId,from_toId)
VALUES
(1,1,5,'1 (From)'),
(2,1,6,'2 (To)'),
(3,1,10,'2 (CC)')
CREATE TABLE #RESULT (
DocID int,
[From] nvarchar(15),
[To] nvarchar(15),
CC nvarchar(15))
INSERT INTO #RESULT (DocID)
SELECT DocumentID
FROM #PRERESULT
GROUP BY DocumentID
DECLARE #Documentid int,#Employee int, #Alias nvarchar(10),#SQL nvarchar(250)
DECLARE C_FromTo CURSOR
FOR
SELECT DocumentID,EmployeeID
FROM #PRERESULT
OPEN C_FromTo
FETCH NEXT FROM C_FromTo INTO #Documentid, #Employee
While (##Fetch_status = 0)
BEGIN
SET #Alias = (SELECT SUBSTRING(from_toId,PATINDEX('%(%',from_toId)+1,(LEN(from_toId)-PATINDEX('%(%',from_toId)-1)) FROM #PRERESULT WHERE #Employee = EmployeeId)
SET #SQL = 'UPDATE #RESULT
SET ['+#Alias+'] = '+Convert(nvarchar(50),#Employee)+'
WHERE '+Convert(nvarchar(50),#Documentid)+' = DocID'
EXEC (#SQL)
FETCH NEXT FROM C_FromTo INTO #Documentid, #Employee
END
CLOSE C_FromTo
DEALLOCATE C_FromTO
SELECT * FROM #RESULT
DROP TABLE #PRERESULT
DROP TABLE #RESULT
Gives you this:
DocID |From |To |CC
1 |5 |6 |10
Hope this will help you
Have a nice day & Greets from Switzerland
Etienne
I think this reflects your table, although I am using names instead of IDs for the last two columns:
CREATE TABLE [dbo].[Transaction](
[TransId] [int] NOT NULL,
[DocId] [int] NOT NULL,
[EmpId] [nvarchar](10) NOT NULL,
[FromToId] [nchar](10) NOT NULL
) ON [PRIMARY]
INSERT INTO [Transaction] ([TransId],[DocId],[EmpId],[FromToId])VALUES(1,1,'Jo','From')
INSERT INTO [Transaction] ([TransId],[DocId],[EmpId],[FromToId])VALUES(2,1,'Fo','To')
INSERT INTO [Transaction] ([TransId],[DocId],[EmpId],[FromToId])VALUES(3,1,'Do','CC')
INSERT INTO [Transaction] ([TransId],[DocId],[EmpId],[FromToId])VALUES(4,2,'Jo','From')
INSERT INTO [Transaction] ([TransId],[DocId],[EmpId],[FromToId])VALUES(5,2,'Bo','To')
INSERT INTO [Transaction] ([TransId],[DocId],[EmpId],[FromToId])VALUES(6,2,'Zo','CC')
INSERT INTO [Transaction] ([TransId],[DocId],[EmpId],[FromToId])VALUES(7,3,'Bo','From')
INSERT INTO [Transaction] ([TransId],[DocId],[EmpId],[FromToId])VALUES(8,3,'Go','To')
Then this query will give you the results you requested:
SELECT DISTINCT
t.DocId
,x.[From]
,y.[To]
,z.Cc
FROM [Transaction] t
LEFT JOIN
(
SELECT DocId
,CASE WHEN FromToId = 'From' THEN EmpId END AS [From]
FROM [Transaction]
WHERE CASE WHEN FromToId = 'From' THEN EmpId END IS NOT NULL
) x ON t.DocId = x.DocId
LEFT JOIN
(
SELECT DocId
,CASE WHEN FromToId = 'To' THEN EmpId END AS [To]
FROM [Transaction]
WHERE CASE WHEN FromToId = 'To' THEN EmpId END IS NOT NULL
) y ON t.DocId = y.DocId
LEFT JOIN
(
SELECT DocId
,CASE WHEN FromToId = 'CC' THEN EmpId END AS [Cc]
FROM [Transaction]
WHERE CASE WHEN FromToId = 'Cc' THEN EmpId END IS NOT NULL
) z ON t.DocId = z.DocId
DocId From To Cc
1 Jo Fo Do
2 Jo Bo Zo
3 Bo Go NULL

Resources