How to reuse itemID after being deleted - c#

Sorry not good in english, hope you all can understand and please help.
I have unique identity for each item.
In the program, it can only have 200 itemsID
For instance, when the user create the first item, the database will assign itemID 1 to the first item, itemID 2 to second item till itemID 200.
User can delete the items. For instance, user delete from itemID 1 to itemID 20. So when user create a new item, i want to reuse the itemID 1, may i know how can i do about it?
For fresh start, whenever user create new item, i will check database for the max item id then retrieve it and add 1.
mySql_ = "SELECT MAX(ITEM_ID) FROM ITEM";
Object returnValue = ExecuteSql(mySql_);
if (DBNull.Value.Equals(returnValue))
{
ItemId += 1;
}
My table design structure is as follow
CREATE TABLE ITEM (
ID NOT NULL PRIMARY KEY
ITEMID NOT NULL
ITEM_NAME
)
The above will be my table structure. I only allowed to have maximum itemID of 200. For example, user have created 200 items (table will have itemID 1 to 200), So if user want to create more items, they delete from itemID 1 to itemID20. So the next time when user create item, the new item insert to table will be itemID1.

If you are using SQl server and Identity. Identity fields do not reuse old values by default. You can reseed them with DBCC CHECKIDENT (orders, RESEED, 9)
That means you'll have to run the statement based on every DELETE.

It is generally not a good idea to handle IDs yourself. Let SQL Server do this withan identity column. You especially shouldn't reuse IDs after delete if your ID is the orimary key. That will degrade insert and index performance. It is always most efficient to add primary key values at the end of the range.
Especially to query your MAX(ID) is not efficient. If you want to reuse IDs without compromising good DB design, you can create a table that contains your ID pool and link with a foreign key:
CREATE TABLE IDPool (
ID TINYINT NOT NULL PRIMARY KEY
--More columns if you need them
)
This table contains one entry for each reusable ID, so in your case 200 entries with ID 1 to 200
Your item would look somehow like this:
CREATE TABLE Items (
ID INT NOT NULL IDENTITY PRIMARY KEY,
PooledID TINYINT NOT NULL FOREIGN KEY REFERENCES IDPool(ID)
--Your other columns
)
You can insert new items like this:
INSERT INTO Items (PooledID)
VALUES ((SELECT MIN(ID) FROM IDPool p WHERE NOT EXISTS (
SELECT * FROM Items i WHERE p.PooledID = i.ID
)))

I don't think that your approach is good. You should use identity field which is always unique and don't use old id's anyway. But if you really want to use your approach try to create one more attribute in database. Let's say Hidden field.
When user delete record in application you just update this row and set it as Hidden (Hidden=1).
When user create new record you again update this record and set it to visible (Hidden=0).
The you can build application based on this filed and you can show/hide/work with records in base of Hidden attribute.

Related

How to keep ID from increasing and use first available unused ID instead?

When I delete an Item from my table I want that when adding next Item in that table that item should use the first available unused ID. How can i achieve this? When I deleted all of the Items and created new ones this happened:
In this case it would be much better that Item Id 21 was 1.
I would recommend against modifying (what looks like) a primary key column. As an example of side effects: if other entities are referencing the primary column, this will fail, or break the relations. Also, you potentially need to renumber the whole table for every delete that is executed.
If you want a dynamic auto-incremented number, you can use row_number() in a view:
create view myview as
select
row_number() over(order by item_id) item_id,
title,
description
from mytable
You can then query the view instead of the table, which gives you an always up-to-date increment number.
You mean you want to rearrange the Autoincerment ID back to 1? I believe this will solve it Reorder / reset auto increment primary key

How can I make Entity Framework's generated INSERT statement to include a database generated IDENTITY column?

This is Entity Framework 6.1.3
The SQL Server table has a two-column composite key.
ID INT Identity(1,1) NOT NULL
VERSION INT NOT NULL
Inserting a new record works because I don't set the ID on my object; I only set the VERSION.
So a new record would look like this:
ID VERSION
1 1
Perfect! The database generates the ID because the column is configured with Identity and my model is decorated with [DatabaseGenerated(DatabaseGeneratedOption.Identity)].
But now I need to insert another row with the same ID but a different VERSION; hence the composite key. So I would expect the second row to be:
ID Version
1 1
1 2 <- second row has same ID and different version
I do need this to work both ways because there is the scenario where a new ID should be auto-generated by the database, and the other scenario where I have the same ID but a different VERSION.
The Problem:
Because my Code-First model has the ID configured with DatabaseGeneratedOption.Identity, when I set the ID property on my object, my SaveChanges generates the insert statement without the ID!
(Diagnostic Tools in VS shows that Entity Framework generated this statement)
ADO.NET: Execute Reader "INSERT [dbo].[T1]([Version], ... VALUES (#0, ...)
Note the omission of ID. Because I explicitly set the ID on my object, I expected to see this statement instead.
INSERT [dbo].[T1]([ID], [Version], ... VALUES (#0, #1, ...)
That's what I'm trying to accomplish.
The question is:
How can I make Entity Framework include that ID column in its generated insert statement in an elegant way?
I don't want to use a stored procedure or hard code a SQL statement or hack the insert statement by 'squeezing in' the column.
If there is no way, I know that I would have to remove the use of Identity altogether and define my own IDs, which I'm trying to avoid.
Also, my SaveChanges() already makes use of SET IDENTITY_INSERT ON/OFF so that isn't any problem.
Here is the relevant part of my model: (I omitted other properties)
[Key, Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Key, Column(Order = 1)]
public int VERSION { get; set; }
One avenue that I've explored was to reset my DbContext with a twist in OnModelCreating, but that didn't make a difference.
Of course, in that revision I did remove the DatabaseGenerated decorator off my ID property in the class. I inserted this into OnModelCreating:
if (this.AllowIdentityInsert)
{
modelBuilder.Entity<T1>().Property(x => x.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
else
{
modelBuilder.Entity<T1>().Property(x => x.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
If I could successfully control the model by changing the ID property to DatabaseGeneratedOption to None before my SaveChanges, then this could work and be an elegant solution.
Has anyone run into this situation and found a good solution?
Thanks so much for your input or suggestions.
Generally you don't want to use an identity column in that manner but I suppose if you are using a composite key you could. The problem that you will be faced with to insert your second record is that you will have to turn IDENTITY_INSERT on and off. So thinking of the SQL of it here is an example to show you what has to be done to accomplish the task.
IF OBJECT_ID('tempdb..#TblName') IS NOT NULL
BEGIN
DROP TABLE #TblName
END
CREATE TABLE #TblName (
ID INT IDENTITY(1,1) NOT NULL, Version INT NOT NULL
)
INSERT INTO #TblName (Version) VALUES (1)
SET IDENTITY_INSERT #TblName ON
INSERT INTO #TblName (ID, Version) VALUES (1,2)
SET IDENTITY_INSERT #TblName OFF
SELECT *
FROM
#TblName
A more typical design is to actually maintain a log table via a trigger and store the history in it. Because in that table it wouldn't need the identity column simply another INT.
There are a few more 2 table designs to get around the limitation but you may also want to look into creating SQL SEQUENCE https://msdn.microsoft.com/en-us/library/ff878058.aspx and instead of using IDENTITY on the ID column retrieving a SEQUENCE when you need it and always inserting the value. If you use a SEQUENCE you get the added benefit of being able to add another IDENTITY column that will be a local table ID which is typically recommended rather than relying solely on the composite key.
Okay here is (to me) a very interesting way of doing getting around your IDENTITY issue and maintaining a "incremented version". You can use an Update able View instead of directly using your table. You would use 2 SEQUENCES one for ID and one for VersionId and then to get Version you would use ROW_NUMBER() in the view. You could expand this solution by adding INSTEAD OF INSERT/UPDATE trigger to handle setting of the IDS more automatically but I don't generally like triggers. Anyway, here is to me an interesting solution:
CREATE TABLE dbo.yourTable (
TableId INT NOT NULL IDENTITY(1,1)
,Id INT NOT NULL
,VersionId INT NOT NULL
,Col VARCHAR(100) NOT NULL
,PRIMARY KEY (Id, VersionId)
)
GO
CREATE SEQUENCE dbo.SEQ_yourTableIdBy1
START WITH 1
INCREMENT BY 1;
GO
CREATE SEQUENCE dbo.SEQ_yourTableVersionIdBy1
START WITH 1
INCREMENT BY 1;
GO
CREATE VIEW dbo.yourTable_v
AS
SELECT
Id
,VersionId
,Version = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY VersionId)
,Col
,LatestVersion = CASE
WHEN ROW_NUMBER() OVER (PARTITION BY Id ORDER BY VersionId DESC) = 1
THEN 1 ELSE 0 END
FROM
dbo.yourTable
GO
--New Record
INSERT INTO dbo.yourTable_v(Id, VersionId, Col)
VALUES (NEXT VALUE FOR dbo.SEQ_yourTableIdBy1, NEXT VALUE FOR dbo.SEQ_yourTableVersionIdBy1, 'A')
SELECT * FROM dbo.yourTable_v
--Change To Existing Record
INSERT INTO dbo.yourTable_v(Id, VersionId, Col)
VALUES (1, NEXT VALUE FOR dbo.SEQ_yourTableVersionIdBy1, 'B')
SELECT * FROM dbo.yourTable_v
link showing how it works http://rextester.com/GBHG23338
To Make Entity Framework believe the view is a table you may need to change the Key definition and the Entity Type here is a msdn blog on the subject. https://blogs.msdn.microsoft.com/alexj/2009/09/01/tip-34-how-to-work-with-updatable-views/
Benefits:
this isn't going to break if 2 people try to submit simultaneously etc.
Entity Framework will think this is a normal table once you fake it out slightly by following the link.
While it will have VersionId incremented across all records it will still present a nice Version + 1 for use in your application.
And you can easily add a latest version column for use in your application/queries.

LINQ to SQL handle autoincremented primary key

I'm trying store data in a local database(SQL CE). I've set a property of RowObject like this:
[Column(IsPrimaryKey=true,CanBeNull=false,IsDbGenerated=true,AutoSync=AutoSync.OnInsert,DbType="int NOT NULL IDENTITY")]
public int key
{ get; set; }
and the primary key is automatically auto-incremented when I insert a new record. But if I remove one or more record and then I insert a new record, its primary key value corresponds at the primary key value of the last inserted record plus one, although that record was deleted.
I know it is not possible to update all primary keys. But, is it possible to control the increment of the primary key?and avoid the continuous increment of the primary key?
ok, thank you for your answer. I ask because I'm worried that PK reaches its maximum value. Maybe this isn't a really problem for my project, but I want understand how handle it, in case it happens. And in case, if it is possible, how can I understand and handle if the database is completely full.
that's violating auto increment.
I sometimes due to need disable auto incrementation of a column to add a row which is deleted on the old place and then enable it again.
If you insist on doing so, you can add 2 triggers, in the delete trigger disable column's auto increment behaviour, then enable it again on another trigger which is after insert trigger.
So on delete disable it, on next insert add it with the max(ID)+1 and after insert enable it again.
For example:
-------------Person Table--------------
ID[Auto-Increment]----Name----Age
10000-------------------Jack-------21
10001-------------------Shawn----19
10002-------------------Albert-----33
Delete from Person where ID = 10002;
set identity_insert AAA ON
now the next ID will be 10003 but you want 10002
DECLARE #ID int;
select #ID = max(ID)+1 from Person
Insert into Person(ID,Name,Age) values(#ID,'Mahdi',23)
set identity_insert AAA OFF --Auto Increments from now on
you should just check to see if you should insert your record with/without ID, I put that burden on your shoulder.
GOOD LUCK.

How to update autoincremented id when delete row in table?

I am creating application that uses MYSQL database in C#. I want to delete row and update autoincremented value of id in table. For example, I have table with two columns: id and station, and table is station list. Something like this
id station
1 pt1
2 pt2
3 pt3
If i delete second row, after deleting the table looks something like this:
id station
1 pt1
3 pt3
Is there any way that I update id of table, for this example that id in third row instead value 3 have value 2?
Thanks in advance!
An autoincrement column, by definition, should not be changed manually.
What happen if some other tables use this ID (3) as foreign key to refer to that record in this table? That table should be changed accordingly.
(Think about it, in your example is simple, but what happen if you delete ID = 2 in a table where the max(ID) is 100000? How many updates in the main table and in the referring tables?)
And in the end there is no real problem if you have gaps in your numbering.
I suggest you don't do anything special when a row is deleted. Yes you will have gaps in the ids, but why do you care? It is just an id.
If you change the value of id_station, you would also need to update the value in all tables that have an id_station field. It causes more unnecessary UPDATES.
The only way to change the value of the id column in other rows is with an UPDATE statement. There is no builtin mechanism to accomplish what you want.
I concur with the other answers here; normally, we do not change the value of an id column in other rows when a row is deleted. Normally, that id column is a primary key, and ideally, that primary key value is immutable (it is assigned once and it doesn't change.) If it does change, then any references to it will also need to change. (The ON UPDATE CASCADE for a foreign key will propagate the change to a child table, for storage engines like InnoDB that support foreign keys, but not with MyISAM.
Basically, changing an id value causes way more problems than it solves.
There is no "automatic" mechanism that changes the value of a column in other rows when a row is deleted.
With that said, there are times in the development cycle where I have had "static" data, and I wanted control over the id values, and I have made changes to id values. But this
is an administrative exercise, not a function performed by an application.

Problems with IDENTITY_INSERT Set to OFF? :-/

I'm trying to insert order details into my DB, and it keeps saying:
Cannot insert explicit value for identity column in table 'Orders' when IDENTITY_INSERT is set to OFF.
All I am trying to do is simply insert the users UserId into the UserId column, by using WebSecurity.CurrentUserId - Why is this not working?
I have:
dbase.Execute("INSERT INTO Orders
(UserId, OrderId, Status)
VALUES
(#0, #1, #2)",
WebSecurity.CurrentUserId,
Session["OSFOID"],
"Confirmed");`
So, as you can see, it's pretty simple. But, why won't it work?
My table definition is:
Unless you enable the ability to do identity-insert (by setting identity-insert on for that table), you are NOT ALLOWED to touch that column - the database owns it.
Either enable identity insert briefly, or: don't try to insert the UserId (let the DB create a new id).
As per books online, SET IDENTITY_INSERT:
SET IDENTITY_INSERT Orders ON
INSERT INTO Orders (UserId, OrderId, Status) VALUES (#0, #1, #2)
SET IDENTITY_INSERT Orders OFF
More likely, though: if this is the Orders table, should the identity not be on OrderId ? You'd still have the same problem since you are trying to control the OrderId, of course.
Generally you would not want to insert an integer into a primary key column. You would usually set the column's "Identity" flag to true only where you wanted to have SQL Server set an auto-incrementing integer into this column on insert.
As with Marc Gravell's answer, you can enable identity insert using
SET IDENTITY_INSERT [ database. [ owner. ] ] { table } { ON | OFF }
But doing this in regular application code is really unhealthy -- you'll end up with concurrency issues and quite likely duplicate identities. Better, don't insert the Order's ID -- let the DB do it for you automatically, and then simply query for the new ID using ##IDENTITY (or better, SCOPE_IDENTITY()).
If for some reason you definitely need to store the user's session id, make this a separate column on the order table, or better still, on a separate User table, with the UserId being a foreign key.
You do not want the UserID to be an Identity, and I also do not think you want the UserID to be the primary key either. More than likely, you want the OrderID to be the primary key, or at best shared primary key with the userid and orderid.
In your table's definition set Is Identity? as False, with out setting that you cant insert a value manually to the UserID
You have set IS Identity to YES that's why now you cant insert value for this column DB will automatically insert it by incrementing values..
And the thing i am seeing you set UserId as the primary key of the table which is wrong Order OoderID should be the primary key of the column.
Set UserID IsIdentify to false
and OrderID IsEdentitfy to yes and made it primary key column.
Interestingly I found that when I created a table using a "Select.....Into [new table] from [tables containing data]" I subsequently could not Insert new records, getting the Insert_Identity set to off message.
I got around this by deleting the table then using a Create Table script to rebuild it and now have no problems inserting as many new IDs as needed

Categories

Resources