My first question — Violation of PRIMARY KEY constraint - sql

I have the following issue: I want to execute a script where I'm trying to insert data from a database that I restored (iNode-8-24-14-Orig) into a database that I created.
The code is:
/****************************************************/
SET IDENTITY_INSERT [dbo].[XHQ_HIER_DEF] ON;
GO
INSERT INTO [dbo].[XHQ_HIER_DEF]
([HIER_KEY]
,[HIER_NAME]
,[HIER_DESC]
,[SYNONYM_DEF_LEVEL]
,[CRT_XHQUSERID]
,[CRT_TIMESTAMP]
,[CRT_TZ_BIAS]
,[UPDT_XHQUSERID]
,[UPDT_TIMESTAMP]
,[UPDT_TZ_BIAS])
SELECT [HIER_KEY]
,[HIER_NAME]
,[HIER_DESC]
,[SYNONYM_DEF_LEVEL]
,[CRT_XHQUSERID]
,[CRT_TIMESTAMP]
,[CRT_TZ_BIAS]
,[UPDT_XHQUSERID]
,[UPDT_TIMESTAMP]
,[UPDT_TZ_BIAS]
FROM [iNode-8-24-14-Orig].[dbo].[XHQ_HIER_DEF]
GO
SET IDENTITY_INSERT [dbo].[XHQ_HIER_DEF] OFF;
GO
/****************************************************/
But I get this error for each table:
Violation of PRIMARY KEY constraint 'XPKXHQ_HIER_DEF'. Cannot insert
duplicate key in object 'dbo.XHQ_HIER_DEF'. The duplicate key value is
(1).
Any idea how can I fix this? I want to mention that I have the same tables and columns in both databases. I know is saying that I'm already using the same primary key, but I don't know how to fix it.

This is my first question on this site.
And we care... why?
Any idea how can i fix this?
Read the error, fix the data? The error says very clear what the issue is:
Cannot insert duplicate key in object 'dbo.XHQ_HIER_DEF'.
Do not insert the same key value multiple times. Period. If you define a field as primary key, values have per definition to be unique in the key.
There already is an entry with identity 1, or your source data has multiple rows with the same value, which is not valid per your data model.
Generally for problems like this, actually reading the error helps. In your case it is EXTREMELY clear in the description what the issue is, even giving you the value causing the problem:
The duplicate key value is (1).

Solution is obvious: do not insert duplicate primary key values.
From your I suppose:
You're using MSSQL with linked server
PK consists of HIER_KEY field only
So:
/****************************************************/
SET IDENTITY_INSERT [dbo].[XHQ_HIER_DEF] ON;
GO
INSERT INTO [dbo].[XHQ_HIER_DEF]
([HIER_KEY]
,[HIER_NAME]
,[HIER_DESC]
,[SYNONYM_DEF_LEVEL]
,[CRT_XHQUSERID]
,[CRT_TIMESTAMP]
,[CRT_TZ_BIAS]
,[UPDT_XHQUSERID]
,[UPDT_TIMESTAMP]
,[UPDT_TZ_BIAS])
SELECT [HIER_KEY]
,[HIER_NAME]
,[HIER_DESC]
,[SYNONYM_DEF_LEVEL]
,[CRT_XHQUSERID]
,[CRT_TIMESTAMP]
,[CRT_TZ_BIAS]
,[UPDT_XHQUSERID]
,[UPDT_TIMESTAMP]
,[UPDT_TZ_BIAS]
FROM [iNode-8-24-14-Orig].[dbo].[XHQ_HIER_DEF] T1
WHERE NOT EXISTS(
SELECT 1 FROM [dbo].[XHQ_HIER_DEF] T2
WHERE
T1.HIER_KEY = T2.HIER_KEY
)
GO
SET IDENTITY_INSERT [dbo].[XHQ_HIER_DEF] OFF;
GO
/****************************************************/
Warning: performance of such insert could be especially terrible.

Related

IDENTITY_INSERT error while trying to insert data into table

I want to copy data from a table named ActionType inside a database TD_EDD, into another table named ActionType inside another database DsVelocity.
I have written the following query:
INSERT INTO [DsVelocity].[dbo].[ActionType]
([ActionTypeID]
,[ActionTypeName]
,[ActiveStatus])
SELECT [ActionTypeID], [ActionType], [Active/Deactive]
FROM [TD_EDD].[dbo].[ActionType]
GO
Whenever I'm trying to do this, I'm getting the following error:
Msg 544, Level 16, State 1, Line 1
Cannot insert explicit value for identity column in table 'ActionType' when IDENTITY_INSERT is set to OFF.
I don't understand what's wrong and why I'm getting this error?
Note that I'm using Microsoft SQL Server 2008 R2.
This means that when you insert data into target table, you will have conflicting ids. Most likely ActionTypeId column
to Fix it use
INSERT INTO [DsVelocity].[dbo].[ActionType]
([ActionTypeName]
,[ActiveStatus])
SELECT [ActionType], [Active/Deactive]
FROM [TD_EDD].[dbo].[ActionType]
GO
Well, lets assume from the message that ActionTypeID is an IDENTITY Column. You cannot isert values into this column as it is auto generate, uless you use IDENTITY_INSERT
Allows explicit values to be inserted into the identity column of a
table.
At any time, only one table in a session can have the IDENTITY_INSERT
property set to ON. If a table already has this property set to ON,
and a SET IDENTITY_INSERT ON statement is issued for another table,
SQL Server returns an error message that states SET IDENTITY_INSERT is
already ON and reports the table it is set ON for.
If the value inserted is larger than the current identity value for
the table, SQL Server automatically uses the new inserted value as the
current identity value.
The setting of SET IDENTITY_INSERT is set at execute or run time and
not at parse time.
So you would have to do something like
SET IDENTITY_INSERT [DsVelocity].[dbo].[ActionType] ON
before the insert.

How to turn IDENTITY_INSERT on and off using SQL Server 2008?

Why am I getting an error doing an insert when IDENTITY_INSERT is set to OFF?
How do I turn it on properly in SQL Server 2008? Is it by using SQL Server Management Studio?
I have run this query:
SET IDENTITY_INSERT Database. dbo. Baskets ON
Then I got the message back in the console that the Command(s) completed successfully.
However when I run the application, it still gives me the error shown below:
Cannot insert explicit value for identity column in table 'Baskets' when
IDENTITY_INSERT is set to OFF.
Via SQL as per MSDN
SET IDENTITY_INSERT sometableWithIdentity ON
INSERT INTO sometableWithIdentity
(IdentityColumn, col2, col3, ...)
VALUES
(AnIdentityValue, col2value, col3value, ...)
SET IDENTITY_INSERT sometableWithIdentity OFF
The complete error message tells you exactly what is wrong...
Cannot insert explicit value for identity column in table 'sometableWithIdentity' when IDENTITY_INSERT is set to OFF.
I had a problem where it did not allow me to insert it even after setting the IDENTITY_INSERT ON.
The problem was that i did not specify the column names and for some reason it did not like it.
INSERT INTO tbl Values(vals)
So basically do the full INSERT INTO tbl(cols) Values(vals)
Import:
You must write columns in INSERT statement
INSERT INTO TABLE
SELECT * FROM
Is not correct.
Insert into Table(Field1,...)
Select (Field1,...) from TABLE
Is correct
I know this is an older thread but I just bumped into this. If the user is trying to run inserts on the Identity column after some other session Set IDENTITY_INSERT ON, then he is bound to get the above error.
Setting the Identity Insert value and the subsequent Insert DML commands are to be run by the same session.
Here #Beginner was setting Identity Insert ON separately and then running the inserts from his application. That is why he got the below Error:
Cannot insert explicit value for identity column in table 'Baskets' when
IDENTITY_INSERT is set to OFF.
It looks necessary to put a SET IDENTITY_INSERT Database.dbo.Baskets ON; before every SQL INSERT sending batch.
You can send several INSERT ... VALUES ... commands started with one SET IDENTITY_INSERT ... ON; string at the beginning. Just don't put any batch separator between.
I don't know why the SET IDENTITY_INSERT ... ON stops working after the sending block (for ex.: .ExecuteNonQuery() in C#). I had to put SET IDENTITY_INSERT ... ON; again at the beginning of next SQL command string.
This is likely when you have a PRIMARY KEY field and you are inserting a value that is duplicating or you have the INSERT_IDENTITY flag set to on
Another option is where you have tables like 'type' or 'status', for example, OrderStatus, where you always want to control the Id value, create the Id (Primary Key) column without it being an Identity column is the first place.

Help with SQL Server Trigger to truncate bad data before insert

We consume a web service that decided to alter the max length of a field from 255. We have a legacy vendor table on our end that is still capped at 255. We are hoping to use a trigger to address this issue temporarily until we can implement a more business-friendly solution in our next iteration.
Here's what I started with:
CREATE TRIGGER [mySchema].[TruncDescription]
ON [mySchema].[myTable]
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [mySchema].[myTable]
SELECT SubType, type, substring(description, 1, 255)
FROM inserted
END
However, when I try to insert on myTable, I get the error:
String or binary data would be
truncated. The statement has been
terminated.
I tried experimenting with SET ANSI_WARNINGS OFF which allowed the query to work but then simply didn't insert any data into the description column.
Is there any way to use a trigger to truncate the too-long data or is there another alternative that I can use until a more eloquent solution can be designed? We are fairly limited in table modifications (i.e. we can't) because it's a vendor table, and we don't control the web service we're consuming so we can't ask them to fix it either. Any help would be appreciated.
The error cannot be avoided because the error is happening when the inserted table is populated.
From the documentation:
http://msdn.microsoft.com/en-us/library/ms191300.aspx
"The format of the inserted and deleted tables is the same as the format of the table on which the INSTEAD OF trigger is defined. Each column in the inserted and deleted tables maps directly to a column in the base table."
The only really "clever" idea I can think of is to take advantage of schemas and the default schema used by a login. If you can get the login that the web service is using to reference another table, you can increase the column size on that table and use the INSTEAD OF INSERT trigger to perform the INSERT into the vendor table. A variation of this is to create the table in a different database and set the default database for the web service login.
CREATE TRIGGER [myDB].[mySchema].[TruncDescription]
ON [myDB].[mySchema].[myTable]
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [VendorDB].[VendorSchema].[VendorTable]
SELECT SubType, type, substring(description, 1, 255)
FROM inserted
END
With this setup everything works OK for me.
Not to state the obvious but are you sure there is data in the description field when you are testing? It is possible they change one of the other fields you are inserting as well and maybe one of those is throwing the error?
CREATE TABLE [dbo].[DataPlay](
[Data] [nvarchar](255) NULL
) ON [PRIMARY]
GO
and a trigger like this
Create TRIGGER updT ON DataPlay
Instead of Insert
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [tempdb].[dbo].[DataPlay]
([Data])
(Select substring(Data, 1, 255) from inserted)
END
GO
then inserting with
Declare #d as nvarchar(max)
Select #d = REPLICATE('a', 500)
SET ANSI_WARNINGS OFF
INSERT INTO [tempdb].[dbo].[DataPlay]
([Data])
VALUES
(#d)
GO
I am unable to reproduce this issue on SQL 2008 R2 using:
Declare #table table ( fielda varchar(10) )
Insert Into #table ( fielda )
Values ( Substring('12345678901234567890', 1, 10) )
Please make sure that your field is really defined as varchar(255).
I also strongly suggest you use an Insert statement with an explicit field list. While your Insert is syntactically correct, you really should be using an explicit field list (like in my sample). The problem is when you don't specify a field list you are at the mercy of SQL and the table definition for the field order. When you do use a field list you can change the order of the fields in the table (or add new fields in the middle) and not care about your insert statements.

SCOPE_IDENTITY And Instead of Insert Trigger work-around

OK, I have a table with no natural key, only an integer identity column as it's primary key. I'd like to insert and retrieve the identity value, but also use a trigger to ensure that certain fields are always set. Originally, the design was to use instead of insert triggers, but that breaks scope_identity. The output clause on the insert statement is also broken by the instead of insert trigger. So, I've come up with an alternate plan and would like to know if there is anything obviously wrong with what I intend to do:
begin contrived example:
CREATE TABLE [dbo].[TestData] (
[TestId] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL,
[Name] [nchar](10) NOT NULL)
CREATE TABLE [dbo].[TestDataModInfo](
[TestId] [int] PRIMARY KEY NOT NULL,
[RowCreateDate] [datetime] NOT NULL)
ALTER TABLE [dbo].[TestDataModInfo] WITH CHECK ADD CONSTRAINT
[FK_TestDataModInfo_TestData] FOREIGN KEY([TestId])
REFERENCES [dbo].[TestData] ([TestId]) ON DELETE CASCADE
CREATE TRIGGER [dbo].[TestData$AfterInsert]
ON [dbo].[TestData]
AFTER INSERT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
INSERT INTO [dbo].[TestDataModInfo]
([TestId],
[RowCreateDate])
SELECT
[TestId],
current_timestamp
FROM inserted
-- Insert statements for trigger here
END
End contrived example.
No, I'm not doing this for one little date field - it's just an example.
The fields that I want to ensure are set have been moved to a separate table (in TestDataModInfo) and the trigger ensures that it's updated. This works, it allows me to use scope_identity() after inserts, and appears to be safe (if my after trigger fails, my insert fails). Is this bad design, and if so, why?
As you mentioned, SCOPE_IDENTITY is designed for this situation. It's not affected by AFTER trigger code, unlike ##IDENTITY.
Apart from using stored procs, this is OK.
I use AFTER triggers for auditing because they are convenient... that is, write to another table in my trigger.
Edit: SCOPE_IDENTITY and parallelism in SQL Server 2005 cam have a problem
HAve you tried using OUTPUT to get the value back instead?
Have you tried using:
SELECT scope_identity();
http://wiki.alphasoftware.com/Scope_Identity+in+SQL+Server+with+nested+and+INSTEAD+OF+triggers
You can use an INSTEAD OF trigger just fine, by in the trigger capturing the value just after the insert to the main table, then spoofing the Scope_Identity() into ##Identity at the end of the trigger:
-- Inside of trigger
SET NOCOUNT ON;
INSERT dbo.YourTable VALUES(blah, blah, blah);
SET #YourTableID = Scope_Identity();
-- ... other DML that inserts to another identity-bearing table
-- Last statement in trigger
SELECT YourTableID INTO #Trash FROM dbo.YourTable WHERE YourTableID = #YourTableID;
Or, here's an alternate final statement that doesn't use any reads, but may cause permission issues if the executing user doesn't have rights (though there are solutions to this).
SET #SQL =
'SELECT identity(smallint, ' + Str(#YourTableID) + ', 1) YourTableID INTO #Trash';
EXEC (#SQL);
Note that Scope_Identity() may return NULL on a table with an INSTEAD OF trigger on it in some cases, even if you use this spoofing method. But you can at least get the value using ##Identity. This can make MS Access ADP projects start working right again after breaking because you put a trigger on a table that the front end inserts to.
Also, be aware that any parallelism at all can make ##Identity and Scope_Identity() return incorrect values—so use OPTION (MAXDOP 1) or TOP 1 or a single-row VALUES clause to defeat this problem.

Sql Server INSERT scope problem

This may have been asked before, but it's really hard to search for terms that limit the search results...
Take the following SQL snippet:
declare #source table (id int)
declare #target table(id int primary key, sourceId int)
set nocount on
insert into #target values (0,0)
insert into #source(id) values(1)
--insert into #source(id) values(2)
set nocount off
insert into #target select (select max(id)+1 from #target), s.id from #source s
select * from #target
This obviously executes without error, but now uncomment the second insert line and the following error occurs:
Msg 2627, Level 14, State 1, Line 15
Violation of PRIMARY KEY constraint 'PK__#7DB3CB72__7EA7EFAB'. Cannot insert duplicate key in object 'dbo.#target'.
I realise that the insert statement more than likely is effected against a snapshot of the #target table so (select max(id)+1 from #target) will always return a value of 1 - causing the violation error above...
Is there any way around this apart from resorting to a cursor?
Change your insert statement to the following:
insert into #target select (select
max(id) from #target) + (ROW_NUMBER()
OVER(ORDER BY s.id)), s.id from
#source s
This should work for this specific case but I would be careful about generalizing it.
You could use an identity column (that's exactly what they are meant for)
declare #target table(id int IDENTITY(1,1), sourceId int)
If your problem is that the select clause is "computed" before the insert is executed, there's afaik no way around this using a single SQL request
I think it's by design ; For your insertion to avoid duplicates, the index id must be computed during the insert, not during the select. This is the exact purpose of the IDENTITY keyword.
If you want to insert one select at a time, you must write separate requests (using cursors for example, but you'll lose atomicity, and will have to use proper locking keywords to avoid race conditions)
The way you're determining your new PK value, is a race condition waiting to happen.
If your DB is under high load, and multiple records are being inserted at the same time, you're going to get unexpected results.
Why don't you just use an identity column , and let the database handle the assignment of a new primary Id ?
Or, you can create some kind of meta-table, which holds a record for every table in your database, and this record contains the next value that should be used as a primary id in the table.
Then, you must make sure that every time you create a new record, you also update the next-value in your meta-table (and you should make sure that you do the appropriate locking), but, I see no added value in this approach vs making use of identity columns.