Merge Replication Unique Key Constraint - sql-server

I have SQL Server Merge Replication setup on two servers and I am getting a key constraint error when the synchronization runs. The only way I can resolve this issue is to delete the record on one of the servers and then run synchronization.
Question: Is there a way to configure replication or a resolver so that the publisher record wins and the subscriber record is automatically removed when it encounters a unique or primary key violation?
Sample Table:
CREATE TABLE [dbo].[tblPeople](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[col1] [int] NULL,
[col2] [int] NULL,
[col3] [int] NULL,
[name] [varchar](52) NULL
CONSTRAINT [UK_keys] UNIQUE NONCLUSTERED
(
[col1] ASC,
[col2] ASC,
[col3] ASC
)
Insert on Server 1
INSERT into tblPeople (col1, col2, col3, name) values (1, 1, 1, 'Server 1 Insert')
Insert on Server 2
INSERT into tblPeople (col1, col2, col3, name) values (1, 1, 1, 'Server 2 Insert')
Trigger synchronization, which results in this conflict error and both servers having their own version of this record.
A row insert at 'SERVER1.TestDb' could not be propagated to 'SERVER2.TestDb'. This failure can be caused by a constraint violation. Violation of UNIQUE KEY constraint 'UK_keys'. Cannot insert duplicate key in object 'dbo.tblPeople'. The duplicate key value is (1, 1, 1).
Everything I read about this suggests adding a unique guid or using identity columns, which isn't a solution to this problem. The identity ranges work great and I can even create my own rowguid, but that still doesn't solve the constraint violation where I end up needing to manually delete the records.
This person asked a similar question, but I need the unique key on top of the guid and identity.
Automatically resolve primary key merge conflict

This was resolved by setting compensate_for_errors to true on the merge article. By default SQL Server doesn't trigger a resolver when a constraint error occurs. You cannot change this setting through the user interface and must use t-sql to update it.
exec sp_changemergearticle #publication = 'PublicationName'
, #article = 'TableName'
, #property = 'compensate_for_errors'
, #value = N'true'
, #force_invalidate_snapshot = true
, #force_reinit_subscription = true
https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.replication.mergearticle.compensateforerrors.aspx

Related

How to insert values into two SQL Server tables updating primary key and foreign key simultaneously, using a procedure?

I have something like this:
CREATE TABLE [dbo].[table1]
(
[id1] [int] IDENTITY(1,1) NOT NULL,
[data] [varchar](255) NOT NULL,
CONSTRAINT [PK_table1] PRIMARY KEY(id1)
)
CREATE TABLE [dbo].[table2]
(
[id2] [int] IDENTITY(1,1) NOT NULL,
[id1] [int] ,
CONSTRAINT [PK_table2] PRIMARY KEY (id2)
CONSTRAINT [FK_table2] FOREIGN KEY(id1) REFERENCES Table1
)
I want to add values to both the tables using a procedure. I'm not adding any key values just data values.
If I use INSERT INTO to add data into Table 1, its primary key will be autoincremented. I will also be incrementing the Table 2 in the same procedure.
I want that the autoincremented primary key of Table 1 should automatically be updated as foreign key in Table 2 when I run that procedure.
You need to do something like this:
CREATE PROCEDURE dbo.InsertData (#data VARCHAR(255))
AS
BEGIN
-- Insert row into table1
INSERT INTO dbo.Table1 (data) VALUES (#data);
-- Capture the newly generated "Id1" value
DECLARE #NewId1 INT;
SELECT #NewId1 = SCOPE_IDENTITY();
-- Insert data into table2
INSERT INTO dbo.table2 (Id1) VALUES (#NewId1);
END
I don't know if I understand what do you want to do but I think you can do something like this:
INSERT INTO table1 (data) VALUES 'mydata'
DECLARE #LastKey INT
SET #LastKey = SCOPE_IDENTITY() -- FOR SQL SERVER, OR LAST_INSERT_ID() FOR MYSQL
INSERT INTO table2 (data) VALUES #LastKey

SCOPE_IDENTITY() return 1

I use this code in SQL Server 2012:
INSERT INTO [dbo].[test] ([t])
VALUES ('tyy');
SELECT [Id]
FROM [dbo].[test]
WHERE ([Id] = SCOPE_IDENTITY())
It returns the last inserted id and it worked well.
But the same code in vb.net 2017 and .net Framework 4.7.2 is not working - for every insert, it returns 1.
This is the code:
Dim id = TestTableAdapter.InsertQuery("nvnh")
Table:
CREATE TABLE [dbo].[test]
(
[Id] INT IDENTITY (1, 1) NOT NULL,
[t] NVARCHAR (50) NULL,
CONSTRAINT [PK_test] PRIMARY KEY CLUSTERED ([Id] ASC)
);
thanks for all.
i finally found the solution.
i use already any from above solutions,
but the query eqcutemode is NonQuery ,so it give me the count of inserted rows
i changed it to Scalar

Altering an existing column to 'Primary Key' and 'NOT NULL' without dropping the table

I have a table named dbo.ReferenceDetails which contains several millions of records. Here is the create and select script of my table :
CREATE TABLE [dbo].[RefDetails](
[REQ_XREF_TYPE] [varchar](12) NULL
, [REQUEST_ID] [varchar](24) NULL
, [CROSS_REFERENCE] [varchar](32) NULL
, [RUN_BY] [varchar](100) NULL
, [RUN_DATE] [datetime] NULL
, [ISCURRENTRECORD] [int] NULL
, [RECORDSTARTDATE] [datetime2](7) NULL
, [RECORDENDDATE] [datetime2](7) NULL
, [UPDATE_FLAG] [varchar](50) NULL
, [SECUREFLAG] [int] NULL
, [EVENT_TIMESTAMP] [datetime2](7) NULL
) ON [PRIMARY]
SELECT TOP (10)
[REQ_XREF_TYPE]
,[REQUEST_ID]
,[CROSS_REFERENCE]
,[RUN_BY]
,[RUN_DATE]
,[ISCURRENTRECORD]
,[RECORDSTARTDATE]
,[RECORDENDDATE]
,[UPDATE_FLAG]
,[SECUREFLAG]
,[EVENT_TIMESTAMP]
FROM [dbo].[ReferenceDetails]
Can I alter the table so that REQ_XREF_TYPE, ISCURRENTRECORD and EVENT_TIMESTAMP is Primary Key and NOT NULL without dropping the table?
Your response will be appreciated. :)
See below. You first need to convert the columns to NOT NULL then you create the Primary Key. If you already have data in the table then the creation of the primary key may take some time.
ALTER TABLE [dbo].[RefDetails] ALTER COLUMN [REQ_XREF_TYPE] VARCHAR(12) NOT NULL
ALTER TABLE [dbo].[RefDetails] ALTER COLUMN [ISCURRENTRECORD] INT NOT NULL
ALTER TABLE [dbo].[RefDetails] ALTER COLUMN [EVENT_TIMESTAMP] DATETIME2(7) NOT NULL
ALTER TABLE [dbo].[RefDetails] ADD CONSTRAINT PK_RefDetails PRIMARY KEY ([REQ_XREF_TYPE],[ISCURRENTRECORD],[EVENT_TIMESTAMP]);
Note: Your creation script says the table name is RefDetails but your OP says ReferenceDetails. I went with the creation script name.
Update:
The Primary Key requires that any column(s) selected contain a unique combination - duplicates are not allowed. If duplicates exist, the creation of the primary key will fail. To check for duplicates before creating a primary key, run the following:
SELECT [REQ_XREF_TYPE], [ISCURRENTRECORD], [EVENT_TIMESTAMP], CountDupes = COUNT(1)
FROM [dbo].[RefDetails]
GROUP BY [REQ_XREF_TYPE], [ISCURRENTRECORD], [EVENT_TIMESTAMP]
HAVING COUNT(1) > 1
ORDER BY [REQ_XREF_TYPE], [ISCURRENTRECORD], [EVENT_TIMESTAMP]
You are expecting 0 results, which means there are no duplicates. Any result will identify the unique set of duplicate records along with the number of times they are duplicates (see the CountDupes column result).
If you get 0 results, then you are clear to create the primary key.
If you get any results, then you will need to address this (i.e., remove the duplicates or include additional columns that create a unique combination).

How to design audit dimension in the data mart using SQL Server 2012

Goal
I aim to create SSIS (ETL) Template that enables audit functionality (Audit Dimension). I've discovered a few ways to implement audit dimension that are described below with some reference links below:
SEQUENCE
Primary Key
Best way to get identity of inserted row?
Environment:
There are millions of rows in a fact tables and packages run a few
times a day.
Incremental ETL gets thousands of rows.
SQL Server 2012 BI edition is used for the BI solution.
Simplified Schema of DimAudit table:
CREATE TABLE [dw].[DimAudit] (
[AuditKey] [int] IDENTITY(1 ,1) NOT NULL,
[ParentAuditKey] [int] NOT NULL,
[TableName] [varchar] (50) NOT NULL DEFAULT ('Unknown'),
[PackageName] [varchar] (50) NOT NULL DEFAULT ('Unknown'),
[ExecStartDate] [datetime] NOT NULL DEFAULT ( getdate()),
[ExecStopDate] [datetime] NULL,
[SuccessfulProcessingInd] [char] (1) NOT NULL DEFAULT ('N'),
CONSTRAINT [PK_dbo.DimAudit] PRIMARY KEY CLUSTERED
(
[AuditKey] ASC
)
) ON [PRIMARY]
ALTER TABLE [dw].[DimAudit] WITH CHECK ADD CONSTRAINT [FK_DimAudit_ParentAuditKey] FOREIGN KEY( [ParentAuditKey])
REFERENCES [dw]. [DimAudit] ( [AuditKey])
GO
ALTER TABLE [dw].[DimAudit] CHECK CONSTRAINT [FK_DimAudit_ParentAuditKey]
GO
Primary Key Option:
Primary key is generated in the audit table and then AuditKey is queried.
Task: Master SQL Audit Generates Key (SQL Task)
INSERT INTO [dw].[DimAudit]
(ParentAuditKey
,[TableName]
,[PackageName]
,[ExecStartDate]
,[ExecStopDate]
,[SuccessfulProcessingInd])
VALUES
(1
,'Master Extract Package'
,?
,?
,?
,'N')
SELECT AuditKey
FROM [dw].[DimAudit]
WHERE TableName = 'Master Extract Package' and ExecStartDT = ?
/*
Last Parameter: ParameterSystem::StartTime
Result Set populates User::ParentAuditKey
*/
Task: Master SQL Audit End (SQL Task)
UPDATE [dw]. [DimAudit]
SET ParentAuditKey = AuditKey
,ExecStopDT = SYSDATETIME()
,SuccessfulProcessingInd= 'Y'
WHERE AuditKey = ?
/*
Parameter: User::ParentAuditKey
*/
SEQUENCE Option:
The sequence option does not select primary key (AuditKey) but uses logic below to create next available AuditKey.
CREATE SEQUENCE dbo . AuditID as INT
START WITH 1
INCREMENT BY 1 ;
GO
DECLARE #AuditID INTEGER ;
SET #AuditID = NEXT VALUE FOR dbo. AuditID ;
Best way to get identity of inserted row?
It feels risky using identity options as ETL packages could be executed in parallel.
Question
What is the recommended practice for audit dimension table and managing keys?
Sequence & primary key options do the job; however, I have concerns about the selecting primary key option because package could be executed the same millisecond (in theory) and therefore, a few primary keys would exist. So, Sequence sounds like the best option.
Is anything better I could do to create Audit Dimension for a data mart?
You could use the OUTPUT syntax:
INSERT INTO [dw].[DimAudit]
(ParentAuditKey
,[TableName]
,[PackageName]
,[ExecStartDate]
,[ExecStopDate]
,[SuccessfulProcessingInd])
OUTPUT inserted.AuditKey
VALUES
(1
,'Master Extract Package'
,?
,?
,?
,'N')
or SCOPE_IDENTITY() which is what I'm personally using:
INSERT INTO Meta.AuditDim (
Date,
UserName,
Source,
SourceType,
AuditType,
ExecutionId,
ExecutionHost,
ParentAuditKey,
FileID
)
VALUES (
GETDATE(),
CURRENT_USER,
#Source,
#SourceType,
#AuditType,
#ExecutionId,
#ExecutionHost,
#ParentAuditKey,
#FileID
);
SELECT AuditKey FROM Meta.AuditDim WHERE AuditKey = SCOPE_IDENTITY();

Load user extract with stored procedure

I have a ASP MVC web application that is required to load a user extract each day from a file. The users in the database should be updated accordingly: deleted if not in source, updated if in source and target, and created if only in source. While doing this, certain rights should also automatically be given to the users. If there is any error, nothing should happen at all.
First I tried to do this with Entity Framework, but the SaveChanges call takes around two minutes to return, which is a lot for the relatively small amount of users (~140 000).
My idea now is to write a stored procedure that would do the updating. I would pass the list of new users as a parameter. The type of my temporary table:
CREATE TYPE [dbo].[TempUserType] AS TABLE
(
[Uid] NVARCHAR(80) NOT NULL PRIMARY KEY,
[GivenName] NVARCHAR(80) NOT NULL,
[FamilyName] NVARCHAR(80) NOT NULL,
[Email] NVARCHAR(256) NOT NULL,
[GiveRight1] BIT NOT NULL,
[GiveRight2] BIT NOT NULL,
[GiveRight3] BIT NOT NULL
)
The users:
CREATE TABLE [dbo].[User] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Uid] NVARCHAR (80) NOT NULL,
[GivenName] NVARCHAR (80) NOT NULL,
[FamilyName] NVARCHAR (80) NOT NULL,
[Email] NVARCHAR (256) NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC),
UNIQUE NONCLUSTERED ([Uid] ASC)
);
The user roles:
CREATE TABLE [dbo].[UserRole] (
[UserId] INT NOT NULL,
[RoleId] INT NOT NULL,
CONSTRAINT [PK_UserRole] PRIMARY KEY CLUSTERED ([UserId] ASC, [RoleId] ASC),
CONSTRAINT [FK_UserRole_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]),
CONSTRAINT [FK_UserRole_Role] FOREIGN KEY ([RoleId]) REFERENCES [dbo].[Role] ([Id])
);
The procedure I am stuck writing:
CREATE PROCEDURE [dbo].[UpdateUsers]
#extractedUsers TempUserType READONLY
AS
BEGIN TRANSACTION
MERGE
[dbo].[User] AS trg
USING
#extractedUsers AS src
ON
(trg.[Uid] = src.[Uid])
WHEN MATCHED THEN
UPDATE SET
trg.GivenName = src.GivenName,
trg.FamilyName = src.FamilyName,
trg.Email = src.Email
WHEN NOT MATCHED BY TARGET THEN
INSERT
([Uid], GivenName, FamilyName, Email)
VALUES
(src.[Uid], src.GivenName, src.FamilyName, src.Email)
WHEN NOT MATCHED BY SOURCE THEN
DELETE;
COMMIT TRANSACTION
My question: is the use of a procedure with merge appropriate in this case to achieve the performance improvement over EF? How can I attribute roles according to the 3 boolean values that are in the source table?
Roles can be hadcoded, meaning I know that the Right1 corresponds to the RoleId 1, Right 2 to RoleId 2 and Right 3 to RoleId3.
After reading the question I want to leave an idea for the solution.
For me it makes more sense to have an IF / ELSE for calling the update or the insert, not using the merge since you need the UserId that you are updating/inserting to add it's role permissions.
You can check if UId exists and if so update the user details, if does not exists then create and keep the new Identity value.
In both cases, when having the user ID add the corresponding permissions according to the boolean values with IF statements.
Pseudo-code:
If UserId exists in UsersTable
Update
Store UserId
Else
Insert
Store created UserId (using the ##IDENTITY)
If bool1 add permission 1
If bool3 add permission 2
If bool3 add permission 3

Resources