I'm using EF in order to insert and retrieve info from DB,
there is any way to insert new row but at the specified position,
Like i have 10 rows with IDs ranging from 0 to 9 and new row i'm inserting will be on the position 4?
I'm using ASP.NET MVC 5 and LINQ.
Thank you.
The simple answer is no. Order has no meaning unless it's explicit in a database system. Sure in most cases I can insert into a table and pull from this exact table and get the exact order as it was inserted, but this is undefined...and the only guarantee is to use an ORDER BY clause.
If you are talking about changing an auto number property, this is also not possible, the database does not go back and fill in gaps with id numbers. If numbering is critical and important to you don't set the auto-increment property.
Your ID and order position are different things.
For ID you use an autonumeric and you shouldnt mess with that.
For order you use another column and run a trigger when a new row is insert update all the rows
So when the new row is inserted with order_id = 4 all the rows get update
something like
UPDATE table
set order_id = order_id +1
when order_id >= 4
So, I would do so quickly:
I would plan the database to not auto increment primary key and saving would so that the id is attributed according to the specific location. Obviously put an IF to verify that it is available, and if I would start a review cycle to the cascade of subsequent ID or positioning the value traded in the end.
for example
MyTable table = myDb.MyTable.Find(id); //position
if (table==null)
{ table.id=position; table.Field=value; myDb.SaveChanges() }
else
{
var temp = table.id;
var max = table.count(x=> x.id).value;
table.id=max+1;myDb.SaveChanges();
table.id=id; table.Field=value; myDb.SaveChanges();
}
sorry if translate is no good! ;-)
Related
I have a DataGrid View pulling some items from my database. What I want to achieve is to be able to edit the pack size or the bar_code fields. I am aware on how to update values in a database but how would I go about doing it if the data is the same? Meaning in many instances a bar code would have multiple pack sizes that is related to the one bar code number. Let's say I have the below screenshot. A data entry error was made and the bar_code and PackSize columns are the exact same. I want to change the first bar code to "1234." How would I achieve this? I can't say update barcode to 'textBox1.Text' where bar_code = '771313166386' because it would then change both data. How do I go about only focusing on one row of data at a time?
You can try using this query to update only the first row:
UPDATE TOP (1) my_table
SET bar_code = '1234'
WHERE bar_code = '771313166386'
You should have an auto-increment id column or a Primary key in your table.
I'd suggest you handle the logic of data duplicate manipulation at the backend rather than pulling them inside the grid and handle it there.
The following query will help you retrieve the duplicate records based on the mentioned columns. You can change it to UPDATE or DELETE as per your requirement.
-- Using cte and ranking function
;With CTE
As
(
Select
Product,
Description,
BarCode,
PackSize
Row_Number() Over(Partition By Product, BarCode, PackSize Order By Product) As RowNum
From YourTable
)
Select * From CTE
-- Where RowNum > 1;
Hope this is helpful :)
This might not help you directly in your answer. But, it is important to mention that your table design is incorrect. You should ensure the data integrity by creating a primary key in your table.
So when you need to update a product you have only one row to update.
Then you can add more tables and use foreign key references between them.
You need to uniquely represent the products. As per your sample data, I guess that there isn't any primary key on your table.
What you can do is either specify a unique constraint on columns to ensure that this type of data entry cannot be done.
If you cannot come up with list of columns to uniquely identify the rows, you can use surrogate keys by specifying Identity column and then while updating, always put a constraint where thisIdentityColumn=value
A data entry error was made and the bar_code and PackSize columns are
the exact same
I think this is the key. Essentially, the exact duplicates are unintentional, and the rows should be unique. Further it looks like bar_code + pack_size is your primary key (subject to data being entered correctly).
So, when you do an update, simply update the first row found that matches a bar_code and a pack_size. If it isn't unique, then the update should ensure that you are one step closer to unique rows in the database.
If you need a non-verbal answer, let me know.
I need a way to generate ids for a database based on certain information. Depending on the conference the user is registering for, the id will differ. The id will be 4 digits of the event identifier concatenated with 5 sequential digits. For example:
Event A: 1000
Event B: 2000
Event C: 3000
An id for event A could be 100012345, and the next number for this id would be 100012346. An id for event B could be 200012345. Etc.
So how can I grab the last id for a certain event?
How can I autogenerate the next number for this?
Use uniqueidentifier type for IDs with newid() default value , not int. This will create automatically new id of type guid.
If the problem is to take last created ID.Create two new columns in your table UpdateDate and CreateDate. When you are inserting a record
CreateDate = DateTime.Now;
On update
UpdateDate = DateTime.Now;
If you want to take last create item you will fetch data from database with this query:
Select * from [TableName] Order By CreateDate Desc
I suggest you split the event and id as two separate columns in the DB and make them both primary key.
Then, should you need to add another key, first check what event you need (for example 1000) then check on DB what's the max id for that event. This should answer your first question.
As for your second question, once you have the max id for the event, you can just add +1.
Or even better, you could define id as auto-increment, but I'm not sure it'll work on a single part of a complex primary key.
Hope this helps! :)
If you insist on combining the two int values as a string then you would have to search. Since you are using strings you will need to make sure your sequence number is zero padded e.g 200000001 being the first id for event B. Given this then the query
Select top 1 ID from [TableName] Where ID like '2000%' Order By ID Desc
should get you the largest ID and you will have to break it apart/convert to int and increment it. Of course you will then need some collision handling code when you attempt to create the record unless you are single thread/process when changing the db.
You might want to consider breaking these two concepts apart. You could use individual int fields to prevent doing string searches and instead combine the two int values when you need to present them as a combined identifier. Then you r SQL is
Select Top 1 SequenceId from [TableName] Where EventId=2000 Order By SequenceId Desc
Also if you truly want auto incremented sequence identifiers then you would have to move to a table per event with an auto incremented PK on each table as primary keys are the only auto incremented fields in SQL Server.
In my opinion, a trigger would be a good bet. You could create an sql trigger which would fire before the insert, checking which event the user is registering for, and generating the corresponding Id.
I want to get a new row id for "products", for this I use MAX SQL command as follwing (the command is in insert new record button click event):
SqlCommand cmd = new SqlCommand("Select ISNULL(MAX(id)+1,0) from products", SqlCon);
the issue is when there are rows with IDs 10,11,12 (12 is MAX) and i delete id 12 record , i gets MAX+1 id 12 when the new id row is 13 ("id" field is PK with identity increment 1).
can i do it with other way?
example:
id prodect
-- -------
1 dog
2 cat
3 mouse
4 elefant
when i deletes row 4 i get MAX(id)+1 = 4 and i want to get 5 since this is the next row id.
I suspect the actual question is How can I find the ID of the row I just inserted so I can use it as a foreign key in related tables or in an image file name?
SQL Server since 2005 provides the OUTPUT clause in INSERT, UPDATE, DELETE statements that returns the values of the columns just inserted or modified. In the case of the insert statement, the syntax is:
insert into Products (Product)
OUTPUT inserted.ID
VALUES ('xxx')
This is a better option than the IDENT_CURRENT or SCOPE_IDENTITY values because it returns the values using a single statement and there is no ambiguity about what is returned:
IDENT_CURRENT may return a different value if multiple users are writing to the table outside a transaction
SCOPE_IDENTITY returns the last ID generated in a transaction, no matter the table
You can return more than one column:
insert into Products (Product)
OUTPUT inserted.ID, inserted.Product
VALUES ('xxx')
You can execute this statement with ExecuteScalar, if you return only one column or ExecuteReader, if you want to return more columns.
In the case of UPDATE or DELETE statements, the deleted table contains the deleted values and inserted contains the new values
Note ORMs like Entity Framework use such statements already to retrieve auto-generated IDs and update saved objects. In this case one only needs to read the ID property of the saved objects.
I will take a stab at what I think you are after. :)
If you include SELECT SCOPE_IDENTITY(); in your SQL you will get the ID you need:
INSERT INTO products (
* your fields *
)
VALUES (
* your values *
);
SELECT SCOPE_IDENTITY();
And then in your code you can have:
var Id = Convert.ToInt32(cmd.ExecuteScalar());
This will give you the id of the record you have inserted.
One possible solution could be that you don't delete the rows. You can add a flag and make it inactive/deleted. That way your row numbers will always be preserved and your code will give you the max Id.
I think the OP tries to tackle the wrong problem...
When you insert a new product into the products table, you should try to retrieve the new id directly with the scope_identity function as such (SQLServer!):
string sql = "insert into products(name) values('Yellow Cup'); SELECT SCOPE_IDENTITY();";
var sqlCommand = new SqlCommand(sql, conn);
var id = cSqlServer.ExecuteScalar();
Definitely MAX is not what anybody would use in this case. Closest solution would be to get recently used identity value and then increment it by 1 (in your case) or by seed value, whatever it is.
select ident_current('products') + 1
Caution - although this solves your purpose for now, beware that 'ident_current' will return you the identity value set by other sessions as well. In simple words, if there is some request/trigger/execution that causes id to be incremented even before your button click finishes then you you will get inserted_id and not deleted one.
I am doing a conversion with SqlBulkCopy. I currently have an IList collection of classes which basically i can do a conversion to a DataTable for use with SqlBulkCopy.
Problem is that I can have 3 records with the same ID.
Let me explain .. here are 3 records
ID Name Address
1 Scott London
1 Mark London
1 Manchester
Basically i need to insert them sequentially .. hence i insert record 1 if it doesn't exist, then the next record if it exists i need to update the record rather than insert a new 1 (notice the id is still 1) so in the case of the second record i replace both columns Name And Address on ID 1.
Finally on the 3rd record you notice that Name doesn't exist but its ID 1 and has an address of manchester so i need to update the record but NOT CHANGING Name but updating Manchester.. hence the 3rd record would make the id1 =
ID Name Address
1 Mark Manchester
Any ideas how i can do this? i am at a loss.
Thanks.
EDIT
Ok a little update. I will manage and merge my records before using SQLbulkCopy. Is it possible to get a list of what succeeded and what failed... or is it a case of ALL or nothing? I presume there is no other alternative to SQLbulkCopy but to do updates?
it would be ideal to be able to Insert everything and the ones that failed are inserted into a temp table ... hence i only need to worry about correcting the ones in my failed table as the others i know are all OK
Since you need to process that data into a DataTable anyway (unless you are writing a custom IDataReader), you should merge the records before giving them to SqlBulkCopy; for example (in pseudo code):
/* create empty data-table */
foreach(row in list) {
var row = /* try to get exsiting row from data-table based on id */
if(row == null) { row = /* create and append row to data-table */ }
else { merge non-trivial properties into existing row */
}
then pass the DataTable to SqlBulkCopy once you have the desired data.
Re the edit; in that scenario, I would upload to a staging table (just a regular table that has a schema like the uploaded data, but typically no foreign keys etc), then use regular TSQL to move the data into the transactional tables. In addition to full TSQL support this also allows better logging of operations. In particular, perhaps look at the OUTPUT clause of INSERT which can help complex bulk operations.
You can't do updates with bulk copy (bulk insert), only insert. Hence the name.
You need to fix the data before you insert them. If this means you have updates to pre-existing rows, you can't insert those as that will generate the key conflict.
You can either bulk insert into a temporary table, and run the appropriate insert or update statements, only insert the new rows and issue update statements for the rest, or delete the pre-existing rows after fetching them and fixing the data before reinserting.
But there's no way to persuade bulk copy to update an existing row.
I have 200,000 records in a database with the PK as a varchar(50)
Every 5 minutes I do a SELECT COUNT(*) FROM TABLE
If that result is greater than the List.Count I then execute
"SELECT * FROM TABLE WHERE PRIMARYKEY NOT IN ( " + myList.ToCSVString() + ")"
The reason I do this is because records are being added to the table via another process.
This query takes a long time to run and I also believe its throwing an OutOfMemoryException
Is there a better way to implement this?
Thanks
SQL Server has a solution for this, add a timestamp column, every time you touch any row in the table the timestamp will grow.
Add an index for the timestamp column.
Instead of just storing ids in memory, store ids and last timestamp.
To update:
select max timestamp
select all the rows between old max timestamp and current max timestamp
merge that into the list
Handling deletions is a bit more tricky, but can be achieved if you tombstone as opposed to delete.
Can you change the table?
If so, you might want to add a new auto incremented column that will serve as the PK TableId.
On each SELECT save the max id and on the next select add where TableId > maxId.
Create an INT PK, and use something like this:
"SELECT * FROM TABLE WHERE MY_ID > " + myList.Last().Id;
If you can't change your PK, create another column with date as type , and with NOW() as the default value and use it to query for new items.
Create another table in the database with a single column for for the primary key. When your application starts, insert the PKs into this table. Then you can detect added keys directly with a select rather than checking the count:
select PrimaryKey from Table where PrimaryKey not in (select PrimaryKey from OtherTable)
If this CSV list is large, I would recommend loading your file into a temp table, put an index on it and do a left join where null
select tbl.*
from table tbl
left join #tmpTable tmp on tbl.primarykey = tmp.primarykey
where tmp.primary key is null
edit: a Primary Key should not be a varchar. It should almost always be a incremented int/bigint. This would've been a lot easier. select * from table where primarykey > #lastknownkey
Smack the DB programmer who designed this.. :p
This design would also cause index fragmentation because rows won't be inserted in a linear fashion.