If my SQLite database has 5 rows and 1st column as autoincremented field and then I deleted the 3rd row then how can I update the value of primary key of all rows in a autoincremented form starting from one.
Initial table:
Table after deletion of row
but I want something like this:-
How can I achieve this result using SQLite queries.
Although the manipulation of Primary Keys is not recommended what you want to achieve can be achieved.
Assuming that by saying "1st column as autoincremented field" that the pk column is defined with the AUTOINCREMENT keyword rather than just an alias of the rowid (which autoincrements anyway see SQLite - AUTOINCREMENT).
Then you could using SQL such as the following (again not recommended):-
/* Create and populate the test table */
DROP TABLE IF EXISTS mytable;
CREATE TABLE IF NOT EXISTS mytable (pk INTEGER PRIMARY KEY AUTOINCREMENT, set_name_fk TEXT, question TEXT);
INSERT INTO mytable VALUES(1,'A','Question0'),(2,'B','Question1'),(3,'C','Question2'),(4,'D','Question3'),(5,'E','Question4');
/* Do the deletion */
DELETE FROM mytable WHERE question = 'Question2';
/* Start the manipulation */
/* Just in case it exists DROP the temporary table */
DROP TABLE IF EXISTS new_othermytable;
/* Create the temporary table to maintain the column definitions */
CREATE TABLE IF NOT EXISTS new_othermytable (pk INTEGER PRIMARY KEY AUTOINCREMENT, set_name_fk TEXT, question TEXT);
/* Populate the temporary table manipulating the pk value accordingly */
INSERT INTO new_othermytable SELECT coalesce((SELECT count(pk) FROM mytable WHERE pk < x.pk),0) + 1 AS pk, set_name_fk, question FROM mytable AS x ORDER BY pk;
/* DROP the original table */
DROP TABLE IF EXISTS mytable;
/* Rename the temporary table to be the original table */
ALTER TABLE new_othermytable RENAME TO mytable;
SELECT * FROM mytable;
The crux of the above is the query that selects the count of the rows with a lower pk value than the current row (coalesce sets this to 0 if there are no rows) and then adds 1, thus effectively renumbering the pk i.e. SELECT coalesce((SELECT count(pk) FROM mytable WHERE pk < x.pk),0) + 1
The query could be used to get the desired output without having to change the table.
Related
I have a table where I've merged 2 tables into one.
One of the tables had an ID (primary key).
Now I got a merged table where some of the ID is 0.
I now try to restore and fill out the 0 with AUTO_INCREMENT so I
get a table with unique numbers (and not lose the one already there )
Someone got a god solution here ?
Firstly, the fact that you have a bunch of 0's in the table implies 2 additional problems:
The "ID" column is not a Primary Key or dose not have a Unique Index on it; meaning that duplicates were inserted
The column is (likely) no longer an IDENTITY.
Firstly, You'll need to get the new values in there. This can be done with an updatable CTE, with ROW_NUMBER and a windowed MAX
First some sample data:
CREATE TABLE dbo.TestTable (ID int NOT NULL);
INSERT INTO dbo.TestTable (ID)
VALUES(1),(2),(3),(0),(0),(0);
And now to UPDATE the rows with 0:
WITH RNs AS(
SELECT ID,
ROW_NUMBER() OVER (PARTITION BY CASE ID WHEN 0 THEN 0 ELSE 1 END ORDER BY (SELECT NULL)) + --If you have a way of determining the order, change the ORDER BY
MAX(ID) OVER () AS [NewID]
FROM dbo.TestTable)
UPDATE RNs
SET ID = [NewID]
WHERE ID = 0;
Now we (probably) need to fix the table and get the IDENTITY column in there. You can't change a column to an IDENTITY, so we'll need to create a new one and ensure it follows the value of the existing ID.
First, therefore, we need to add a CLUSTERED index to the table, so that the new IDENTITY will use that to generate its value:
ALTER TABLE dbo.TestTable ADD CONSTRAINT PK_TestTable PRIMARY KEY CLUSTERED (ID);
Now we can add the new IDENTITY column:
ALTER TABLE dbo.TestTable ADD IdentityID int IDENTITY NOT NULL;
Then we need to DROP the Primary Key we just created, and then the old column:
ALTER TABLE dbo.TestTable DROP CONSTRAINT PK_TestTable ;
ALTER TABLE dbo.TestTable DROP COLUMN ID;
And then, finally, we can rename the new column, and then recreate the Primary Key:
EXEC sys.sp_rename N'dbo.TestTable.IdentityID','ID','COLUMN';
GO
ALTER TABLE dbo.TestTable ADD CONSTRAINT PK_TestTable PRIMARY KEY CLUSTERED (ID);
Im trying to update a table I have the same Data but with different ID's so i would like to set the ID of both communs to the lowest ID register for the results.
UPDATE TABLENAME
SET EXAMPLEID = LOWER(EXAMPLEID)
WHERE
TID = TID
AND
KID = KID
AND
STREET = STREET
I'm getting the following error:
Msg 8102, Level 16, State 1, Line 1 Cannot update identity column
'EXAMPLEID'
Identity Column is generally used with Primary Key column. In your case if ExampleID is your primary key and also identity Column, You cannot have same ExampleID on two different rows.
Primary Key Column is unique for every row
On the other hand If your column is not PK but Identity Column, then SQL Server does not allow you to update Identity Key Column Value.
But there is a dirty workaround alternative for this (Not Recommended)
You can't update an identity column. You may insert new records with an explicit value using IDENTITY_INSERT, but SQL Server won't let you do an update.
If you really need to do this, the only option you have is to copy the full table temporarily and recreate your final table again with the updated values. This is strongly NOT recommended:
Create a copy of your table, with all related objects (indexes, constraints, etc.), but with no rows (only schema objects).
CREATE TABLE TABLENAME_Mirror (
ExampleID INT IDENTITY,
TID VARCHAR(100),
KID VARCHAR(100),
STREET VARCHAR(100))
Set IDENTITY_INSERT ON on this new table and insert the records with the updated values.
SET IDENTITY_INSERT TABLENAME_Mirror ON
INSERT INTO TABLENAME_Mirror (
ExampleID,
TID,
KID,
STREET)
SELECT
/*Updated values*/
FROM
--....
SET IDENTITY_INSERT TABLENAME_Mirror OFF
Drop the original table and rename the copied one to the original name:
BEGIN TRANSACTION
IF OBJECT_ID('dbo.TABLENAME') is not null
DROP TABLE dbo.TABLENAME
EXEC sys.sp_rename
'dbo.TABLENAME_Mirror',
'TABLENAME'
COMMIT
You might need to reseed the identity with a proper value once the rows are inserted, if you want to keep the same seed as before.
I'm grabbing some rows from a table, manipulating them in a temp table, and then looking to insert them as new rows into my original table.
However, I'm running into an issue with the identity column, even when I don't have the identity column on my temp table. The identity column is an auto-incrementing int.
This seems like a simple thing I'm way overthinking.
select top 0 *
into #TestTable
from OriginalTable;
...
--insert and manipulate records
...
ALTER TABLE #TestTable
DROP COLUMN MyIdentityColumn;
DECLARE #InsertedRows TABLE (NewSeqNum INT);
INSERT INTO OriginalTable
OUTPUT MyIdentityColumn INTO #InsertedRows(NewSeqNum)
SELECT * FROM #TestTable
but I get this error:
An explicit value for the identity column in table 'OriginalTable' can only be specified when a column list is used and IDENTITY_INSERT is ON.
I absolutely do not want to set an explicit value, I want it to insert and give me the new identity (via #InsertedRows)
If you don't want to keep the id of inserted records, then you need to specify all your columns but the id column in the select. As general good practice, dont select *, always specify the columns you want to retrieve-insert.
INSERT INTO OriginalTable (col1, col2, col3...)
OUTPUT MyIdentityColumn INTO #InsertedRows(NewSeqNum)
SELECT (col1, col2, col3...) FROM #TestTable
If I'm understanding you, I think your problem is that you're trying to insert '*' into the original table - which means all of your columns from the temp table. Including your ID column (which you don't want to insert, because you're wanting it to auto-generate.)
Instead, I'd suggest doing something like this:
Select [ColumnB],[ColumnC],[ColumnD],[Etc] into your temp table
Select [ColumnB],[ColumnC],[ColumnD],[Etc] into your original table.
... aka, spell out the columns explicitly, and omit the Identity column.
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.
I have a table table1 in SQL server 2008 and it has records in it.
I want the primary key table1_Sno column to be an auto-incrementing column. Can this be done without any data transfer or cloning of table?
I know that I can use ALTER TABLE to add an auto-increment column, but can I simply add the AUTO_INCREMENT option to an existing column that is the primary key?
Changing the IDENTITY property is really a metadata only change. But to update the metadata directly requires starting the instance in single user mode and messing around with some columns in sys.syscolpars and is undocumented/unsupported and not something I would recommend or will give any additional details about.
For people coming across this answer on SQL Server 2012+ by far the easiest way of achieving this result of an auto incrementing column would be to create a SEQUENCE object and set the next value for seq as the column default.
Alternatively, or for previous versions (from 2005 onwards), the workaround posted on this connect item shows a completely supported way of doing this without any need for size of data operations using ALTER TABLE...SWITCH. Also blogged about on MSDN here. Though the code to achieve this is not very simple and there are restrictions - such as the table being changed can't be the target of a foreign key constraint.
Example code.
Set up test table with no identity column.
CREATE TABLE dbo.tblFoo
(
bar INT PRIMARY KEY,
filler CHAR(8000),
filler2 CHAR(49)
)
INSERT INTO dbo.tblFoo (bar)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT 0))
FROM master..spt_values v1, master..spt_values v2
Alter it to have an identity column (more or less instant).
BEGIN TRY;
BEGIN TRANSACTION;
/*Using DBCC CHECKIDENT('dbo.tblFoo') is slow so use dynamic SQL to
set the correct seed in the table definition instead*/
DECLARE #TableScript nvarchar(max)
SELECT #TableScript =
'
CREATE TABLE dbo.Destination(
bar INT IDENTITY(' +
CAST(ISNULL(MAX(bar),0)+1 AS VARCHAR) + ',1) PRIMARY KEY,
filler CHAR(8000),
filler2 CHAR(49)
)
ALTER TABLE dbo.tblFoo SWITCH TO dbo.Destination;
'
FROM dbo.tblFoo
WITH (TABLOCKX,HOLDLOCK)
EXEC(#TableScript)
DROP TABLE dbo.tblFoo;
EXECUTE sp_rename N'dbo.Destination', N'tblFoo', 'OBJECT';
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 ROLLBACK TRANSACTION;
PRINT ERROR_MESSAGE();
END CATCH;
Test the result.
INSERT INTO dbo.tblFoo (filler,filler2)
OUTPUT inserted.*
VALUES ('foo','bar')
Gives
bar filler filler2
----------- --------- ---------
10001 foo bar
Clean up
DROP TABLE dbo.tblFoo
SQL Server: How to set auto-increment on a table with rows in it:
This strategy physically copies the rows around twice which can take a much longer time if the table you are copying is very large.
You could save out your data, drop and rebuild the table with the auto-increment and primary key, then load the data back in.
I'll walk you through with an example:
Step 1, create table foobar (without primary key or auto-increment):
CREATE TABLE foobar(
id int NOT NULL,
name nchar(100) NOT NULL,
)
Step 2, insert some rows
insert into foobar values(1, 'one');
insert into foobar values(2, 'two');
insert into foobar values(3, 'three');
Step 3, copy out foobar data into a temp table:
select * into temp_foobar from foobar
Step 4, drop table foobar:
drop table foobar;
Step 5, recreate your table with the primary key and auto-increment properties:
CREATE TABLE foobar(
id int primary key IDENTITY(1, 1) NOT NULL,
name nchar(100) NOT NULL,
)
Step 6, insert your data from temp table back into foobar
SET IDENTITY_INSERT temp_foobar ON
INSERT into foobar (id, name) select id, name from temp_foobar;
Step 7, drop your temp table, and check to see if it worked:
drop table temp_foobar;
select * from foobar;
You should get this, and when you inspect the foobar table, the id column is auto-increment of 1 and id is a primary key:
1 one
2 two
3 three
If you want to do this via the designer you can do it by following the instructions here "Save changes is not permitted" when changing an existing column to be nullable
Yes, you can. Go to Tools > Designers > Table and Designers and uncheck "Prevent Saving Changes That Prevent Table Recreation".
No, you can not add an auto increment option to an existing column with data, I think the option which you mentioned is the best.
Have a look here.
If you don't want to add a new column, and you can guarantee that your current int column is unique, you could select all of the data out into a temporary table, drop the table and recreate with the IDENTITY column specified. Then using SET IDENTITY INSERT ON you can insert all of your data in the temporary table into the new table.
Below script can be a good solution.Worked in large data as well.
ALTER DATABASE WMlive SET RECOVERY SIMPLE WITH NO_WAIT
ALTER TABLE WMBOMTABLE DROP CONSTRAINT PK_WMBomTable
ALTER TABLE WMBOMTABLE drop column BOMID
ALTER TABLE WMBOMTABLE ADD BomID int IDENTITY(1, 1) NOT NULL;
ALTER TABLE WMBOMTABLE ADD CONSTRAINT PK_WMBomTable PRIMARY KEY CLUSTERED (BomID);
ALTER DATABASE WMlive SET RECOVERY FULL WITH NO_WAIT