insert then select scope_identity() generates duplicate rows - sql-server

CREATE TABLE [schema].[table] (
[column1] int IDENTITY NOT NULL,
[column2] int NULL,
[column3] int NULL,
PRIMARY KEY CLUSTERED ([column1])
);
INSERT INTO schema.table (column2,column3) VALUES (1,1);
SELECT scope_identity();
it inserts TWO identical rows, and returns the primary key for the second inserted row.
It is probably a very basic reason, but google is not my friend on this one.

Please copy and paste verbatim
SET NOCOUNT ON;
USE tempdb;
CREATE TABLE dbo.[table] (
[column1] int IDENTITY NOT NULL,
[column2] int NULL,
[column3] int NULL,
PRIMARY KEY CLUSTERED ([column1])
);
INSERT INTO dbo.[table] (column2,column3) VALUES (1,1);
SELECT scope_identity();
SELECT * FROM dbo.[table]
You should get
---------------------------------------
1
column1 column2 column3
----------- ----------- -----------
1 1 1
When in doubt, always try on a clean new table in tempdb.
Other notes:
If you are running insert from ASP.Net, check whether you have CSS elements (background image link) that is blank, it causes a 2nd request to the same page
If you are running just a plain INSERT in SSMS or similar tool, check for triggers
To find triggers against a table using TSQL
select name, OBJECT_NAME(parent_object_id)
from sys.objects
where type='TR'
and OBJECT_NAME(parent_object_id) = 'table' -- or whatever the table name is
To view the text of a trigger (or any module) using TSQL
select definition
from sys.sql_modules
where object_id = object_id('tg_table') -- or whatever the trigger is named

Related

Inserting to table having issue - Explicit value must be specified for identity column in table

I'm getting ready to release a stored procedure that gets info from other tables, does a pre-check, then inserts the good data into a (new) table. I'm not used to working with keys and new tables as much, and my insert into this new table I'm creating is having this error message having to do with the insert/key:
Msg 545, Level 16, State 1, Line 131
Explicit value must be specified for identity column in table 'T_1321_PNAnnotationCommitReport' either when IDENTITY_INSERT is set to ON or when a replication user is inserting into a NOT FOR REPLICATION identity column.
BEGIN
...
BEGIN
IF NOT EXISTS (SELECT * FROM sys.tables where name = N'T_1321_PNAnnotationCommitReport')
BEGIN
CREATE TABLE T_1321_PNAnnotationCommitReport (
[id] [INT] IDENTITY(1,1) PRIMARY KEY NOT NULL, --key
[progressnote_id] [INT] NOT NULL,
[form_id] [INT] NOT NULL,
[question_id] [INT],
[question_value] [VARCHAR](max),
[associatedconcept_id] [INT],
[crte_date] [DATETIME] DEFAULT CURRENT_TIMESTAMP,
[create_date] [DATETIME] --SCHED_RPT_DATE
);
print 'test';
END
END --if not exists main table
SET IDENTITY_INSERT T_1321_PNAnnotationCommitReport ON;
...
INSERT INTO dbo.T_1321_PNAnnotationCommitReport--(progressnote_id,form_id,question_id,question_value,associatedconcept_id,crte_date, create_date) **I tried with and without this commented out part and it's the same.
SELECT progressnote_id,
a.form_id,
question_id,
questionvalue,
fq.concept_id,
getdate(),
a.create_date
FROM (
SELECT form_id,
progressnote_id,
R.Q.value('#id', 'varchar(max)') AS questionid,
R.Q.value('#value', 'varchar(max)') AS questionvalue,
create_date
FROM
#tableNotes t
OUTER APPLY t.form_questions.nodes('/RESULT/QUESTIONS/QUESTION') AS R(Q)
WHERE ISNUMERIC(R.Q.value('#id', 'varchar(max)')) <> 0
) a
INNER JOIN [CKOLTP_DEV]..FORM_QUESTION fq ON
fq.form_id = a.form_id AND
fq.question_id = a.questionid
--select * from T_1321_PNAnnotationCommitReport
SET IDENTITY_INSERT T_1321_PNAnnotationCommitReport OFF;
END
Any ideas?
I looked at some comparable inserts we do at work, insert into select and error message, and insert key auto-incremented, and I think I'm doing what they do. Does anyone else see my mistake? Thanks a lot.
To repeat my comment under the question:
The error is literally telling you the problem. You turn change the IDENTITY_INSERT property to ON for the table T_1321_PNAnnotationCommitReport and then omit the column id in your INSERT. If you have enabled IDENTITY_INSERT you need to supply a value to that IDENTITY, just like the error says.
We can easily replicate this problem with the following batches:
CREATE TABLE dbo.MyTable (ID int IDENTITY(1,1),
SomeValue varchar(20));
GO
SET IDENTITY_INSERT dbo.MyTable ON;
--fails
INSERT INTO dbo.MyTable (SomeValue)
VALUES('abc');
GO
If you want the IDENTITY value to be autogenerated, then leave IDENTITY_INSERT set to OFF and omit the column from the INSERT (like above):
SET IDENTITY_INSERT dbo.MyTable OFF; --Shouldn't be needed normally, but we manually changed it before
--works, as IDENTITY_INSERT IS OFF
INSERT INTO dbo.MyTable (SomeValue)
VALUES('abc');
If you do specifically want to define the value for the IDENTITY, then you need to both set IDENTITY_INSERT to ON and provide a value in the INSERT statement:
SET IDENTITY_INSERT dbo.MyTable ON;
--works
INSERT INTO dbo.MyTable (ID,SomeValue)
VALUES(10,'def');
GO
SELECT *
FROM dbo.MyTable;
IDENTITY_INSERT doesn't mean "Get the RDBMS to 'insert' the value" it means that you want to want to tell the RDBMS what value to INSERT. This is covered in the opening sentence of the documentation SET IDENTITY_INSERT (Transact-SQL):
Allows explicit values to be inserted into the identity column of a table.
(Emphasis mine)

Reordering Identity primary key in sql server

Yes i am very well aware the consequences. But i just want to reorder them. Start from 1 to end.
How do I go about reordering the keys using a single query ?
It is clustered primary key index
Reordering like
First record Id 1
second record Id 2
The primary key is Int
Drop PK constraint
Drop Identity column
Re-create Identity Column
Re-Create PK
USE Test
go
if(object_id('IdentityTest') Is not null)
drop table IdentityTest
create table IdentityTest
(
Id int identity not null,
Name varchar(5),
constraint pk primary key (Id)
)
set identity_insert dbo.IdentityTest ON
insert into dbo.IdentityTest (Id,Name) Values(23,'A'),(26,'B'),(34,'C'),(35,'D'),(40,'E')
set identity_insert dbo.IdentityTest OFF
select * from IdentityTest
------------------1. Drop PK constraint ------------------------------------
ALTER TABLE [dbo].[IdentityTest] DROP CONSTRAINT [pk]
GO
------------------2. Drop Identity column -----------------------------------
ALTER table dbo.IdentityTest
drop column Id
------------------3. Re-create Identity Column -----------------------------------
ALTER table dbo.IdentityTest
add Id int identity(1,1)
-------------------4. Re-Create PK-----------------------
ALTER TABLE [dbo].[IdentityTest] ADD CONSTRAINT [pk] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
--------------------------------------------------------------
insert into dbo.IdentityTest (Name) Values('F')
select * from IdentityTest
IDENTITY columns are not updatable irrespective of SET IDENTITY_INSERT options.
You could create a shadow table with the same definition as the original except for the IDENTITY property. Switch into that (this is a metadata only change with no movement of rows that just affects the table's definition) then update the rows and switch back though.
A full worked example going from a situation with gaps to no gaps is shown below (error handling and transactions are omitted below for brevity).
Demo Scenario
/*Your original table*/
CREATE TABLE YourTable
(
Id INT IDENTITY PRIMARY KEY,
OtherColumns CHAR(100) NULL
)
/*Some dummy data*/
INSERT INTO YourTable (OtherColumns) VALUES ('A'),('B'),('C')
/*Delete a row leaving a gap*/
DELETE FROM YourTable WHERE Id =2
/*Verify there is a gap*/
SELECT *
FROM YourTable
Remove Gaps
/*Create table with same definition as original but no `IDENTITY`*/
CREATE TABLE ShadowTable
(
Id INT PRIMARY KEY,
OtherColumns CHAR(100)
)
/*1st metadata switch*/
ALTER TABLE YourTable SWITCH TO ShadowTable;
/*Do the update*/
WITH CTE AS
(
SELECT *,
ROW_NUMBER() OVER (ORDER BY Id) AS RN
FROM ShadowTable
)
UPDATE CTE SET Id = RN
/*Metadata switch back to restore IDENTITY property*/
ALTER TABLE ShadowTable SWITCH TO YourTable;
/*Remove unneeded table*/
DROP TABLE ShadowTable;
/*No Gaps*/
SELECT *
FROM YourTable
I don't think there is any way to do this in a single query. Your best bet is to copy the data to a new table, drop and recreate the original table (or delete the data and reseed the identity) and reinsert the data in the original order using the previous identity as the ordering (but not re-inserting it).
CREATE TABLE Table1_Stg (bla bla bla)
INSERT INTO Table1_Stg (Column2, Column3,...) SELECT Column2, Column3,... FROM Table1 ORDER BY Id
Here the Id column is excluded from the SELECT column list.
Or, you can do:
SELECT * INTO Table1_Stg FROM Table1 ORDER BY Id
DROP Table1
sp_rename Table1_stg Table1
Please lookup the usage for sp_rename as I am doing this from memory.
Hope this helps.
EDIT: Please save a script with all your indexes and constraints if any on Table1.
EDIT2: Added second method of creating table and inserting into table.
UPDATE tbl SET id = (SELECT COUNT(*) FROM tbl t WHERE t.id <= tbl.id);
This last statement is genius. Just had to remove the primary key from the table design first and make sure under the design option Identity Specifications is set to no. Once you run the query set these options back.

Is it possible to associate Unique constraint with a Check constraint?

I have a table access whose schema is as below:
create table access (
access_id int primary key identity,
access_name varchar(50) not null,
access_time datetime2 not null default (getdate()),
access_type varchar(20) check (access_type in ('OUTER_PARTY','INNER_PARTY')),
access_message varchar(100) not null,
)
Access types allowed are only OUTER_PARTY and INNER_PARTY.
What I am trying to achieve is that the INNER_PARTY entry should be only once per day per login (user), but the OUTER_PARTY can be recorded any number of times. So I was wondering if its possible to do it directly or if there is an idiom to create this kind of restriction.
I have checked this question: Combining the UNIQUE and CHECK constraints, but was not able to apply it to my situation as it was aiming for a different thing.
A filtered unique index can be added to the table. This index can be based on a computed column which removes the time component from the access_time column.
create table access (
access_id int primary key identity,
access_name varchar(50) not null,
access_time datetime2 not null default (SYSDATETIME()),
access_type varchar(20) check (access_type in ('OUTER_PARTY','INNER_PARTY')),
access_message varchar(100) not null,
access_date as CAST(access_time as date)
)
go
create unique index IX_access_singleinnerperday on access (access_date,access_name) where access_type='INNER_PARTY'
go
Seems to work:
--these inserts are fine
insert into access (access_name,access_type,access_message)
select 'abc','inner_party','hello' union all
select 'def','outer_party','world'
go
--as are these
insert into access (access_name,access_type,access_message)
select 'abc','outer_party','hello' union all
select 'def','outer_party','world'
go
--but this one fails
insert into access (access_name,access_type,access_message)
select 'abc','inner_party','hello' union all
select 'def','inner_party','world'
go
unfortunately you cant add a "if" on a check constraint. I advise using a trigger:
create trigger myTrigger
on access
instead of insert
as
begin
declare #access_name varchar(50)
declare #access_type varchar(20)
declare #access_time datetime2
select #access_name = access_name, #access_type= access_type, #access_time=access_time from inserted
if exists (select 1 from access where access_name=#access_name and access_type=#access_type and access_time=#access_time) begin
--raise excetion
end else begin
--insert
end
end
you will have to format the #access_time to consider only the date part

SQL Server add auto increment primary key to existing table

As the title, I have an existing table which is already populated with 150000 records. I have added an Id column (which is currently null).
I'm assuming I can run a query to fill this column with incremental numbers, and then set as primary key and turn on auto increment. Is this the correct way to proceed? And if so, how do I fill the initial numbers?
No - you have to do it the other way around: add it right from the get go as INT IDENTITY - it will be filled with identity values when you do this:
ALTER TABLE dbo.YourTable
ADD ID INT IDENTITY
and then you can make it the primary key:
ALTER TABLE dbo.YourTable
ADD CONSTRAINT PK_YourTable
PRIMARY KEY(ID)
or if you prefer to do all in one step:
ALTER TABLE dbo.YourTable
ADD ID INT IDENTITY
CONSTRAINT PK_YourTable PRIMARY KEY CLUSTERED
You can't "turn on" the IDENTITY: it's a table rebuild.
If you don't care about the number order, you'd add the column, NOT NULL, with IDENTITY in one go. 150k rows isn't a lot.
If you need to preserve some number order, then add the numbers accordingly. Then use the SSMS table designer to set the IDENTITY property. This allows you to generate a script which will do the column drop/add/keep numbers/reseed for you.
I had this issue, but couldn't use an identity column (for various reasons).
I settled on this:
DECLARE #id INT
SET #id = 0
UPDATE table SET #id = id = #id + 1
Borrowed from here.
If the column already exists in your table and it is null, you can update the column with this command (replace id, tablename, and tablekey ):
UPDATE x
SET x.<Id> = x.New_Id
FROM (
SELECT <Id>, ROW_NUMBER() OVER (ORDER BY <tablekey>) AS New_Id
FROM <tablename>
) x
When we add and identity column in an existing table it will automatically populate no need to populate it manually.
ALTER TABLE table_name ADD temp_col INT IDENTITY(1,1)
update
This answer is a small addition to the highest voted answer and works for SQL Server. The question requested an auto increment primary key, the current answer does add the primary key, but it is not flagged as auto-increment. The script below checks for the columns, existence, and adds it with the autoincrement flag enabled.
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'YourTable' AND COLUMN_NAME = 'PKColumnName')
BEGIN
ALTER TABLE dbo.YourTable
ADD PKColumnName INT IDENTITY(1,1)
CONSTRAINT PK_YourTable PRIMARY KEY CLUSTERED
END
GO
by the designer you could set identity (1,1)
right click on tbl => desing => in part left (right click) => properties => in identity columns select #column
Properties
idendtity column
If your table has relationship with other tables using its primary or foriegen key, may be it is impossible to alter your table. so you need to drop and create the table again.
To solve these problems you need to Generate Scripts by right click on the database and in advanced option set type of data to script to scheme and data. after that, using this script with the changing your column to identify and regenerate the table using run its query.
your query will be like here:
USE [Db_YourDbName]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
Drop TABLE [dbo].[Tbl_TourTable]
CREATE TABLE [dbo].[Tbl_TourTable](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[Family] [nvarchar](150) NULL)
GO
SET IDENTITY_INSERT [dbo].[Tbl_TourTable] ON
INSERT [dbo].[Tbl_TourTable] ([ID], [Name], [Family]) VALUES (1,'name 1', 'family 1')
INSERT [dbo].[Tbl_TourTable] ([ID], [Name], [Family]) VALUES (1,'name 1', 'family 1')
INSERT [dbo].[Tbl_TourTable] ([ID], [Name], [Family]) VALUES (1,'name 1', 'family 1')
INSERT [dbo].[Tbl_TourTable] ([ID], [Name], [Family]) VALUES (1,'name 1', 'family 1')
INSERT [dbo].[Tbl_TourTable] ([ID], [Name], [Family]) VALUES (1,'name 1', 'family 1')
INSERT [dbo].[Tbl_TourTable] ([ID], [Name], [Family]) VALUES (1,'name 1', 'family 1')
INSERT [dbo].[Tbl_TourTable] ([ID], [Name], [Family]) VALUES (1,'name 1', 'family 1')
SET IDENTITY_INSERT [dbo].[Tbl_TourTable] off
Here is an idea you can try.
Original table - no identity column table1
create a new table - call table2 along with identity column.
copy the data from table1 to table2 - the identity column is populated automatically with auto incremented numbers.
rename the original table - table1 to table3
rename the new table - table2 to table1 (original table)
Now you have the table1 with identity column included and populated for the existing data.
after making sure there is no issue and working properly, drop the table3 when no longer needed.
Create a new Table With Different name and same columns, Primary Key and Foreign Key association and link this in your Insert statement of code.
For E.g : For EMPLOYEE, replace with EMPLOYEES.
CREATE TABLE EMPLOYEES(
EmpId INT NOT NULL IDENTITY(1,1),
F_Name VARCHAR(20) ,
L_Name VARCHAR(20) ,
DOB DATE ,
DOJ DATE ,
PRIMARY KEY (EmpId),
DeptId int FOREIGN KEY REFERENCES DEPARTMENT(DeptId),
DesgId int FOREIGN KEY REFERENCES DESIGNATION(DesgId),
AddId int FOREIGN KEY REFERENCES ADDRESS(AddId)
)
However, you have to either delete the existing EMPLOYEE Table or do some adjustment according to your requirement.
alter table /** paste the tabal's name **/
add id int IDENTITY(1,1)
delete from /** paste the tabal's name **/
where id in
(
select a.id FROM /** paste the tabal's name / as a
LEFT OUTER JOIN (
SELECT MIN(id) as id
FROM / paste the tabal's name /
GROUP BY
/ paste the columns c1,c2 .... **/
) as t1
ON a.id = t1.id
WHERE t1.id IS NULL
)
alter table /** paste the tabal's name **/
DROP COLUMN id
Try This Code Bellow:
DBCC CHECKIDENT ('settings', RESEED, 0)
This works in MariaDB, so I can only hope it does in SQL Server: drop the ID column you've just inserted, then use the following syntax:-
ALTER TABLE table_name ADD id INT PRIMARY KEY AUTO_INCREMENT;
No need for another table. This simply inserts an id column, makes it the primary index, and populates it with sequential values. If SQL Server won't do this, my apologies for wasting your time.
Try something like this (on a test table first):
USE your_database_name
GO
WHILE (SELECT COUNT(*) FROM your_table WHERE your_id_field IS NULL) > 0
BEGIN
SET ROWCOUNT 1
UPDATE your_table SET your_id_field = MAX(your_id_field)+1
END
PRINT 'ALL DONE'
I have not tested this at all, so be careful!
ALTER TABLE table_name ADD COLUMN ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT ;
This could be useful

getting the autoincremnt value after inserting record using SQL Server

I'm developing a project using VB.NET connected to SQL Server database
and in this project i need to get the value of column called "ID" after inserting a record to
the database immediately.
thanx.
CREATE TABLE dbo.SomeTable (
ID int IDENTITY
,[NAME] varchar(50)
);
go
INSERT INTO dbo.SomeTable ([Name])
SELECT 'Joe' UNION
SELECT 'Jim' UNION
SELECT 'JIll'
;
SELECT ident_current('dbo.SomeTable') AS [LastID_1]
,##IDENTITY AS [LastID_2]
,scope_identity() AS [LastID_3]
;
USES:
ident_current ('TableName') for a specific table, not limited by scope and session
##IDENTITY last ID in current session
scope_identity() last ID in current session, current scope
Have a look at SCOPE_IDENTITY (Transact-SQL)
INSERT
INTO [News]
(
LanguageID,
Title,
Short,
[Full],
Published,
AllowComments,
CreatedOn
)
VALUES
(
#LanguageID,
#Title,
#Short,
#Full,
#Published,
#AllowComments,
#CreatedOn
)
set #NewsID=##identity
SCOPE_IDENTITY, IDENT_CURRENT, and ##IDENTITY are similar functions because they return values that are inserted into identity columns.
TABLE
EMPID ( AUTOINCREAMENT)
NAME VARCHAR(50)
INSERT INTO TABLE ( NAME) VALUES ('RAMKUMAR')
SELECT ##IDENTITY
use ##identity
returns the autoincreament value
You can create temp table variable and create a column called StuId.
--Student table creation
CREATE TABLE [dbo].[StuTable](
[StuId] [int] IDENTITY(1,1) NOT NULL,
[StuName] [nvarchar](50) NOT NULL,
[DOB] [date] NULL
)
DECLARE #TempTable TABLE(StuId INT)
INSERT INTO [dbo].[StuTable]([StuName]
,[DOB])
OUTPUT inserted.StuId INTO #TempTable
VALUES ('Peter', '1979-01-01')
SELECT StuId FROM #TempTable --Get the auto increment Id

Resources