Pervasive SQL reuses a deleted primary key - pervasive

I came across a very odd behavior of PSQL (v11) recently:
If I create a table with an identity column, add some records and then delete the last row, this rows key will be reused!
create table "MyTable" (id identity not null, name varchar(50));
insert into "MyTable" (name) values ('Row 1');
insert into "MyTable" (name) values ('Row 2');
insert into "MyTable" (name) values ('Row 3');
select * from "MyTable"
so far so good
id name
=========== ============
1 Row 1
2 Row 2
3 Row 3
then when I delete "Row 3"
delete from "MyTable" where id = 3;
and add a new row
insert into "MyTable" (name) values ('Row 4');
I am surprised to get
id name
=========== ==============
1 Row 1
2 Row 2
!!! ---> 3 Row 4
Is there a way to change the server configuration to prevent this behavior. I would also be glad for any other suggestion to create a reliably unique integer in pervasive SQL.

As per the Autoincrement documentation (here), this seems to be expected behavior. Specifically, in the documentation, it says:
If you indicate that you want the database engine to assign the next
value by entering a zero (0) value in an insert or update, the
database simply finds the highest number, adds 1, and inserts the
resulting value.
Because the highest number is "2" in the example give, the next autoincrement value will be "3".
The only way to prevent this behavior within autoincrement usage would be to specify the value on insert.
If you really want something unique, you can use the UNIQUEIDENTIFIER described in the documentation here.
Here's an example:
create table "MyTable" (id identity not null, gid UNIQUEIDENTIFIER default newid(), name varchar(50));
insert into "MyTable" (name) values ('Row 1');
insert into "MyTable" (name) values ('Row 2');
insert into "MyTable" (name) values ('Row 3');
select * from "mytable";
delete from "MyTable" where id = 3;
insert into "MyTable" (name) values ('Row 4');
select * from "mytable";
And the results:
<<<<<<<<<<<<<<<<<<<<<<<<
id gid name
=========== ==================================== ==================================================
1 1F74A3E5-6EFC-4382-81DA-94F58710AD73 Row 1
2 B5FEFA7A-F85B-486A-A7CA-9615EAD1A601 Row 2
3 E327B2CF-D01D-4039-BB83-CAD966C72131 Row 3
<<<<<<<<<<<<<<<<<<<<<<<<
id gid name
=========== ==================================== ==================================================
1 1F74A3E5-6EFC-4382-81DA-94F58710AD73 Row 1
2 B5FEFA7A-F85B-486A-A7CA-9615EAD1A601 Row 2
3 66F0E55C-52CF-4183-87EE-2C0FDD6E45B6 Row 4

Related

Is Identity Column hold the duplicate values?

I have the table with the identity column. This is looks like identity column is duplicated. What are all the possibilities that column hold the duplicate values.
Table structure looks like below,
create table Table1(ID INT identity, value varchar(10))
if the column has no unique constraint, it is possible to get duplicate values in it.
One way to do this is like this
SET IDENTITY_INSERT Table1 ON
INSERT INTO Table1 (ID, value)
VALUES (1, 'hello')
SET IDENTITY_INSERT Table1 OFF
Another way is to reseed the table, check the answer of Satheesh on how to do that.
If you dont want that, make this column primary key or create an unique constraint on that column.
how to make a unique index :
CREATE UNIQUE NONCLUSTERED INDEX idx_Table1_ID
ON dbo.Table1(ID)
WHERE ID IS NOT NULL;
The WHERE ID IS NOT NULL; is needed in your example because you allow null values in the ID column, which I do not recommend
I am reasonably sure your table should look like this
Create table Table1 (
ID int identity not null,
Value varchar(10),
constraint PK_Table1_ID primary key (ID)
)
Of course and Identity Column can have Duplicate values.
But since the values are auto-populated by the Table itself, there is no chance that the Duplicates can be created by the system.
But you can add Duplicate values using the INSERT INTO Statement. or if you RESEED the identity column without removing the Existing values the system itself will create Duplicates.
because of The identity column in Not having a UNIQUE constraint by default.
see the below Example
CREATE TABLE Temp
(
SeqNo INT IDENTITY(1,1),
MyStr VARCHAR(10)
)
INSERT INTO Temp
VALUES('A')
Result
SeqNo MyStr
1 A
SET IDENTITY_INSERT TEMP ON
INSERT INTO TEMP(SeqNo,MyStr)
VALUES(1,'B')
SET IDENTITY_INSERT TEMP OFF
Result
SeqNo MyStr
1 A
1 B
Inserted Couple more records
INSERT INTO TEMP
VALUES('C')
INSERT INTO TEMP
VALUES('D')
Result
SeqNo MyStr
1 A
1 B
2 C
3 D
Perform identity Reseed and insert a New value
DBCC CHECKIDENT ('TEMP', RESEED, 1)
INSERT INTO TEMP
VALUES('E')
Final result
SeqNo MyStr
1 A
1 B
2 C
3 D
2 E

Constraint on a column based on values at another column in another table

I have table A which has two columns policy and rule. Table values are as below:
policy | rule
-------------
1 | A
-------------
1 | B
-------------
2 | C
-------------
3 | D
-------------
I have another table B with a column (id).
I want to write a constraint on this table B at column (id) such that if a number of distinct policies in table A are equal to 1, then the value of (id) should always be NULL.
I am using SQL Server and I am looking at a query-based solution (not via GUI steps).
Add CHECK Constraints into the table
create table tableA (policy int , [rule] char(1))
create table tableB (Id int , policy int )
GO
Function to check the constraint
CREATE FUNCTION CheckFnctn(#policy int) -- Adapt if necessary
RETURNS bit
AS
BEGIN
DECLARE #retval bit = 1
IF EXISTS(SELECT TOP 1 1 FROM tableA WHERE policy = #policy and policy = 1)
BEGIN
RETURN 0
END
RETURN #retval
END;
GO
Add the constraint into the table
ALTER TABLE tableB ADD CONSTRAINT ck_id_policy_1 CHECK (dbo.CheckFnctn(policy) = 1)
Test
INSERT tableA Values
(1,'A')
,(1,'B')
,(2,'B')
,(2,'D')
GO
(4 row(s) affected)
INSERT tableB (Id, policy) values (1,2)
GO
(1 row(s) affected)
INSERT tableB (Id, policy) values (1,1)
GO
Msg 547, Level 16, State 0, Line 2
The INSERT statement conflicted with the CHECK constraint "ck_id_policy_1". The conflict occurred in database "XXXX", table "dbo.tableB", column 'policy'.
The statement has been terminated.
If you need to check if policy 1 exists you can never insert an Id different from null. Use this function and constraint
CREATE FUNCTION CheckFnctn(#Id int)
RETURNS bit
AS
BEGIN
DECLARE #retval bit = 1
IF EXISTS(SELECT TOP 1 1 FROM tableA WHERE policy = 1 AND #Id IS NOT NULL)
BEGIN
RETURN 0
END
RETURN #retval
END;
GO
ALTER TABLE tableB ADD CONSTRAINT ck_id_policy_1 CHECK (dbo.CheckFnctn(Id) = 1)
INSERT tableB (Id, policy) values (1,2)
GO
Msg 547, Level 16, State 0, Line 3
The INSERT statement conflicted with the CHECK constraint "ck_id_policy_1". The conflict occurred in database "XXXXX", table "dbo.tableB", column 'Id'.
The statement has been terminated.
INSERT tableB (Id, policy) values (NULL,1)
GO
(1 row(s) affected)

Custom value for Identity Column

Suppose there is a table containing records of students from roll no 1 to 10. If we delete the record at roll number 4&5. Is it possible to enter new records at 4&5? Can anybody please help.
Yes, you can do that using SET IDENTITY_INSERT, given that for the StudentRollID column, IDENTITY(1, 1) is specified. This means that as each row is inserted into the table, SQL Server will automatically increment this value by 1 starting with the number 1.
-- 1 - Retrieve all of the data
-- from the dbo.Student table
SELECT *
FROM dbo.Student;
GO
-- 2 - Delete a single record
DELETE
FROM dbo.Student
WHERE StudentRollID = 4;
GO
-- 3 - Verify the record was deleted
SELECT *
FROM dbo.Student;
GO
-- 4 - Insert the deleted record
-- Insert fails
INSERT INTO [dbo].[Student]
([StudentRollID]
,[FirstName]
,[LastName]
,[CreateDate])
VALUES
(4
,'Bar'
,'Foo'
,'2014-12-04');
GO
-- 5 - Insert the deleted record
-- Insert succeeds
SET IDENTITY_INSERT [dbo].[Student] ON
INSERT INTO [dbo].[Student]
([StudentRollID]
,[FirstName]
,[LastName]
,[CreateDate])
VALUES
(4
,'Bar'
,'Foo'
,'2014-12-04');
SET IDENTITY_INSERT [dbo].[Student] OFF
GO
-- 6 - Verify the data
SELECT *
FROM dbo.Student;
GO

Get inserted table identity value and update another table

I have two tables with foreign key constraint on TableB on TablesAs KeyA column. I was doing manual inserts till now as they were only few rows to be added. Now i need to do a bulk insert, so my question if i insert multiple rows in TableA how can i get all those identity values and insert them into TableB along with other column values. Please see the script below.
INSERT INTO Tablea
([KeyA]
,[Value] )
SELECT 4 ,'StateA'
UNION ALL
SELECT 5 ,'StateB'
UNION ALL
SELECT 6 ,'StateC'
INSERT INTO Tableb
([KeyB]
,[fKeyA] //Get value from the inserted row from TableA
,[Desc])
SELECT 1 ,4,'Value1'
UNION ALL
SELECT 2 ,5,'Value2'
UNION ALL
SELECT 3 ,6, 'Value3'
You can use the OUTPUT clause of INSERT to do this. Here is an example:
CREATE TABLE #temp (id [int] IDENTITY (1, 1) PRIMARY KEY CLUSTERED, Val int)
CREATE TABLE #new (id [int], val int)
INSERT INTO #temp (val) OUTPUT inserted.id, inserted.val INTO #new VALUES (5), (6), (7)
SELECT id, val FROM #new
DROP TABLE #new
DROP TABLE #temp
The result set returned includes the inserted IDENTITY values.
Scope identity sometimes returns incorrect value. See the use of OUTPUT in the workarounds section.

SQL Server Identity Column Insert Fails

Hi guys I just have one quick question:
what happens when an insert statement fails on an identity column?
Is it possible that say for example that if I insert a row with an identity column, that identity column will be 1, and insert again but that fails and does not insert and data. Then try to insert again and that identity for that row is now 3?
Any advice will be much appreciated.
Thanks.
It depends on what the cause of the fail on data insert is. If for example the values are invalid (wrong types), then the identity value won't be incremented. However, if the first insert is successful, but is then removed (by a transaction failed and rolled back), then the identity value IS incremented.
-- Next identity value = 1
INSERT INTO Table1 (
field1)
VALUES ('a')
-- Next identity value = 2
BEGIN TRAN
INSERT INTO Table1 (
field1)
VALUES ('b')
-- Next identity value = 3
ROLLBACK TRAN
-- Next identity value = 3, although the insertion was removed.
INSERT INTO Table1 (
field1)
VALUES ('c')
-- Next identity value = 4
The first insert will have identity column value = 1, the second one fails, and the third one will have identity column value = 3.
Just because a column has an IDENTITY specification doesn't necessarily mean it's unique.
If you don't have a unique constraint (or a primary key constraint) on that column, you can definitely insert multiple identical values into rows for that column.
Typically, though, your IDENTITY columns will be the primary key (or at least have a UNIQUE constraint on them) and in that case, attempting to insert a value that already exists will result in an error ("unique constraint violation" or something like that)
In order to be able to insert specific values into an IDENTITY column you need to have the SET IDENTITY_INSERT (table name) ON - otherwise, SQL Server will prevent you from even specifying values for an IDENTITY column.
For illustration - try this:
-- create demo table, fill with values
CREATE TABLE IdentityTest (ID INT IDENTITY, SomeValue CHAR(1))
INSERT INTO IdentityTest(SomeValue) VALUES('A'), ('B'), ('C')
SELECT * FROM IdentityTest -- Output (1)
-- insert duplicate explicit values into table
SET IDENTITY_INSERT IdentityTest ON
INSERT INTO IdentityTest(ID, SomeValue) VALUES(1, 'Z'), (2, 'Y')
SET IDENTITY_INSERT IdentityTest OFF
SELECT * FROM IdentityTest -- Output (2)
-- add unique constraint
TRUNCATE TABLE dbo.IdentityTest
ALTER TABLE IdentityTest ADD CONSTRAINT UX_ID UNIQUE(ID)
INSERT INTO IdentityTest(SomeValue) VALUES('A'), ('B'), ('C')
SET IDENTITY_INSERT IdentityTest ON
INSERT INTO IdentityTest(ID, SomeValue) VALUES(1, 'Z') -- error message (3)
DROP TABLE IdentityTest
Output (1):
ID SomeValue
1 A
2 B
3 C
Output (2):
ID SomeValue
1 A
2 B
3 C
1 Z
2 Y
Error Message (3):
Msg 2627, Level 14, State 1, Line 9
Violation of UNIQUE KEY constraint 'UX_ID'. Cannot insert duplicate key in object 'dbo.IdentityTest'. The duplicate key value is (1).
One way is to prevent the insert if the row already exists.
IF (Not Exists (Select ID From Table Where SomeCol = #SomeVal)
Insert Into Table Values (#SomeVal)

Resources