SQL Server Numeric, can create but not update using C# - c#

Just a simple quick question, I'm using Microsoft SQL Server 2014 Express and now I have two functions to create a record and to update a record containing a numeric value.
For some unknown reason I can create the record using a numeric value with a number bigger than 0 after the decimal point (like 50.50), however, when trying to update this record with the numeric value it just says that my syntax is wrong after the decimal point. So tl,dr (50.00 works, 50.50 or something like that, doesn't).
My question now is: what am I doing wrong?
Here are my two functions:
public static void UpdateProduct(int id, string name, decimal price)
{
try
{
string query = "UPDATE dbo.Products SET Name = '" + name + "' , Price = " + price + " WHERE ProductID = " + id;
SqlCommand command = new SqlCommand(query, connection);
command.ExecuteNonQuery();
}
catch (SqlException e)
{
Console.WriteLine(e.Message);
}
}
public static void AddProduct(string name, decimal price)
{
string query = "INSERT INTO dbo.Products (Name, Price) VALUES (#name, #price)";
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("#name", name);
command.Parameters.AddWithValue("#price", price);
command.ExecuteNonQuery();
}
And here is my SQL create for this value
create Table Products
(
ProductID INT IDENTITY(1,1) PRIMARY key,
Name VARCHAR(255) NOT NULL,
Price NUMERIC(5,2) NOT NULL,
Active BIT DEFAULT 1
);

In your insert query you use query parameters, which, among other things, take care of correct formatting of your decimal value.
In you update query you use string concatenation to add you decimal to the query. Most certainly, your current culture formats the decimal point not as point but as comma, resulting in an syntactically incorrect query.
So your assignment of
string query = "UPDATE dbo.Products SET Name = '" + name + "' , Price = " + price + " WHERE ProductID = " + id;
Will result in a string like
UPDATE dbo.Products SET Name = 'somename' , Price = 50,5 WHERE ProductID = 3
Instead of
UPDATE dbo.Products SET Name = 'somename' , Price = 50.5 WHERE ProductID = 3
Use parametrized queries like in the insert and this problem -- and many potential others you didn't even notice yet -- will be gone.

Related

Why is my call to LastInsertedId not returning the expected value?

I have MySql Tables with autoinc ID columns, such as "director_id" here:
CREATE TABLE directors (
director_id Integer NOT NULL AUTO_INCREMENT,
first_name VarChar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
middle_name VarChar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,
last_name VarChar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
suffix VarChar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,
PRIMARY KEY (
director_id
)
)
I want to store the autoincremented director_id value in the movies_main Table.
So I try to assign the autoincremented value to an int variable:
long director_id = 0;
...in the call to LastInsertedId here (last line):
if (!alreadyExists)
{
comm.Parameters.Clear();
comm.CommandText = "INSERT INTO Directors " +
"(first_name, middle_name, last_name, suffix) " +
"VALUES " +
"(#first_name, #middle_name, #last_name, #suffix)";
comm.Parameters.AddWithValue("#first_name", directorNamePartsList[0]);
comm.Parameters.AddWithValue("#middle_name", directorNamePartsList[1]);
comm.Parameters.AddWithValue("#last_name", directorNamePartsList[2]);
comm.Parameters.AddWithValue("#suffix", directorNamePartsList[3]);
comm.ExecuteNonQuery();
director_id = comm.LastInsertedId;
}
...and then assign it to the movies_main Table like so:
if (!alreadyExists)
{
comm.Parameters.Clear();
comm.CommandText = "INSERT INTO Movies_Main " +
"(movie_title, mpaa_rating, imdb_rating, movie_length, director_id,
screenwriter_id, year_released) " +
"VALUES " +
"(#movie_title, #mpaa_rating, #imdb_rating, #movie_length, #director_id,
#screenwriter_id, #year_released)";
comm.Parameters.AddWithValue("#movie_title", title);
comm.Parameters.AddWithValue("#mpaa_rating", mpaa_rating);
comm.Parameters.AddWithValue("#imdb_rating", Math.Round(imdb_rating, 1));
comm.Parameters.AddWithValue("#movie_length", movie_length);
comm.Parameters.AddWithValue("#director_id", director_id);
comm.Parameters.AddWithValue("#screenwriter_id", screenwriter_id);
comm.Parameters.AddWithValue("#year_released", year_released);
comm.ExecuteNonQuery();
movie_id = comm.LastInsertedId;
}
Yet the value assigned to the movies_main Table for director_id is always 0!
Why is LastInsertId (apparently) returning 0, and how can I get it to actually return the value its name claims it does? Will I have to resort to a "SELECT MAX(director_id)" query to actually get the value?
NOTE: The movie_id code does work! I get a non-zero value when assigning the result of the call to LastInsertedId to the movie_id variable, and it is added to other tables just fine. This code works as expected:
foreach (var gen_desc in genreList)
{
long genreID = Convert.ToInt32(GetGenreIDForDescription(gen_desc));
alreadyExists = PairAlreadyExistsInMoviesGenresM2Mtable(
movie_id, genreID);
if (!alreadyExists)
{
comm.Parameters.Clear();
comm.CommandText = "INSERT INTO Movies_Genres " +
"(movie_id, genre_id) " +
"VALUES " +
"(#movie_id, #genre_id)";
comm.Parameters.AddWithValue("#movie_id", movie_id);
comm.Parameters.AddWithValue("#genre_id", genreID);
comm.ExecuteNonQuery();
}
}
An alternative way to LastInsertedId property from the MySqlCommand is the native MySql function LAST_INSERT_ID. We can call this function and get its return value adding a simple SELECT statement to your current command text. MySql supports batch statements and so, with a single server call we could execute more than one single command text.
if (!alreadyExists)
{
comm.Parameters.Clear();
comm.CommandText = "INSERT INTO Directors " +
"(first_name, middle_name, last_name, suffix) " +
"VALUES " +
"(#first_name, #middle_name, #last_name, #suffix); " + // semicolon to close the first statement
"SELECT LAST_INSERT_ID()";
comm.Parameters.AddWithValue("#first_name", directorNamePartsList[0]);
comm.Parameters.AddWithValue("#middle_name", directorNamePartsList[1]);
comm.Parameters.AddWithValue("#last_name", directorNamePartsList[2]);
comm.Parameters.AddWithValue("#suffix", directorNamePartsList[3]);
director_id = Convert.ToInt64(comm.ExecuteScalar());
}
Note that we can now use ExecuteScalar because we get back just one record with a single column.
Let me say however that I have tried to reproduce your problem with LastInsertedId. I have recreated your table and written a simple script in LinqPad trying to insert some fixed data in that table.
I have no problem with LastInsertedId property and I get the correct value. I have read that if you have more threads that are concurrently inserting records you could get some problems with that property but I have no proof of any kind of misbehaving

C# Exception with MySQL UPDATE query

Recently, I have been working on a C# plugin for a game I play. My problem at the moment is that the query listed below is generating many errors, such as these errors:
MySql.Data.MySqlClient.MySqlException: Data too long for column 'lastServer' at row 1
^^This error shows when my lastServer data type is varchar(10), but when I changed it to varchar(25), it went away. However I would still like to know why this would be, if anyone is able to figure it out.
.
My current error is having #lastServer, #characterName, and #ip showing up as #lastServer, #characterName, and #ip instead of their "AddWithValue" values.
Here's the query used for Table Creation:
command.CommandText = string.Concat("CREATE TABLE `", MDiscipline.Instance.Configuration.Instance.PlayerInfoTableName, "` (`steamId` varchar(32) NOT NULL,`characterName` varchar(40) NOT NULL,`ip` varchar(15) NOT NULL, `lastPunishment` tinyint(2) UNSIGNED NOT NULL,`lastServer` varchar(25) NOT NULL,`lastLogin` TIMESTAMP NOT NULL DEFAULT current_timestamp, PRIMARY KEY (`steamId`)) ");
And Here is the Code I'm having problems with:
public void UpdatePlayerInfo(string steamid, string characterName, string ip)
{
try
{
MySqlConnection connection = createConnection();
MySqlCommand command = connection.CreateCommand();
command.Parameters.AddWithValue("#steamId", steamid);
command.Parameters.AddWithValue("#characterName", characterName);
command.Parameters.AddWithValue("#ip", ip);
command.Parameters.AddWithValue("#lastServer", MDiscipline.Instance.Configuration.Instance.Instance);
command.CommandText = "UPDATE `" + MDiscipline.Instance.Configuration.Instance.PlayerInfoTableName + "` SET `characterName` = '#characterName', `ip` = '#ip', `lastServer` = '#lastServer', `lastLogin` = NOW() WHERE `steamId` = '" + steamid.ToString() + "';";
connection.Open();
command.ExecuteNonQuery();
connection.Close();
}
catch (Exception ex)
{
Logger.LogException(ex);
}
}
Helpful info to understand variables:
MDiscipline.Instance.Configuration.Instance.PlayerInfoTableName can be translated to "player_info".
steamid or (#steamId) can be translated to a 32 char number.
characterName or (#characterName) can be translated to any value between 1 and 30 characters.
ip or (#ip) can be translated to an ip address in a string, such as "255.255.255.255".
MDiscipline.Instance.Configuration.Instance.Instance or (#lastServer) can be translated to Server01.

Incorrect Syntax when creating table from textbox name

I was trying to create a table based on the name given in textbox1 .I am getting error in the following code :
Incorrect syntax near 'Ramesh'.
Here Ramesh was the value in textbox.
string Customername = Textbox1.text
SqlCommand cmd7 = new SqlCommand("CREATE TABLE '" + CustomerName + "' (ItemCode int,Quantity int,PricePerQuantity int,Brand char(50),Discount int , DateTime datetime)",connection
You don't need single quotes for your table name.
SqlCommand cmd7 = new SqlCommand("CREATE TABLE " + CustomerName + " (ItemCode int,Quantity int,PricePerQuantity int,Brand char(50),Discount int , DateTime datetime)",connection);
But weird part, don't use SqlCommand for MySQL. Use MySqlCommand and related class.
Also I would say that use parameterize queries but since you can't parameterize column name, and looks like you get it as an input, use strong validation or use whitelisting before you put it in your query.
You can read: The BobbyTables culture
remove ' from sides of the table name in query.
string Customername = Textbox1.text
SqlCommand cmd7 = new SqlCommand("CREATE TABLE " + CustomerName + " (ItemCode int,Quantity int,PricePerQuantity int,Brand char(50),Discount int , DateTime datetime)",connection
The immediate cause of the error is that you should not put table name into apostrophes. Something like this:
// put IDisposable into using
using (SqlCommand cmd7 = new SqlCommand(
// Keep SQL readable; "$" - C# 6.0 feature
$#"CREATE TABLE {Textbox1.text}(
ItemCode int,
Quantity int,
PricePerQuantity int,
Brand char(50),
Discount int,
DateTime datetime)",
connection)) {
cmd7.ExecuteNonQuery(); // execute and create the table
}

C# update database

I'm using this string to update database and in this case, it works fine. It updates Znesek_nakupa in in last row:
string sqlUpd = "UPDATE Racun SET Znesek_nakupa='10' WHERE Id_racun= (SELECT MAX(Id_racun) FROM Racun)";
But when I'm trying to insert variable and not just 10 it gives me error:
Error converting data type varchar to numeric.
Code example:
double totalPrice = 1.1;
string sqlUpd = "UPDATE Racun SET Znesek_nakupa='totalPrice' WHERE Id_racun= (SELECT MAX(Id_racun) FROM Racun)";
How can I do this?
This problem less to do with SQL, and more to do with using strings and variables in C#.
In order to insert the value of a variable in a string in C#, you can't just place the name of the variable in the string. The string doesn't "know" that it contains a variable. Here are a couple of approaches that will work instead:
double totalPrice = 1.1;
// string concatenation
string sqlUpd =
"UPDATE Racun SET Znesek_nakupa='" +
totalPrice +
"' WHERE Id_racun= (SELECT MAX(Id_racun) FROM Racun)";
// with string.Format
string sqlUpd = string.Format(
"UPDATE Racun SET Znesek_nakupa='{0}' WHERE Id_racun= (SELECT MAX(Id_racun) FROM Racun)",
totalPrice);
However, the approach of just embedding a variable's value in a SQL query like this is not considered best practice as it risks SQL injection attacks. Usually you would want to use parameterised SQL queries.
A parameterised version of your query would look like this (lifting the example from the page linked to above):
SqlConnection conn = new SqlConnection(_connectionString);
conn.Open();
string s = "UPDATE Racun SET Znesek_nakupa='#totalPrice' WHERE Id_racun= (SELECT MAX(Id_racun) FROM Racun";
SqlCommand cmd = new SqlCommand(s);
cmd.Parameters.Add("#totalPrice", totalPrice);
SqlDataReader reader = cmd.ExecuteReader();
Ok, I got it.
When I try to save variable totalPrice in database it comes to error, because C# has comma as separator. In database I have to send dot instead. So I simple replace comma with dot and now it works perfect.
So code looks like this now:
string sqlUpd = "UPDATE Racun SET Znesek_nakupa='" + Convert.ToString(totalPrice).Replace(',', '.') + "' WHERE Id_racun= (SELECT MAX(Id_racun) FROM Racun)";

Oracle guid to other table

At the moment I'm writing a small conversion program, it will convert the primary key strategy to the using of GUIDs in stead of integers. This is a simple client induced requirement and I can't change that.
I've added a substitute pk candidate of the RAW(16) to every table in the database and filled each record with a SYS_GUID().
I did the same for the FKs, I added a substitute column for each FK.
Now I'm in the process of linking the FKs to their PKs, by querying the parent table I get the guid/new key for the specific row, after that I want to insert into the substitute candidate FK in the child table.
Somewhat like this:
sqlString = "SELECT PK FROM " + t+ " WHERE " + fkcol+ " = " + childValue;
OracleDataReader guidReader = GetDataReader(sqlString);
while (guidReader.Read())
{
sqlString = "UPDATE T SET FK = " + guidReader["PK"];
}
Debugging this sqlString gets me the following value:
UPDATE SIS_T_USER SET FK_C_EMPLOYEE_ID
= System.Byte[]
Now, how do I go forth and save this as a nice guid in my oracle database?
EDit how:
OracleCommand command = new OracleCommand(sqlString, this.oracleConnection);
command.CommandType = CommandType.Text;
OracleParameter op1 = new OracleParameter("guid", OracleDbType.Raw);
op1.Value = guidReader["PK"];
command.Parameters.Add(op1);
try
{
command.ExecuteNonQuery();
}
catch (OracleException oex)
{
Console.WriteLine("Unable to update: {0}", oex.Message);
}
Why don't you just do this all on Oracle side?
MERGE
INTO sis_t_user s
USING employee e
ON (s.integer_fk = e.integer_pk)
WHEN MATCHED THEN
UPDATE
SET s.guid_fk = e.guid_pk
Try this code:
sqlString = "UPDATE T SET FK = '" + (new Guid((byte[])guidReader["PK"])).ToString() + "'";
Basically, you just need to create guid from bytes and then convert it to string.
There is Guid constructor that allows it: http://msdn.microsoft.com/en-us/library/90ck37x3(v=VS.100).aspx.

Categories

Resources