Reset IDENTITY column whitout deleting all rows - sql-server

I have the following table
CREATE TABLE [dbo].[MyTable](
[Name] NVARCHAR(200) NOT NULL,
[Surname] NVARCHAR(200) NOT NULL,
[Permanent] [bit] NULL,
[Idx] [bigint] IDENTITY(1,1) NOT NULL
CONSTRAINT [MyTable] PRIMARY KEY CLUSTERED
(
[Idx] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
that contains 1000 rows where [Permanent]=1 and 500.000 rows with [Permanent] NULL
In addition, I have a stored procedure (called MySP) that do the following steps:
DELETE FROM [MyTable] WHERE [Permanent] IS NULL
(lots of calculations)
INSERT results of point 2 into [MyTable]
MySP runs every day so the number of [Idx] increase very quickly as every day 500.000 rows are deleted and inserted.
My target is, during the execution of the SP, to reset the value of column [Idx] to mantain the max number of [Idx] lower possible.
1st try
I have tried to update with the following query at the end of MySP but the system gives me a (correct) error.
UPDATE A
SET A.[Idx]=B.[Idx_new]
FROM [dbo].[MyTable] A
INNER JOIN (
SELECT [Idx],ROW_NUMBER() OVER (ORDER BY [Idx]) AS [Idx_new]
FROM [dbo].[MyTable]
) B
ON A.[Idx]=B.[Idx]
2nd try
After reading the following two questions/answers
Reset identity seed after deleting records in SQL Server
How to update Identity Column in SQL Server?
I have add the following at the end of MySP
DBCC CHECKIDENT ('MyTable', RESEED, 1);
but also this doesn't not work as in [MyTable], differently from the situation of both quesions, remain some rows, so the is a concrete risk that [Idx] is not unique and that's not good as [Idx] is my primary key.
How could I reset the identity column value and also the rows that still remains into [MyTable]?

Using #Killer Queen suggest, I have solved using this snippet of code, which find the MAX([Idx]) of MyTable after the DELETE and the reeseed the identity before the new INSERT.
It works because the rows with [Permanent]=1 are the first rows inserted in the table so their [Idx] values start from 1 and are very low.
DELETE
FROM [MyTable]
WHERE [Permanent]!=1 OR [Permanent] IS NULL
DECLARE #MaxIdx As bigint
SET #MaxIdx = (SELECT ISNULL(MAX([Idx]),0) FROM [MyTable])
DBCC CHECKIDENT ('MyTable', RESEED, #MaxIdx);

Related

Why does a newly-inserted row show up in two filegroups on a partitioned table in SQL Server?

I partitioned an existing table by year. After inserting a new record for a date in 2020, the new record shows as part of the 2020 partition and in the primary filegroup. I'm having a hard time finding if that's what is supposed to happen, or if I have something configured incorrectly.
This is what the partitions look like after inserting the record. Before inserting the record, this query showed 3 records in the primary filegroup, and 3 records in the 2021 group.
I was expecting a new row in the 2020 partition and for the number of rows in the primary filegroup to remain at 3, but perhaps those are wrong expectations.
Does the primary filegroup always show the total of the partitions, or should rows only show up as being in just the partition in which they belong?
I can show how I set this all up, and I'd be happy to, but this question is more about what the results should look like. I don't want the same row in multiple partitions.
Edit
I just found this link. It shows the results of inserting data where new rows only show up in the correct partition and not also in primary. So it appears I've done something incorrectly.
https://www.sqlshack.com/how-to-automate-table-partitioning-in-sql-server/
Edit 2
The reason primary has all 4 rows may be that its upper boundary is null. Since primary already existed on the table, that was never modified. Perhaps I need to modify it?
Edit 3
Interesting that this link also shows the sum of the partitions within the primary.
https://www.mssqltips.com/sqlservertip/2888/how-to-partition-an-existing-sql-server-table/
Script that SSMS generated for partitioning
USE [Sandbox]
GO
BEGIN TRANSACTION
CREATE PARTITION FUNCTION [PunchPF1](datetimeoffset(7)) AS RANGE LEFT FOR VALUES (N'2020-01-01T00:00:00-05:00', N'2021-01-01T00:00:00-05:00', N'2022-01-01T00:00:00-05:00')
CREATE PARTITION SCHEME [PunchPS1] AS PARTITION [PunchPF1] TO ([Pre2020_Punch], [2020_Punch], [2021_Punch], [2022_Punch])
ALTER TABLE [time].[Punch] DROP CONSTRAINT [PK_Punch] WITH ( ONLINE = OFF )
ALTER TABLE [time].[Punch] ADD CONSTRAINT [PK_Punch] PRIMARY KEY NONCLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
CREATE CLUSTERED INDEX [ClusteredIndex_on_PunchPS1_637515095430656187] ON [time].[Punch]
(
[PunchTime]
)WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PunchPS1]([PunchTime])
DROP INDEX [ClusteredIndex_on_PunchPS1_637515095430656187] ON [time].[Punch]
COMMIT TRANSACTION
Edit 4
Here is the same query, but with IndexName and IndexID included.
Edit 5
Things look better now after doing this:
USE [Sandbox]
--------------------------------------------------------------------------------------------------------------------
-- Drop table, scheme, and function
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_NAME = 'Punch' AND TABLE_SCHEMA = 'time')
DROP TABLE time.Punch;
IF EXISTS (SELECT 1 FROM sys.partition_schemes WHERE name = 'PunchPS1')
DROP PARTITION SCHEME PunchPS1
IF EXISTS (SELECT 1 FROM sys.partition_functions WHERE name = 'PunchPF1')
DROP partition function PunchPF1
--------------------------------------------------------------------------------------------------------------------
-- Files and Filegroups
-- Add files and filegroups manually in SSMS because each SQL Server could have different file locations?
-- How to add a file and filegroup using SQL:
-- https://learn.microsoft.com/en-us/sql/t-sql/statements/alter-database-transact-sql-file-and-filegroup-options?view=sql-server-ver15
-- To see an example of creating filegroups in SQL, script the Sandbox DB AS CREATE...
--------------------------------------------------------------------------------------------------------------------
-- Partition Function
CREATE PARTITION FUNCTION [PunchPF1](datetimeoffset(7))
AS RANGE RIGHT FOR VALUES (N'2020-01-01T00:00:00-05:00', N'2021-01-01T00:00:00-05:00', N'2022-01-01T00:00:00-05:00')
--------------------------------------------------------------------------------------------------------------------
-- Partition Scheme
CREATE PARTITION SCHEME [PunchPS1]
AS PARTITION [PunchPF1] TO ([Pre2020_Punch], [2020_Punch], [2021_Punch], [2022_Punch])
--------------------------------------------------------------------------------------------------------------------
-- Table
CREATE TABLE [time].[Punch](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[EmployeeId] [bigint] NOT NULL,
[PunchTime] [datetimeoffset](7) NOT NULL
) ON [PunchPS1] (PunchTime)
--------------------------------------------------------------------------------------------------------------------
-- Add some data
INSERT INTO [time].[Punch] ([EmployeeId],[PunchTime]) VALUES (10,'2020-12-18 16:40:20')
INSERT INTO [time].[Punch] ([EmployeeId],[PunchTime]) VALUES (10,'2020-12-20 16:40:20')
INSERT INTO [time].[Punch] ([EmployeeId],[PunchTime]) VALUES (10,'2020-12-22 16:40:20')
INSERT INTO [time].[Punch] ([EmployeeId],[PunchTime]) VALUES (10,'2021-3-18 16:40:20')
As seen here, the boundaries look good, I'm using RANGE RIGHT, and the rows seem to show in the correct partition.
The only question I have now is if it's bad to have no clustered index?

Adding auto incrementing primary key to an existing table in SQL SERVER 2005

I have a table with 8,000 rows of data and will be adding more. but I forgot to put a primary key in the beginning. so that each row has a unique key. later i added a primary key column. but that column is NULL now.
I want the first row to start at ID 1 and increment all the way up to the last row at ID 8000. How do I update all of the rows with a single query?
i am using SQL Server 2005.
Open Table Design, Add New Column u want Select the column and in Properties In Identity Specification make (Is Identity) Yes..
You can start from where you want by setting the Identity Seed property, by Default it starts from 1.
If you have already Identity Column u can also update it.
Step 1: Remove Identity Specification from Column in Table Design.
Step 2: Use Cursor to Update table Column starting from 1.
Step 3: Again apply Identity Specification on Column in Table Design
Step 4: By query reset Identity Column, from the value u want.
e.g DBCC CHECKIDENT("TableName",Reseed,8000);
so the next identity value will be 8001.
As per your requirement you just needed a sql query that will update the entire table with the new primary key values in an incremental fashion. Here is is :
UPDATE myTable
SET ID = ID + 1
Where ID is the PK field name
Once Updated do not forget to add identity column as shown below :
ALTER TABLE table
ADD ID INT IDENTITY
ALTER TABLE table
ADD CONSTRAINT PK_table
PRIMARY KEY(ID)
Get rid of the column you just added. Then run this
ALTER TABLE table
ADD ID INT IDENTITY
ALTER TABLE table
ADD CONSTRAINT PK_table
PRIMARY KEY(ID)
CREATE TABLE [dbo].[TheTable](
[TheID] [int] IDENTITY(1,1) NOT NULL,
[TheColumn] [nvarchar](100) NOT NULL,
CONSTRAINT [PK_TheTable] PRIMARY KEY CLUSTERED
(
[TheID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
INSERT INTO [dbo].[TheTable]
( [TheColumn] )
SELECT 'one'
UNION
SELECT 'two'
SELECT * FROM [dbo].[TheTable] TT
You can update table based on unique key.
Declare #i int=1
While(#i <=(select count(*) from table))
Begin
Update table
Set primary_key_column=#i
Where
Set#i=#i+1
End

Why would this table allow a PK id of 0?

In SQL Server 2008 R2, I have a table that allows 0 as a foreign key. This is odd because a primary key constraint is specified, and the IDENTITY(1,1) descriptor is also used.
When the first record is inserted to the table, it's PK (RegionID) is 0.
I don't have IDENTITY-INSERT turned ON when I am doing an insert. (Normal operation)
Here's the table definition:
CREATE TABLE [dbo].[tdfRegions](
[RegionID] [int] IDENTITY(1,1) NOT NULL,
[RegionName] [nvarchar](50) NOT NULL,
[RegionDescription] [nvarchar](50) NOT NULL,
[Active] [bit] NOT NULL,
CONSTRAINT [PK_tdfRegions] PRIMARY KEY CLUSTERED
(
[RegionID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
I have been acting under the assumption that the first record inserted to this table would have a RegionID of 1.
INSERT INTO
tdfRegions (RegionName, RegionDescription, Active)
VALUES
('test','test', 1)
Produces:
RegionID RegionName RegionDescription Active
0 test test 1
Why might this be happening?
EDIT:
Ok, I've got a little more background information here. Someone ran a
EXEC sp_MSForEachTable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'
GO
EXEC sp_MSForEachTable 'DELETE FROM ?'
GO
EXEC sp_MSForEachTable 'ALTER TABLE ? CHECK CONSTRAINT ALL'
GO
To clear the database when it was first created. Could this have been responsible?
The only reason it you could end up with 0 in the RegionID is --
when you truncate the table and reseed the identity to 0 using below command
DBCC CHECKIDENT('tdfRegions', RESEED, 0)
If you then insert into the table with your insert block, it will be RegionID = 0.
The ways I know to reset the PK are:a) use truncateBut that would put the see back to 1b) use something like:DBCC CHECKIDENT (MyTable, RESEED, 1)If you used a 0 instead of a 1 in the last statement, that would get the seed set to 0.Someother people talking about this.
http://social.msdn.microsoft.com/Forums/en-US/sqldatabaseengine/thread/ca0db3d2-e3ae-46ce-b8f0-bfc3bf95a509/

How to speed up Xpath performance in SQL Server when searching for an element with specific text

I am supposed to remove whole rows and part of XML-documents from a table with an XML column based on a specific value in the XML column. However the table contains millions of rows and gets locked when I perform the operation. Currently it will take almost a week to clean it up, and the system is too critical to be taken offline for so long.
Are there any ways to optimize the xpath expressions in this script:
declare #slutdato datetime = '2012-03-01 00:00:00.000'
declare #startdato datetime = '2000-02-01 00:00:00.000'
declare #lev varchar(20) = 'suppliername'
declare #todelete varchar(10) = '~~~~~~~~~~'
CREATE TABLE #ids (selId int NOT NULL PRIMARY KEY)
INSERT into #ids
select id from dbo.proevesvar
WHERE leverandoer = #lev
and proevedato <= #slutdato
and proevedato >= #startdato
begin transaction /* delete whole rows */
delete from dbo.proevesvar
where id in (select selId from #ids)
and ProeveSvarXml.exist('/LaboratoryReport/LaboratoryResults/Result[Value=sql:variable(''#todelete'')]') = 1
and Proevesvarxml.exist('/LaboratoryReport/LaboratoryResults/Result[Value!=sql:variable(''#todelete'')]') = 0
commit
go
begin transaction /* delete single results */
UPDATE dbo.proevesvar SET ProeveSvarXml.modify('delete /LaboratoryReport/LaboratoryResults/Result[Value=sql:variable(''#todelete'')]')
where id in (select selId from #ids)
commit
go
The table definitions is:
CREATE TABLE [dbo].[ProeveSvar](
[ID] [int] IDENTITY(1,1) NOT NULL,
[CPRnr] [nchar](10) NOT NULL,
[ProeveDato] [datetime] NOT NULL,
[ProeveSvarXml] [xml] NOT NULL,
[Leverandoer] [nvarchar](50) NOT NULL,
[Proevenr] [nvarchar](50) NOT NULL,
[Lokationsnr] [nchar](13) NOT NULL,
[Modtaget] [datetime] NOT NULL,
CONSTRAINT [PK_ProeveSvar] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [IX_ProeveSvar_1] UNIQUE NONCLUSTERED
(
[CPRnr] ASC,
[Lokationsnr] ASC,
[Proevenr] ASC,
[ProeveDato] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
The first insert statement is very fast. I believe I can handle the locking by committing 50 rows at a time, so other requests can be handled in between my transactions.
The total number of rows for this supplier is about 5.5 million and the total rowcount in the table is around 13 million.
I've not really used xpath within SQL server before, but something which stands out is that you're doing lots of reads and writes in the same command (in the second statement). If possible, change your queries to..
CREATE TABLE #ids (selId int NOT NULL PRIMARY KEY)
INSERT into #ids
select id from dbo.proevesvar
WHERE leverandoer = #lev
and proevedato <= #slutdato
and proevedato >= #startdato
and ProeveSvarXml.exist('/LaboratoryReport/LaboratoryResults/Result[Value=sql:variable(''#todelete'')]') = 1
and Proevesvarxml.exist('/LaboratoryReport/LaboratoryResults/Result[Value!=sql:variable(''#todelete'')]') = 0
begin transaction /* delete whole rows */
delete from dbo.proevesvar
where id in (select selId from #ids)
This means that the first query will only create the new temporary table, and not write anything back, which will take slightly longer than your original, but the key thing is that your second query will ONLY be deleting records based on what's in your temporary table.
What you'll probably find is because it's deleting records, it's constantly re-building indices, and causing the reads to also be slower.
I'd also delete/disable any indices/constraints that don't actually help your query run.
Also, you're creating your clustered primary key on the ID, which isn't always the best thing to do. Especially if you're doing lots of date scans.
Can you also view the estimated execution plan for the top query, it would be interesting to see the order in which it checks the conditions. If it's doing the date first, then that's fine, but if it's doing the xpath before it checks the date, you might have to separte it into 3 queries, or add a new clustered index on 'proevedato,id'. This should force the query to only run the xpath for records which actually match the date.
Hope this helps.

SQL Server Column Display Order

When you create a table via scripts it appears to just list the columns in order of creation. It's a minor annoyance, but I like a certain method to the madness. When I later add a column with ALTER TABLE, how do I make it show up in a specific place when viewing the table in interactive tools such as enterprise manager?
ex:
Table Foo
---------
FooID
BarID
Name
Address
Worth
I want to
ALTER TABLE Foo
ADD BazID INT NULL
and have BazID listed between BarID and Name when I use Management Studio.
You can't do that - a SQL Server table really doesn't know anything about order of columns - there is no order in the tuple algebra that's the foundation of the relational database systems.
What SQL Server Management Studio shows you is just its own display "optimization" - you can change it there, but the standard SQL Data Definition Language (DDL) statement have no notion of "column order" - you cannot ALTER a table to move a column to a different position.
If you change the column display order in SSMS, what it'll do in the background is rename the old table to a temporary name, create the new table with the columns in the order you specified, and then copy around the data. That's why modifying the column order on a large table can take almost forever - it's not just a simple little DDL statement to be executed - it's a major undertaking.
Marc
The way the sql server management studio and other big name tools like redgate do it, is to make a new temp table, copy the information over, then drop the old table (constraints & unique indexes first), rename the temp table to the old table name, then re-add any constraints and indexes. You really can't re-order what's already there.
Here's an example:
-- here's a sales table
CREATE TABLE [dbo].[Sales](
[SalesId] [int] IDENTITY(1,1) NOT NULL,
[Month] [int] NOT NULL,
[Year] [int] NOT NULL,
[InvoiceAmount] [decimal](15, 2) NOT NULL,
[SalesRepId] [int] NOT NULL,
[BranchId] [int] NOT NULL,
CONSTRAINT [PK_Sales] PRIMARY KEY CLUSTERED
(
[SalesId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
--Here's the sales table adding a column called description between SalesId & Month
BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Tmp_Sales
(
SalesId int NOT NULL IDENTITY (1, 1),
Description varchar(MAX) NULL,
Month int NOT NULL,
Year int NOT NULL,
InvoiceAmount decimal(15, 2) NOT NULL,
SalesRepId int NOT NULL,
BranchId int NOT NULL
) ON [PRIMARY]
TEXTIMAGE_ON [PRIMARY]
GO
SET IDENTITY_INSERT dbo.Tmp_Sales ON
GO
IF EXISTS(SELECT * FROM dbo.Sales)
EXEC('INSERT INTO dbo.Tmp_Sales (SalesId, Month, Year, InvoiceAmount, SalesRepId, BranchId)
SELECT SalesId, Month, Year, InvoiceAmount, SalesRepId, BranchId FROM dbo.Sales WITH (HOLDLOCK TABLOCKX)')
GO
SET IDENTITY_INSERT dbo.Tmp_Sales OFF
GO
DROP TABLE dbo.Sales
GO
EXECUTE sp_rename N'dbo.Tmp_Sales', N'Sales', 'OBJECT'
GO
ALTER TABLE dbo.Sales ADD CONSTRAINT
PK_Sales PRIMARY KEY CLUSTERED
(
SalesId
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
COMMIT

Resources