update table statement - Violation of Primary Key constraint - sql

I tried this c# update if record exists else insert new record to update my table, if records exists.
But I run into the exception: "Violation of Primary Key constraint". Because of this line cmdCount.Parameters.AddWithValue("#local_programs_id", local_programs_id); and because there's already a value for local_programs_id in the table
What have I done wrong?
Thanks a lot.
SqlConnection connection = new SqlConnection(sqlConnection_String);
SqlCommand cmdCount = new SqlCommand("SELECT count(*) FROM " + datenbankname + " WHERE local_programs_id = #local_programs_id" , connection);
cmdCount.Parameters.AddWithValue("#local_programs_id", local_programs_id);
connection.Open();
int count = (int)cmdCount.ExecuteScalar();
Console.WriteLine("count1 " + count);
if (count > 0)
{
SqlCommand updateCommand = new SqlCommand("UPDATE " + datenbankname +
" SET local_programs_Id = #local_programs_Id, " +
"program_name = #program_name, " +
"publisher_name = #publisher_name, " +
"program_version = #program_version, " +
"install_dir = #install_dir, " +
"uninstall_dir = #uninstall_dir, " +
"inserted_at = #inserted_at, " +
"direct_link_available = #direct_link_available", connection);
updateCommand.Parameters.AddWithValue("#local_programs_Id", local_programs_id);
updateCommand.Parameters.AddWithValue("#program_name", program_names);
updateCommand.Parameters.AddWithValue("#publisher_name", publisher_names);
updateCommand.Parameters.AddWithValue("#program_version", program_version);
updateCommand.Parameters.AddWithValue("#install_dir", install_location);
updateCommand.Parameters.AddWithValue("#uninstall_dir", uninstall_location);
updateCommand.Parameters.AddWithValue("#inserted_at", DateTime.Now);
updateCommand.Parameters.AddWithValue("#direct_link_available", direct_link_available);
int rowsUpdated = updateCommand.ExecuteNonQuery();
Console.WriteLine("rowsUpdated " + rowsUpdated);
}
else
{
Console.WriteLine("inserted1 ");
string query = "INSERT INTO " + datenbankname + " (local_programs_Id, program_name, publisher_name, program_version, install_dir, uninstall_dir, inserted_at)";
query += " VALUES (#local_programs_Id, #program_name, #publisher_name, #program_version, #install_dir, #uninstall_dir, #inserted_at)";
SqlCommand insertCommand = new SqlCommand(query, connection);
insertCommand.Parameters.AddWithValue("#local_programs_Id", local_programs_id);
insertCommand.Parameters.AddWithValue("#program_name", program_names);
insertCommand.Parameters.AddWithValue("#publisher_name", publisher_names);
insertCommand.Parameters.AddWithValue("#program_version", program_version);
insertCommand.Parameters.AddWithValue("#install_dir", install_location);
insertCommand.Parameters.AddWithValue("#uninstall_dir", uninstall_location);
insertCommand.Parameters.AddWithValue("#inserted_at", DateTime.Now);
int rowsInserted = insertCommand.ExecuteNonQuery();
}

You don't have a where filter on your update statement and you are updating the primary key column. You're essentially doing
Check if there is a row with X primary key value
If there is then update every row to have that primary key value.
If not then insert.
You should not be updating the primary key ever, even in your case where it isn't supposed to be changing. There are lots of annoying behaviour that could come from this, one that springs to mind is where this primary key is referenced in a foreign key in another table - this update statement would put locks on the other table (potentially a full table lock if it's not indexed).
You also should not be using a count(*) to determine whether the row exists. This will only tell you if the row exists at that point in time and is visible to your session (you can't see non-committed transactions). Most RDBMSs have a MERGE operation you can use for this behaviour, have a look at your RDBMSs docs for it. Alternatively, it might be okay to optimistically try the insert statement and if it throws a duplicate row error then you do the update statement.

Related

Number of query values and destination fields are not the same (Union)

Running into this SQL error - number of query values and destination fields are not the same (Union). Using an Access database. The programList table has just two fields in it - userID and programID. Using parameters in my cs file. Here is the complete method:
public void AddProgramList (string program, int userID)
{
dbConnection.Open();
string sqlStmt = "INSERT INTO programs (program) ";
sqlStmt += "VALUES (#program)";
string sqlStmt2 = "INSERT INTO programList (userID, programID) " +
"SELECT userID " +
"FROM users" +
"WHERE userID = #userID " +
"UNION " +
"SELECT programID " +
"FROM programs" +
"WHERE program = #program;";
OleDbCommand dbCommand = new OleDbCommand(sqlStmt, dbConnection);
OleDbParameter param = new OleDbParameter("#program", program);
dbCommand.Parameters.Add(param);
dbCommand.Parameters.Add(new OleDbParameter("#userID", userID));
OleDbCommand dbCommand2 = new OleDbCommand(sqlStmt2, dbConnection);
OleDbParameter param2 = new OleDbParameter("#program", program);
dbCommand2.Parameters.Add(param2);
dbCommand2.Parameters.Add(new OleDbParameter("#userID", userID));
dbCommand.ExecuteNonQuery();
dbCommand2.ExecuteNonQuery();
dbConnection.Close();
}
If you printed out the SQL, you would see:
INSERT INTO programList (userID, programID) " +
SELECT userID FROM usersWHERE userID = #userID UNION SELECT programID FROM programsWHERE program = #program;
The error should be pretty obvious. You probably don't have a table called usersWHERE.
As I look at the problem, you are trying to insert two columns. So, I think you intend:
INSERT INTO programList (userID, programID)
SELECT userID, programID
FROM users, programs
WHERE userID = #userID AND program = #program;
Or, more simply:
INSERT INTO programList (userID, programID)
SELECT #userID, programid
FROM programs
WHERE program = #program;

MS SQL Server (Ver 2008 or above) - How to recreate auto-incremental based in my ID column

I have a table in csv file (with an ID as numeric).
I manually uploaded the information from the file to a SQL Server data table (creating my ID column as numeric).
But, I want to recreate my ID column as autonumeric ID column that continue the number with the latest entry.
Example: the table have the ID 1, 5, 10. I want to recreate the auto-incremental (IDENTITY) ID column (leaving my old ID's) and next row insertion continue with ID 11.
I suppose that doesn't exists a single method to achieve this. But I want to know the steps that I should follow.
Here is a script to give you an idea of one way you can do it.
IF OBJECT_ID('DELETEME.dbo.Tbl') IS NOT NULL
BEGIN
DROP TABLE Tbl
END
IF OBJECT_ID('DELETEME.dbo.stageTbl') IS NOT NULL
BEGIN
DROP TABLE stageTbl
END
CREATE TABLE Tbl (
ID INT
,A CHAR(1)
)
INSERT INTO Tbl VALUES (1,'A'),(2,'B'),(10,'C')
SELECT *
FROM
Tbl
EXEC sp_rename 'DELETEME.dbo.Tbl', 'stageTbl', 'OBJECT'
--renames original table
--create script for the new table
CREATE TABLE Tbl (
ID INT NOT NULL IDENTITY(1,1)
,A CHAR(1)
)
--have to set IDENTITY_INSERT on to insert the ID into an IDENTITY column
SET IDENTITY_INSERT Tbl ON
INSERT INTO Tbl (ID, A)
SELECT ID, A
FROM
stageTbl
SET IDENTITY_INSERT Tbl OFF
DROP TABLE stageTbl
--drops original table
DBCC CHECKIDENT('Tbl', RESEED, 222)
--sets the number you want to with next if you set as 222 the next identity will be 223
INSERT INTO Tbl (A) VALUES ('D')
SELECT *
FROM
Tbl
Basic Steps
Renames original Table (if you want your new table to be the same name as the old, I like to rename first due to auto generated names of constraints etc on the new table)
Create the New table with the Column as an Identity column
Turn on IDENTITY_INSERT
Select all records from the old table into the new one
Turn off IDENTITY_INSERT
You don't have to but you can RESSED the identity to start with whatever number you want otherwise SQL-server will automatically do this based on the greatest ID value.
Drop the original table that you renamed
Thanks to Matt to help me out with the original question.
I want to share a C# method that I used to automate all the necessary steps:
-- Disclaimer: the use of this is my class that connects with MS SQL Server, used to read a SELECT sentence (And returns a DataTable) and Execute SQL Queries, etc. Hope someone could find this code helpfully (AS-IS) --
/// <summary> Recreate an ID with auto-incremental when the table has the ID without this option.
/// <para>Automatically will rename the original table to TABLENAME_TO_DELETE (The process will require copy and recreate the table, then the process will duplicate the information) </para></summary>
/// <param name="strTable">SQL table</param>
/// <param name="strId">ID column</param>
public string recreateIdentityColumn(string strTable, string strId)
{
string strLog = "Table: {0} - ID: {1}".fwFormat(strTable, strId);
string strNewTable = strTable + "_" + fw.rnd(1, 1000).ToString() + fw.rnd(5000, 10000);
DataTable dtTable = this.fillDataTable("SELECT COLUMN_NAME, DATA_TYPE, NUMERIC_PRECISION, NUMERIC_SCALE " +
"FROM Information_SCHEMA.COLUMNS " +
"WHERE TABLE_NAME = '" + strTable + "'");
if (!dtTable.fwHasData()) throw new Exception("The current table '" + strTable + "' doesn't exists");
DataRow[] drIdInfo = dtTable.Select("COLUMN_NAME = '" + strId + "'");
if (!drIdInfo.fwHasData()) throw new Exception("The ID column '" + strId + "' doesn't exists in the table '" + strTable + "'");
string strIdType = "";
string strColumns = "";
strIdType = drIdInfo[0]["DATA_TYPE"].fwEmpty("");
if (strIdType.fwContains("decimal"))
strIdType += "({0}, {1})".fwFormat(drIdInfo[0]["NUMERIC_PRECISION"].ToString(), drIdInfo[0]["NUMERIC_SCALE"].ToString());
strLog += "\r\nID DataType: " + strIdType;
foreach (DataRow drInfo in dtTable.Rows)
strColumns += ",[" + drInfo["COLUMN_NAME"].ToString() + "]";
strId = "[" + strId.TrimStart('[').TrimEnd(']') + "]";
strColumns = strColumns.TrimStart(',');
strLog += "\r\nColumns: " + strColumns;
try
{
// Rule 1: Clone the table (Only the structure)
this.executeQuery("SELECT TOP 0 * INTO " + strNewTable + " FROM " + strTable);
// Rule 2: Remove the ID from the clone table
this.executeQuery("ALTER TABLE " + strNewTable + " DROP COLUMN " + strId);
// Rule 3: Add the ID column with the identity property
this.executeQuery("ALTER TABLE " + strNewTable + " ADD " + strId + " " + strIdType + " IDENTITY(1,1)");
// Rule 4: Allow manual insertion of ID in the identity column
this.executeQuery("SET IDENTITY_INSERT " + strNewTable + " ON");
// Rule 5: Copy the rows into the table
int intTotalRows = this.rowCount(strTable);
int intTotalNewRows = this.executeQuery("INSERT INTO " + strNewTable + "(" + strColumns + ") " +
"SELECT " + strColumns + " FROM " + strTable);
strLog += "\r\nOriginal rows {0} - New rows {1}".fwFormat(intTotalRows.ToString(), intTotalNewRows.ToString());
// Rule 6: Return the insertion of identity rows to a normal state
this.executeQuery("SET IDENTITY_INSERT " + strNewTable + " OFF");
// Rule 7: Rename the table with NO IDENTITY as OLD and rename the table with INDENTITY ID as NEW/ACTUAL
this.executeQuery("EXEC sp_rename '" + strTable + "', '" + strTable + "_TO_DELETE', 'OBJECT'");
this.executeQuery("EXEC sp_rename '" + strNewTable + "', '" + strTable + "', 'OBJECT'");
strLog += "\r\nProcess run without problems";
return strLog;
}
catch (Exception ex)
{
strLog += "\r\nException occur";
throw ex;
}
}

MS SQL Invalid column name error

This code returns the following error:
"System.Data.SqlClient.SqlException (0x80131904): Invalid column name 'a51'"
a51 is the correct value inside of the record I'm looking for in the EstablishmentCode column of the Establishments table. Account ID is used to find all entries on the Establishments table with that account ID and populate a dataset with Establishment Code values. Account ID value comes from a session variable. Then I use each of these values in a loop where each iteration calls a datareader while loop. Hope I explained this clearly, but I would gladly clarify more if needed. Here's my code.
myConnection.Open();
SqlCommand getEst = new SqlCommand("SELECT EstablishmentCode FROM Establishments WHERE AccountID = " + ID, myConnection);
da = new SqlDataAdapter(getEst);
ds = new DataSet();
da.Fill(ds);
int maxrows = ds.Tables[0].Rows.Count;
for (int x = 0; x < maxrows; x++)
{
getPhones = new SqlCommand("SELECT * FROM DispatcherPhones WHERE EstablishmentCode = " + ds.Tables[0].Rows[x].ItemArray.GetValue(0).ToString(), myConnection);
myReader = getPhones.ExecuteReader();
while (myReader.Read())
{
Response.Write("<section id='phone" + myReader["Phone"].ToString() + "' style='padding:20px'>");
Response.Write("<section>Phone Number<br><div class='phone'>" + myReader["Phone"].ToString() + "</div></section>");
Response.Write("<section>Location Code<br><div class='name'>" + myReader["EstablishmentCode"].ToString() + "</div></section>");
Response.Write("<section>Active<br><div class='name'>" + myReader["Active"].ToString() + "</div></section>");
Response.Write("<section class='flex phoneButtonSection'>");
Response.Write("<button type=\"button\" onclick=\"showPhoneForm('" + myReader["ID"].ToString() + "');\">CHANGE</button>");
Response.Write("<button type=\"button\" onclick=\"deletePhones('" + myReader["ID"].ToString() + "');\">DELETE</button>");
Response.Write("</section>");
Response.Write("</section>");
}
myReader.Close();
}
myReader.Close();
myConnection.Close();
String literals in SQL are denoted by single quotes ('s) which are missing for your value:
getPhones = new SqlCommand
("SELECT * " +
"FROM DispatcherPhones
"WHERE EstablishmentCode = '" +
// Here -------------------^
ds.Tables[0].Rows[x].ItemArray.GetValue(0).ToString() +
"'" // And here
, myConnection);
Mandatory comment: concatinating strings in order to create SQL statements may leave your code exposed to SQL injection attacks. You should consider using prepared statements instead.

Sql query added to title?

In c# Windows Forms:
I'm having trouble adding a sql query result as text to a ToolStripMenuItem.Text.
The ToolStripMenuItem title should be, the company + how many orders there are in the sql table for this company which should update every x secounds.
Every 5 seconds it adds the query result to the text. My problem is that is "adds" it.
After the first 5 seconds it looks OK "rexton 1" but 5 seconds after it shows "rexton 1 1" and so on...
Here is my code:
//Rexton ordre klar til bestilling
SqlConnection con = new SqlConnection(#"Data Source=" + globalvariables.hosttxt + "," + globalvariables.porttxt + "\\SQLEXPRESS;Database=ha;Persist Security Info=false; UID='" + globalvariables.user + "' ; PWD='" + globalvariables.psw + "'");
SqlCommand command = con.CreateCommand();
command.CommandText = "SELECT COUNT(*) from bestillinger WHERE firma = #rexton and udlevering BETWEEN #date and #dateadd";
command.Parameters.AddWithValue("#bernafon", "Bernafon");
command.Parameters.AddWithValue("#gn_resound", "GN Resound");
command.Parameters.AddWithValue("#oticon", "Oticon");
command.Parameters.AddWithValue("#phonak", "Phonak");
command.Parameters.AddWithValue("#rexton", "Rexton");
command.Parameters.AddWithValue("#siemens", "Siemens");
command.Parameters.AddWithValue("#widex", "Widex");
con.Open();
command.ExecuteNonQuery();
string result = command.ExecuteScalar().ToString();
con.Close();
if (result != "0")
{
rextonToolStripMenuItem.Text = rextonToolStripMenuItem.Text + " " + result;
rextonToolStripMenuItem.ForeColor = System.Drawing.ColorTranslator.FromHtml("#FF1919");
}
it is because you are setting rextonToolStripMenuItem.Text to rextonToolStripMenuItem.Text + " " + result which is appending to previous text
either set text to blank and set it again or just say
rextonToolStripMenuItem.Text = "rexton " + result

SQL Server 2005 CE 3.5 Re seed IDENTITY

I have inserted some rows into a data table with
Set Identity_insert tblEvent on
I then attempt to 'reseed' the Identity field
int MaxId = this.MaxID()+1;
string upgrade = "ALTER TABLE " + Table + " ALTER COLUMN ID IDENTITY("+ MaxId.ToString() +",1)";
System.Data.SqlServerCe.SqlCeCommand cmd = new System.Data.SqlServerCe.SqlCeCommand(upgrade, connection);
cmd.CommandType = System.Data.CommandType.Text;
connection.Open();
cmd.ExecuteNonQuery();
connection.Close();
'MaxId' is determined by
int MaxId = 0;
string upgrade = "select Max(ID) from " + Table;
System.Data.SqlServerCe.SqlCeCommand cmd = new System.Data.SqlServerCe.SqlCeCommand(upgrade, connection);
cmd.CommandType = System.Data.CommandType.Text;
connection.Open();
MaxId = (int)cmd.ExecuteScalar();
connection.Close();
return MaxId;
However, if I query Max(ID) again after seeding it has'nt changed
Any idea's aprreciated
Try this:
string upgrade = " DBCC CHECKIDENT('[" + Table + "]', RESEED, " + (MaxId + 1)+ " )"
weird, could it be a permissions issue. you should have seen an exception though, unless the exception is gobbled up by a catch all.