I'm developing a WinForm desktop application for users to input employees retirement data, using SQL Server 2008 as DB.
One of the tables that gets part of the user data has a reference to another table whose records were defined at design time, adding a Foreign Key constraint for consistency.
CREATE TABLE tbCongedo (
ID int IDENTITY(0,1) PRIMARY KEY,
ID_ANAGRAFICA int NOT NULL,
ID_TIPOLOGIA int NOT NULL,
DECORRENZA datetime NOT NULL,
PROT_NUM nvarchar(7) NOT NULL,
PROT_DATA datetime NOT NULL
);
CREATE TABLE tbTipologia (
ID int IDENTITY(0,1) PRIMARY KEY,
TIPOLOGIA nvarchar(20)
);
INSERT INTO tbTipologia VALUES ('CONGEDO'), ('POSTICIPO'), ('ANTICIPO'), ('REVOCA'), ('DECESSO')
ALTER TABLE tbCongedo
ADD CONSTRAINT FK_tbCongedo_tbTipologia
FOREIGN KEY (ID_TIPOLOGIA) REFERENCES tbTipologia(ID)
Then, I have this code that should execute the INSERT statement
public int Insert(SqlConnection Connessione)
{
using (SqlCommand Comando = new SqlCommand("INSERT INTO tbCongedo VALUES (#ID_ANAGRAFICA, #PROT_NUM, #PROT_DATA, #DECORRENZA, #ID_TIPOLOGIA); SELECT SCOPE_IDENTITY()", Connessione))
{
Comando.Parameters.AddWithValue("#ID_ANAGRAFICA", ID_ANAGRAFICA);
Comando.Parameters.AddWithValue("#PROT_NUM", PROT_NUM);
Comando.Parameters.AddWithValue("#PROT_DATA", PROT_DATA);
Comando.Parameters.AddWithValue("#DECORRENZA", DECORRENZA);
Comando.Parameters.AddWithValue("#ID_TIPOLOGIA", ID_TIPOLOGIA);
ID = Int32.Parse(Comando.ExecuteScalar().ToString());
}
return ID;
}
but I'm given this SqlException:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_tbCongedo_tbTipologia". The conflict occurred in database "Scadenziario_ver4_TEST", table "dbo.tbTipologia", column 'ID'
These are the data that I was trying to get inserted in the table:
ID_ANAGRAFICA = 2
ID_TIPOLOGIA = 0
PROT_DATA = {16/03/2018 00:00:00}
DECORRENZA = {16/03/2018 00:00:00}
PROT_NUM = 123456
Funny thing is that when I try to insert those same data manually through SQL Server Management Studio, I'm given no error at all.
Thanks.-
Try specifying fields: (col_name1, col_name2, ...).
Without that the VALUES may not be applied as how you might hope. Variable names are NOT automagically matched with similarly-named columns.
So like this:
... new SqlCommand
(
"INSERT INTO tbCongedo " +
" (ID_ANAGRAFICA, PROT_NUM, PROT_DATA, DECORRENZA, ID_TIPOLOGIA) "
"VALUES (#ID_ANAGRAFICA, #PROT_NUM, #PROT_DATA, #DECORRENZA, #ID_TIPOLOGIA); " +
"SELECT SCOPE_IDENTITY()", Connessione
)
...
I think the problem isn't in the data but in the INSERT statement itself. You are trying to insert the values to the wrong columns using the wrong order. To solve the issue you should either specify the columns in the INSERT statement or correct the order of the values. In your case the query will try to insert the value of #PROT_NUM in the ID_TIPOLOGIA instead.
Related
I am using the following code in order to insert some data into my SQL Server table. There isn't any error shown, but the table doesn't insert the values. What is wrong with it?
string sqlTxt1 = "INSERT INTO Users (ChatId , UserName) VALUES (#ChatId, #from)";
SqlCommand cmd1 = new SqlCommand(sqlTxt1, con);
cmd1.Parameters.AddWithValue("#ChatId", 12345);
cmd1.Parameters.AddWithValue("#from", "usrnm");
con.Open();
cmd1.ExecuteNonQuery();
//int recordsAffected =cmd1.ExecuteNonQuery();
con.Close();
As I put the code in a button and set the ChatId in the table as a primary key, I get an error when I click it twice, so I think the code is correct, but why the table doesn't get those values in the first click?
It should be string sqlTxt1 = "INSERT INTO Users (ChatId , UserName) VALUES (#ChatId, #from)";. You need to pass #from also.
You are inserting not updating the table. So when you click the second time, it tries to insert the record with the same primary key into the table again. You cannot insert the same value for the primary key in the table more than once, it will be a primary key violation. The primary key is always unique.
I am using c# to insert a record into the SQLite DB table 'VS_Types' with one parameter from table 'VS_Groups'. I test it with SQLite Browser and it works. While I implement the same sqlite command inside the C# with System.Data.SQlite. It doesn't work.
I modify the commandText to as following it works. Looks like the parameter '#value0' influence the c# SQLite command execution.
cmd.CommandText = "INSERT INTO VS_Types(Name, Group_id, Color) Values('20', 10, 1245679);";
SQLite SQL:
SELECT #value0=ID FROM VS_Groups WHERE (Name = 'Ato_h');
INSERT INTO VS_Types(Name, Group_id, Color) VALUES('20', #value0, 65536);
The Code inside the C#
using (SQLiteConnection conn = new SQLiteConnection(cs))
{
try
{
conn.Open();
SQLiteCommand cmd = new SQLiteCommand(conn);
cmd.CommandText = "Select #groudId = ID From VS_Groups Where(Name = 'Ato_h'); "
+ "INSERT INTO VS_Types(Name, Group_id, Color) Values('20', #groudId, 1245679);";
cmd.ExecuteNonQuery();
conn.Close();
testOutput.WriteLine("Insert Successful");
}
catch (Exception ex)
{
this.testOutput.WriteLine("Failed to open connection: {0}", ex.Message);
}
}
Please give me some suggestions about how to use the parameter inside C# Sqlite command in this situation.
Update: the VS_Groups and VS_Types table and its content
CREATE TABLE "VS_Groups" (
`ID` INTEGER PRIMARY KEY AUTOINCREMENT,
`Name` TEXT,
`Width` INTEGER,
`Height` INTEGER,
`Flags` INTEGER,
`Limi_recognition` INTEGER,
`Base` TEXT,
`Flags1` INTEGER,
`Limit_recognition` INTEGER,
`Flags2` INTEGER,
`Limit_recognition2` INTEGER,
`Distance_threshold` INTEGER
)
CREATE TABLE `VS_Types` (
`ID` INTEGER PRIMARY KEY AUTOINCREMENT,
`Name` TEXT,
`Group_id` INTEGER,
`Color` INTEGER
)
The existing record is:9,Ato_h,160,140,65536,,,,400,,,100
Sqlite SQL does not work like that. You cannot declare and store a value in a variable, then reuse that variable later. Terms like #groudId are only placeholders for parameters passed to the query. That means that the expression #groudId = ID is NOT an assignment, rather it is a comparison. Since you are not binding the parameter #groudId to anything, the parameter value is null, so the expression is a comparison like null = ID which will result in false which numerically is 0 (zero). The select statement returns 0 and is not used in the INSERT statement.
If the INSERT is working at all, it is probably resulting in something like INSERT INTO VS_Types(Name, Group_id, Color) Values('20', null, 1245679);
At the end of the question, you also say "The existing record is ..." and you only show a single record for VS_Groups, although the insert statement is for the table VS_Types. But you don't show output for the VS_Types table! You are inspecting the wrong table for the inserted data. If you query the table VS_Types, you will likely find many records with Name == '20', GroupID == null and Color == 123456789... exactly as the INSERT statement says.
In summary, you are not using parameters correctly, but you really don't even need a parameter in the code you show, so it is difficult to know how to answer properly. An answer showing proper use of parameters would be wasted, but a replacement SQL may not be want you want in the end either. I suggest researching parameters separately to learn how to use them properly. For now, use this nested statement:
INSERT INTO VS_Types(Name, Group_id, Color)
VALUES ('20', (Select ID From VS_Groups Where Name = 'Ato_h'), 1245679);
Regrettably that's not all. The table definition does not show that VS_Groups.Name is unique, so technically there could be multiple rows that match the nested query, so the INSERT statement could still fail. I suggest adding a UNIQUE constraint to the VS_Groups.Name column.
After I try to insert a row into database and it fails due to duplicate (not primary key collision - another key), how do I get the primary key of the colliding row that prevented the insert?
Example:
The table:
CREATE TABLE `my_table` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`unique_value` varchar(60) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_data` (`unique_values`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Some initial data:
INSERT INTO `my_table`(`unique_value`) VALUES ('unique string 1');
INSERT INTO `my_table`(`unique_value`) VALUES ('unique string 2');
And now, this will fail because of the collision in the index:
MySqlConnection conn = new MySqlConnection(connString);
conn.Open();
MySqlCommand comm = conn.CreateCommand();
comm.CommandText = "INSERT INTO `my_table`(`unique_value`) VALUES 'unique string 2'";
comm.ExecuteNonQuery();
conn.Close();
Can I get the primary key of the colliding row without making another query?
while creating the table you can use "Identity" keyword which will do all the "not null and Auto Increment" Stuff.
But this is for MsSQL
Try this:
create table my_table
(
ID int Primary key identity,
unique_value varchar(60) NOT NULL
)
As you said this that you are not worry about PK hence you can check the value of the column which has unique index.If there is already a raw with the value that you passing in new row then you are trying to insert duplicate otherwise not.
My problem might be a bit long to describe as the project we are working on is a bit bigger, but i will try to be as precise as i can.
Basically we're developing a web-bases woundmanagement (part of a project for university) where the user can enter wounds and set additional information like size, consistence, upload a picture, choose the location, ... .
All those information should be stored in a database (we're working with MS SQL Studio and Visual Studio 2017) where the user can also retrieve it later to view it on the module.
The problem we are facing now is that if we want to show a wound to a special wound to the user, we can't get the foreign keys to work.
We can filter via the casenumber (which is working) but we can't filter wound information by the ID of the wound (each wound is getting an unique ID) - so if we choose a wound, we still get information about ALL wounds which are stored for the given casenr.
This is our "main-table" where each wound is getting an unique ID which is also an ascending identity column:
[wound_id] INT IDENTITY (1, 1) NOT NULL,
[wound_type] VARCHAR (500) NULL,
[wound_description] VARCHAR (500) NULL,
[decuGrade] INT NULL,
[wound_comments] VARCHAR (500) NULL,
[wound_timeReal] DATETIME NULL,
[wound_timeGiven] DATETIME NULL,
[casenumber] INT NULL,
[username] VARCHAR (50) NULL,
PRIMARY KEY CLUSTERED ([wound_id] ASC)
);
If the user enters the information and clicks "Next", a function is called in code behind which fills the table:
_db.SaveWoundDetails(casenr, woundValue, decu, additional_info, realTime, givenBackDocDate, user);
This leads to our database-class, where we have our queries for the database, in this case:
public void SaveWoundDetails(int casenr, string woundType, int decuGrade, string woundComment, DateTime timeReal, DateTime timeGiven , string user)
{
var table = ConfigurationManager.AppSettings["woundDetailsTable"];
var insertQuery = "INSERT INTO " + table + "(casenumber, wound_type, decuGrade, wound_comments, wound_timeReal, wound_timeGiven, username) VALUES (#casenr, #woundType, #decuGrade, #woundComment, #timeReal, #timeGiven, #user)";
var cmd = new SqlCommand(insertQuery);
cmd.Parameters.AddWithValue("#casenr", casenr);
cmd.Parameters.AddWithValue("#woundType", woundType);
cmd.Parameters.AddWithValue("#decuGrade", decuGrade);
cmd.Parameters.AddWithValue("#woundComment", woundComment);
cmd.Parameters.AddWithValue("#timeReal", timeReal);
cmd.Parameters.AddWithValue("#timeGiven", timeGiven);
cmd.Parameters.AddWithValue("#user", user);
var db = DatabaseController.getDataBaseController();
try
{
var sqlcmd = db.executeSQL(cmd);
}
catch (SqlException e)
{
}
}
The connection etc. is in a Database-handler class which is not relevant at the moment.
Until here it works fine. But now we have a second table for more information about the wound, which is also filled on next click, related to this table:
CREATE TABLE [dbo].[epadoc_mod_wound_progress] (
[progress_id] INT IDENTITY (1, 1) NOT NULL,
[wound_length] INT NULL,
[wound_width] INT NULL,
[wound_depth] INT NULL,
[wound_surrounding] VARCHAR (500) NULL,
[wound_consistence] VARCHAR (500) NULL,
[wound_state] VARCHAR (200) NULL,
[wound_painscale] VARCHAR (MAX) NULL,
[wound_itch] VARCHAR (MAX) NULL,
PRIMARY KEY CLUSTERED ([progress_id] ASC)
With the INSERT-METHOD:
public void SaveWoundProgress(int woundLength, int woundWidth, int woundDepth, string woundSurrounding, string woundConsistence, string woundState, string woundPainScale, string woundItch)
{
var table = ConfigurationManager.AppSettings["woundProgressTable"];
var insertQuery = "INSERT INTO " + table + "(wound_length,wound_width,wound_depth, wound_surrounding, wound_consistence, wound_state, wound_painscale, wound_itch) VALUES (#woundLength, #woundWidth, #woundDepth, #woundSurrounding, #woundConsistence, #woundState, #woundPainScale, #woundItch)";
var cmd = new SqlCommand(insertQuery);
cmd.Parameters.AddWithValue("#woundLength", woundLength);
cmd.Parameters.AddWithValue("#woundWidth", woundWidth);
cmd.Parameters.AddWithValue("#woundDepth", woundDepth);
cmd.Parameters.AddWithValue("#woundSurrounding", woundSurrounding);
cmd.Parameters.AddWithValue("#woundConsistence", woundConsistence);
cmd.Parameters.AddWithValue("#woundState", woundState);
cmd.Parameters.AddWithValue("#woundPainScale", woundPainScale);
cmd.Parameters.AddWithValue("#woundItch", woundItch);
var db = DatabaseController.getDataBaseController();
try
{
var sqlcmd = db.executeSQL(cmd);
}
catch (SqlException e)
{
}
}
And the method
_db.SaveWoundProgress(wound_length, wound_width, wound_depth, woundArea, woundEdge, woundStatus, painStatus, itchStatus);
which is execute right after the method mentioned above.
I know how to create foreign keys between two tables, but everything we tried failed - if we try to execute it with a foreign key set which is NOT NULL, we're getting a null-exception.
Example of what we tried:
CONSTRAINT [FK_epadoc_mod_wound_details] FOREIGN KEY ([wound_id])
REFERENCES [dbo].[epadoc_mod_wound_progress] ([progress_id])
If we set a foreign key like this, it didn't work.
We came to the conclusion that it must be a problem the callstack when the two methods are executed - but we don't know how we can fix it.
Maybe we have to set the foreign key in the INSERT-query as an explicit variable?
What we want to achieve is that the wound_id of the details-table is taken as foreign key the the progress-table so that a wound can be later changed (for example if it heals the user could re-enter the new size etc.) and we can filter by ID to just show ONE wound to the patient and not all wounds at the same time if clicked on a specific wound.
Sadly i'm not the big database expert so i hope that you can follow my explanations :).
Thanks for any help!
Your epadoc_mod_wound_progress needs to include a [wound_id] INT NOT NULL column. This is what your foreign key should be built on so that one wound can have many wound progresses. Then, in your insert statement, you'll insert the wound_id that generates in woundDetail table insert into epadoc_mod_wound_progress.
Tried to add a comment but I don't have 50 reputation.
I assume from what I can see that you are trying to achieve a one to many relationship between the "main table" and the "epadoc_mod_wound_progress" table, is that right ?
If so, you don't seem to have a field in the "epadoc_mod_wound_progress" table that stores the wound_id, how are you trying to create a foreign key if you are not storing the wound_id ?
Suggest the Primary Key of the epadoc_mod_wound_progress table is a concatenated key of wound_id and progress_id, with wound_id also being the foreign key linking to the main table.
In table epadoc_mod_wound_progress there must be a wound_id INT NOT NULL column acting as foreign key.
Also the constraint must be added to the foreign key table, i.e. the table on the n side of the 1 to n relation. Assuming that the name of the main table is epadoc_mod_wound_details (you did not show it):
ALTER TABLE dbo.epadoc_mod_wound_progress
ADD CONSTRAINT FK_progress_details FOREIGN KEY (wound_id)
REFERENCES dbo.epadoc_mod_wound_details (wound_id)
ON DELETE CASCADE
Also, by adding ON DELETE CASCADE the progress of a wound detail will automatically be deleted when you delete the wound detail.
I am new to sql. I have added 2 new tables in database. The primary key of first is a foreign key in the other. The type of the keys is integer. Now I want to generate the keys in the code and assign it to new data so that the association between different rows of the tables is right. How do I ensure uniqueness of keys and also get the latest key from the db so that there are no errors while saving.
If I had used guids then I would have assigned a new guid to the primary key and then assigned the same to the foreign key in the other table. Also there are multiple clients and one server which is saving the data.
The data to be inserted in both the tables is decided in the c# code and is not derived from the row inserted in the primary table. Even if get the id in db then also the relation between the rows should be stored in some form from the code because after that it is lost.
The only viable way to do this is to use INT IDENTITY that the SQL Server database offers. Trust me on this one - you don't want to try to do this on your own!
Just use
CREATE TABLE dbo.YourTableOne(ID INT IDENTITY(1,1), ...other columns...)
and be done with it.
Once you insert a row into your first table, you can retrieve the value of the identity column like this:
-- do the insert into the first table
INSERT INTO dbo.YourTableOne(Col1, Col2, ...., ColN)
VALUES (Val1, Val2, ...., ValN)
DECLARE #NewID INT
-- get the newly inserted ID for future use
SELECT #NewID = SCOPE_IDENTITY()
-- insert into the second table, use first table's new ID for your FK column
INSERT INTO dbo.YourTableTwo (FKColumn, ......) VALUES(#NewID, ......)
Update: if you need to insert multiple rows into the first table and capture multiple generated ID values, use the OUTPUT clause:
-- declare a table variable to hold the data
DECLARE #InsertedData TABLE (NewID INT, ...some other columns as needed......)
-- do the insert into the first table
INSERT INTO dbo.YourTableOne(Col1, Col2, ...., ColN)
OUTPUT Inserted.ID, Inserted.Col1, ..., Inserted.ColN INTO #InsertedData(NewID, Col1, ..., ColN)
VALUES (Val1, Val2, ...., ValN)
and then go from there. You can get any values from the newly inserted rows into the temporary table variable, which will then allow you to decide which new ID values to use for which rows for your second table
As #marc_s said using Database managed keys is more viable. But in cases there is no much load on the database, for example because there are few users who work simultanously, I will use another easier method. That's I get the last id, I try to add new record, and if I encountered error for duplicate, I will try again. I limited this to 3 trials for my application and there's a 300 ms timeout between each trial. Dont forget that this approach has serious limitations. In my application, there are very few users, the work load is very low, and the connection is a local one so this will do job well. Perhaps in other applications you need to adjust the delay, and in some cases, the approach might completely fail. Here's the code,
I have two tables, Invoices and Invoices_Items the column which relates them is invoice_id:
byte attempts = 0;
tryagain: //Find last invoice no
OleDbCommand command = new OleDbCommand("SELECT MAX(invoice_id) FROM Invoices"
, myconnection);
int last_invoice_id = 0;
try
{
last_invoice_id = (int)command.ExecuteScalar();
}
catch (InvalidCastException) { };
// text_invoice_number.Text = Convert.ToString(last_invoice_id + 1);
try
{
command = new OleDbCommand(#"INSERT INTO Invoices
(invoice_id,patient_id,visit_id,issue_date,invoice_to,doctor_id,assistant_id)
VALUES(?,?,?,?,?,?,?)",myconnection);
// We use last_invoice_id+1 as primary key
command.Parameters.AddWithValue("#invoice_id",last_invoice_id+1);
// I will add other parameters here (with the exact order in query)
command.ExecuteNonQuery();
}
catch (Exception ex){
attempts++;
if (attempts <= 3) // 3 attempts
{
System.Threading.Thread.Sleep(300); // 300 ms second delay
goto tryagain;
}
else
{
MessageBox.Show("Can not add invoice to database, " + ex.Message, "Unexpected error!"
, MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
for (int i = 0; i <= listInvoiceItems.Count-1; i++)
{
command = new OleDbCommand(#"INSERT INTO Invoices_Items
(invoice_id,quantity,product,price,amount,item_type)
VALUES(?,?,?,?,?,?)",myconnection);
// Now we use our stored last_invoice_id+1 as foreign key
command.Parameters.AddWithValue("#invoice_id",last_invoice_id+1);
// Add other Invoice Items parameters here (with the exact order in query)
command.ExecuteNonQuery();
}