I deleted all records from a table on a SQL Server 2014 using the TRUNCATE function, but now new records on this table begin with PK 1.
Is there a way to not reset the primary key so that the new records begin from the last PK + 1?
Deleting will work. But sometimes the time a delete takes can be an issue. If you have to truncate you can do the following:
Get the maximum value from your table and store this in a variable.
Truncate the table
Reseed the identity to the variable value + 1.
Here is an example.
DECLARE #MaxID AS INT;
SELECT #MaxID = MAX(ID) + 1
FROM TableA;
TRUNCATE TABLE TableA;
DBCC CHECKIDENT ('dbo.TableA', RESEED, #MaxID);
New inserts into the table will now continue from the previous identity value.
Marius
Using DELETE seems to work.
DECLARE #Tab TABLE (Id INT IDENTITY(1,1), Col VARCHAR)
DECLARE #Num INT = 10,
#Count INT = 0
WHILE #Count < #Num
BEGIN
INSERT #Tab
VALUES('')
SET #Count = #Count + 1
END
DELETE FROM #Tab
INSERT #Tab
VALUES ('')
SELECT *
FROM #Tab
Related
I know that the SCOPE_IDENTITY() will get the last inserted row from insert statement. However, for the following case, I am not too sure is SCOPE_IDENTITY() is safe. As SELECT MAX(ID) FROM TableA will have go through scan the table to get the max id and it will have performance issue, even slightly, I believe.
Here is the case:
DECLARE #DaysInMonth INT
DECLARE #FirstID INT
DECLARE #SecondID INT
DECLARE #ThirdID INT
DECLARE #FourthID INT
SET #DaysInMonth = DAY(EOMONTH('2016-09-01'))
BEGIN TRY
BEGIN TRANSACTION
WHILE #DaysInMonth > 0
BEGIN
-- First Insert -- Begin
INSERT INTO tableA ('first insert - ' + #DaysInMonth)
-- First Insert -- End
SET #FirstID = SCOPE_IDENTITY()
-- Second Insert -- Begin
INSERT INTO tableB ('second insert - ' + #DaysInMonth)
-- Second Insert -- End
SET #SecondID = SCOPE_IDENTITY()
-- Third Insert -- Begin
INSERT INTO tableC ('third insert - ' + #DaysInMonth)
-- Third Insert -- End
SET #ThirdID = SCOPE_IDENTITY()
-- Fourth Insert -- Begin
INSERT INTO tableD ('fourth insert - ' + #DaysInMonth)
-- Fourth Insert -- End
SET #FourthID = SCOPE_IDENTITY()
SET #DaysInMonth = #DaysInMonth - 1
END
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION
THROW
END CATCH
As from the case above, I have to insert the records every loop for fourth times for how many days in the month that I have declared.
From what I know, there are 4 to get the last inserted ID:
SCOPE_IDENTITY
##IDENTITY
SELECT MAX(ID) FROM tableA
IDENT_CURRENT
From the following post:
Post
Is mentioned that SCOPE_IDENTITY() is what generally that you want to use.
What I mean with 'Safe' is, do the ID will be unique during the loop?
Thank you.
You can use OUTPUT column in the last insert statement, Ofcourse this is another option where you will get what exactly input statement executed.. Below is just an example
CREATE TABLE #tablea (
id int IDENTITY (1, 1),
val char(10)
)
DECLARE #outputtbl TABLE (
id int,
val char(10)
)
INSERT INTO #tablea (val)
OUTPUT INSERTED.* INTO #outputtbl
VALUES ('test')
SELECT id
FROM #outputtbl
I need to insert a value into a table which only consists of one column, that is, the primary key.
Furthermore, NULL is not allowed, Identity is set to FALSE and both Identity Seed and Identity Increment are set to 0.
I try to insert with INSERT INTO table(id) VALUES (null) which obviously does not work. INSERT INTO table(id) default values also does not work.
How can I fill this column with the correctly incremented ID?
Implementing Identity or Sequence would be the best solution, but if you really cannot alter the schema the alternative is to lock the table in a transaction, create the new value, unlock the table. Note this can have performance consequences.
create table dbo.ids ( id int primary key clustered );
GO
insert dbo.ids values ( 1 ), ( 2 ), ( 3 ), ( 4 ) ;
GO
declare #newid int;
begin transaction
set #newid = ( select top( 1 ) id from dbo.ids with ( tablockx, holdlock ) order by id desc ) + 1 ;
insert into dbo.ids values ( #newid );
select #newid;
commit
GO 20
You can use while function in that insert
declare #id int
select #id = max(id) from table
while #id <= (... put here max nuber of your id you want to insert)
begin
insert into table values (#id)
set #id = #id+1 end
end
This can be a solution too.
declare #newid integer
begin tran
select #newid = isnull(max(id), 0) + 1 from table with (xlock,holdlock)
insert into table values(#newid)
select #newid
commit tran
We have 2 tables in SQL Server 2008 R2. Periodically, we have to insert a batch of records from Table A to Table B. While the inserting, Table B still able to SELECT & UPDATE. Currently, we use INSERT..SELECT to copy from Table A to Table B. But the problem is while inserting, sometimes will cause UPDATE statement to TABLE B timeout.
Is there a better bulk insert solution from a table to another that won't cause blocking?
They most obvious solution is to use smaller batches as Stanley suggested. If this is really not an option you could explore '(transaction level) snapshot isolation.
1 Set the transaction timeout to a large enough value, so that the statement no longer goes in timeout.
2 Use CURSOR and do it row by row
3 Try this way of doing things. Requires a row identifier (IDENTITY for instance), best to have a PK or INDEX on that field:
SET NOCOUNT ON;
CREATE TABLE #A(
row_id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
data INT NOT NULL
);
CREATE TABLE #B(
row_id INT NOT NULL PRIMARY KEY,
data INT NOT NULL
);
-- TRUNCATE TABLE #B; -- no truncate needed since you just want to add rows, not copy the whole table
DECLARE #batch_size INT;
SET #batch_size = 10000;
DECLARE #from_row_id INT;
DECLARE #to_row_id INT;
-- You would use this to establish the first #from_row_id if you wanted to copy the whole table
-- SELECT
-- #from_row_id=ISNULL(MIN(row_id),-1)
-- FROM
-- #A AS a;
SELECT
#from_row_id=ISNULL(MAX(row_id),-1)
FROM
#B AS b;
IF #from_row_id=-1
SELECT
#from_row_id=ISNULL(MIN(row_id),-1)
FROM
#A AS a;
ELSE
SELECT
#from_row_id=ISNULL(MIN(row_id),-1)
FROM
#A AS a
WHERE
row_id>#from_row_id;
WHILE #from_row_id>=0
BEGIN
SELECT
#to_row_id=ISNULL(MAX(row_id),-1)
FROM
(
SELECT TOP(#batch_size)
row_id
FROM
#A AS a
WHERE
row_id>=#from_row_id
) AS row_ids
IF #to_row_id=-1
BEGIN
INSERT
#B
SELECT
*
FROM
#A AS a
WHERE
row_id>=#from_row_id;
BREAK;
END
ELSE
INSERT
#B
SELECT
*
FROM
#A AS a
WHERE
row_id BETWEEN #from_row_id AND #to_row_id;
SELECT
#from_row_id=ISNULL(MIN(row_id),-1)
FROM
#A AS a
WHERE
row_id>#to_row_id;
END
DROP TABLE #B;
DROP TABLE #A;
I am a little stuck with why I can not seem to get the 'new identity' of the inserted row with the statement below. SCOPE_IDENTITY() just returns null.
declare #WorkRequestQueueID int
declare #LastException nvarchar(MAX)
set #WorkRequestQueueID = 1
set #LastException = 'test'
set nocount off
DELETE dbo.WorkRequestQueue
OUTPUT
DELETED.MessageEnvelope,
DELETED.Attempts,
#LastException,
GetUtcdate(), -- WorkItemPoisened datetime
DELETED.WorkItemReceived_UTC
INTO dbo.FaildMessages
FROM dbo.WorkRequestQueue
WHERE
WorkRequestQueue.ID = #WorkRequestQueueID
IF ##ROWCOUNT = 0
RAISERROR ('Record not found', 16, 1)
SELECT Cast(SCOPE_IDENTITY() as int)
Any assistance would be most appreciated.
For now I use a workaround this like so.
declare #WorkRequestQueueID int
declare #LastException nvarchar(MAX)
set #WorkRequestQueueID = 7
set #LastException = 'test'
set nocount on
set xact_abort on
DECLARE #Failed TABLE
(
MessageEnvelope xml,
Attempts smallint,
LastException nvarchar(max),
WorkItemPoisened_UTC datetime,
WorkItemReceived_UTC datetime
)
BEGIN TRAN
DELETE dbo.WorkRequestQueue
OUTPUT
DELETED.MessageEnvelope,
DELETED.Attempts,
#LastException,
GetUtcdate(), -- WorkItemPoisened datetime
DELETED.WorkItemReceived_UTC
INTO
#Failed
FROM
dbo.WorkRequestQueue
WHERE
WorkRequestQueue.ID = #WorkRequestQueueID
IF ##ROWCOUNT = 0 BEGIN
RAISERROR ('Record not found', 16, 1)
Rollback
END ELSE BEGIN
insert into dbo.FaildMessages select * from #Failed
COMMIT TRAN
SELECT Cast(SCOPE_IDENTITY() as int)
END
EDITED FEB'2013
#MartinSmith alerts us that this bug don't want be fixed by Microsoft.
"Posted by Microsoft on 2/27/2013 at 2:18 PM Hello Martin, We
investigated the issue and found that changing the behavior is not an
easy thing to do. It would basically require redefining some of the
behavior when both INSERT & OUTPUT INTO target has identity columns.
Given the nature of the problem & the uncommon scenario, we have
decided not to fix the issue. -- Umachandar, SQL Programmability
Team"
EDITED OCT'2012
This is caused by a bug:
Testing bug:
Quoting OUTPUT Clause doc:
##IDENTITY, SCOPE_IDENTITY, and IDENT_CURRENT return identity values
generated only by the nested DML statement, and not those generated by
the outer INSERT statement.
After test it It seems that scope_identity() only works if outer operation is an insert in a table with identity columns:
Test 1: Delete
create table #t ( a char(1) );
create table #d ( a char(1), i int identity );
insert into #t
values ('a'),('b'),('c');
delete #t
output deleted.a into #d;
select SCOPE_IDENTITY(), * from #d;
a i
---- - -
null a 1
null b 2
null c 3
Test 2: Inserting in outer table with identity
create table #t ( a char(1), i int identity );
create table #d ( a char(1), i int identity );
insert into #t
values ('x'),('x'),('x');
insert into #t
output inserted.a into #d
values ('a'),('b');
select scope_identity(), * from #d;
a i
- - -
2 a 1
2 b 2
Test 3: Inserting in outer table without identity
create table #t ( a char(1) );
create table #d ( a char(1), i int identity );
insert into #t
values ('x'),('x'),('x');
insert into #t
output inserted.a into #d
values ('a'),('b');
select scope_identity(), * from #d;
a i
---- - -
null a 1
null b 2
You might try to use a table variable for your output clause, thus allowing you to explicitly insert into FaildMessages:
declare #WorkRequestQueueID int
declare #LastException nvarchar(MAX)
set #WorkRequestQueueID = 1
set #LastException = 'test'
set nocount off
-- Declare a table variable to capture output
DECLARE #output TABLE (
MessageEnvelope VARCHAR(50), -- Guessing at datatypes
Attempts INT, -- Guessing at datatypes
WorkItemReceived_UTC DATETIME -- Guessing at datatypes
)
-- Run the deletion with output
DELETE dbo.WorkRequestQueue
OUTPUT
DELETED.MessageEnvelope,
DELETED.Attempts,
DELETED.WorkItemReceived_UTC
-- Use the table var
INTO #output
FROM dbo.WorkRequestQueue
WHERE
WorkRequestQueue.ID = #WorkRequestQueueID
-- Explicitly insert
INSERT
INTO dbo.FaildMessages
SELECT
MessageEnvelope,
Attempts,
#LastException,
GetUtcdate(), -- WorkItemPoisened datetime
WorkItemReceived_UTC
FROM #output
IF ##ROWCOUNT = 0
RAISERROR ('Record not found', 16, 1)
SELECT Cast(SCOPE_IDENTITY() as int)
I need to create a table variable with an identity seed that starts with the max value of a field in another table?
I've tried this:
DECLARE #IdentitySeed int
SET #IdentitySeed = (SELECT MAX(HHRecId) +1 FROM xxx )
DECLARE #HH TABLE (
HHId INT IDENTITY(#IdentitySeed,1)
,AddressType CHAR(1)
,Address1 VARCHAR(100)
,City VARCHAR(50)
,State VARCHAR(2)
,Zip VARCHAR(5)
,LastName VARCHAR(50)
)
But that gives a syntax error.
For now, I've added another int column to the table variable and update that with the sum of the identity column and #IdentitySeed but I would like to find a way to do that without the update.
You can check the current value of an IDENTITY column by using:
DBCC CHECKIDENT (#HH)
and you can also change that later on using:
DBCC CHECKIDENT (#HH, RESEED, 42)
and that also works with a variable for the new value:
DBCC CHECKIDENT (#HH, RESEED, #IdentitySeed)
It works for local and global temporary tables (i.e. CREATE TABLE #HH (...) or CREATE TABLE ##HH (....) - but it doesn't seem to work with table variables :-(
Sorry, it seems you can't do this with table variables.....
I ended up doing the following:
DECLARE #NewId INT
SELECT #NewId = MAX(ID) FROM MyIDSTable
DECLARE #MyTempData TABLE (
Id int not null primary key
,Field1 int not null
,Field2 nvarchar(25) not null
,Field3 datetime
)
INSERT INTO #MyTempData
SELECT ROW_NUMBER() OVER ( Order by [C].[Cancel_id] ASC) + #NewId -1 [RowNum]
,Field1
,Field2
,Field3
INSERT INTO MyTable SELECT * FROM #MyTempData
UPDATE MYIDSTable SET ID = (SELECT MAX(ID) FROM #MyTempData) + 1 WHERE Name = 'Something'
Thank you
I believe you can do this, but it'll have to be done in dynamic SQL - declare the tableVar in the dynamic SQL and use it there too!
It would surely be easier and result in faster code if you started it at 1 and had a secondary ID field that is calculated as MAX(HHRecId) + ID.