【发布时间】:2018-05-30 18:54:51
【问题描述】:
我使用 SQL Server 2016。我需要将用户从旧系统转移到新系统。但是在从遗留系统中引入所有用户之前,我需要检查他们是否已经存在于我们的新系统中。这种确定是基于每个系统中用户的电子邮件地址的比较来完成的。
所以如果新系统中不存在用户,我需要将这样的记录从旧系统插入到新系统中。但如果用户确实存在,我需要使用旧系统中的数据更新新系统中的某些列。
为了完成它,我使用带有SCOPE_IDENTITY() 的光标。
我还需要在每次游标迭代中记录新插入的和现有的更新记录。问题是我的代码没有区分新记录和现有记录,因此它将所有用户 ID 插入日志表中两次。一次是用户 ID (@ContactID) 为 NULL(新系统中不存在),另一次是用户 ID (@ContactID) 不是 NULL - 存在于新系统中。
在下面的示例中,我有 1 条记录在新系统中不存在,而 3 条记录已经匹配。所以在 IF (@CoantactID IS NULL) 我的意思是只插入新插入到 MyDB.dbo.Contact 表记录中,在 IF (@CoantactID IS NOT NULL) 中插入到 Log 表中只有匹配的树记录(现有) 在新系统中。
但问题是,即使在 this IF (@ContactID IS NULL) 中,它也会将所有四个 ContactID 都插入到日志表中,然后在 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
【问题讨论】:
-
请发布示例数据以及您当前的代码和您获得的结果 - 也称为minimal reproducible example。您可能也不需要在此处使用光标 - 我希望您还没有接受这种方法?
-
在我的示例中,我有 1 条记录在新系统中不存在,3 条记录匹配。所以在 IF (@CoantactID IS NULL) 我的意思是只插入新插入到 MyDB.dbo.Contact 表记录中,在 IF (@CoantactID IS NOT NULL) 中插入到 Log 表中只有匹配的树记录(现有) 在新系统中。
-
但问题是,即使在 this IF (@ContactID IS NULL) 中,它也会将所有四个 ContactID 插入到日志表中,然后在 IF (@ContactID IS NOT NULL) 中再次插入。
-
旁注:您应该不为您的存储过程使用
sp_前缀。微软有reserved that prefix for its own use (see Naming Stored Procedures),你确实会在未来某个时候冒着名称冲突的风险。 It's also bad for your stored procedure performance。最好只是简单地避免sp_并使用其他东西作为前缀 - 或者根本不使用前缀! -
您可能也想看看这里,因为您对临时表使用了 nolock。它实际上并没有做更多的事情,只是让您输入更多内容。 blogs.sentryone.com/aaronbertrand/nolock-temp-tables
标签: loops variables scope cursor sql-server-2016