Switch case in SQL 2008 Server R2 - c#

I've been trying to create a search function on a webpage / form. It's a bit complicated since it has multiple search boxes.
It has to search the sql database and return the right row(s).
I was looking for a switch equivalent to execute a certain query if a value is null or not. typically if the user doesn't enter the student ID (primary key) then it will go the queries that doesn't include the ID parameter. Not sure if it's the right logic. My knowledge in sql is quite limited atm, sorry if it's a basic question but i didn't find a solution on google since apparently nobody ever needed to do this. I'm looking a stored procedure of course
Here's what I'm thinking of firing into a SQL
using (SqlCommand cmd = new SqlCommand("Student_CRUD"))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#Action", "SEARCH");
cmd.Parameters.AddWithValue("#StudentID", StudentID);
cmd.Parameters.AddWithValue("#FirstName", Name);
cmd.Parameters.AddWithValue("#Class", Class);
cmd.Parameters.AddWithValue("#RollNumber", RollNumber);
cmd.Connection = con;
con.Open();
try
{
cmd.ExecuteNonQuery();
con.Close();
}
catch (SqlException exp)
{
Response.Write(exp);
}
}

You can do this in the query inside the stored procedure:
WHERE FirstName = #FirstName
AND (#StudentID IS NULL OR (StudentID = #StudentID))
AND (#Class NULL OR (Class = #Class))
....
But, you have to set the #studentID value to null (DBNull.Value) in your code, so that the null value will be passed to the sql query. So when passing a null value to this query, the condition will be ignored.

Related

How to override "duplicate entry warning" when run an append query (Access db, from c# asp.net)?

I have a query in the access database that appends email addresses from one table to another. In the source table there may be many duplicated emails. In the target table the email field is unique. Before running the query, there may already in the target table emails that are in the source table.
When running the query manually, I get a message about duplicates and I have to click on Yes to run the query anyway.
But when I try to run the query from the asp.net, c#, I get an exception and nothing is done.
How can I override the exception? I mean, how can I "tell" the system to assume I clicked on Yes after the warning?
Here is my code:
public static void RunQuery(string db, string command)
{
OleDbConnection conn = getConn(db); //set the value for conn
try
{
if (conn != null) conn.Close();
conn.Open();
OleDbCommand cmd = conn.CreateCommand();
cmd.CommandText = command; // query name
cmd.ExecuteNonQuery();
conn.Close();
}
catch (Exception e)
{
log.Error(e);
throw;
}
finally
{
if (conn != null)
conn.Close();
}
}
Thanks
It has never been clear when/how the error msg of duplicates will trigger.
I find that this (in most cases) will run the append query, and errors are ignored.
This:
// run append query
using (OleDbConnection conn =
new OleDbConnection(Properties.Settings.Default.AccessDB))
{
using (OleDbCommand cmdSQL =
new OleDbCommand("AddToHotelss", conn))
{
conn.Open();
cmdSQL.CommandType = CommandType.StoredProcedure;
cmdSQL.ExecuteNonQuery();
}
}
Now, the above runs this saved query in Access:
INSERT INTO tblHotels2 ( FirstName, LastName, HotelName, EmailName )
SELECT tblHotels.FirstName, tblHotels.LastName, tblHotels.HotelName,
tblHotels.EmailName
FROM tblHotels
WHERE (tblHotels.City = "Edmonton");
And I have a unique index on EmailName - and the above works.
So, give the "setting" of CommandType = StoredProcedure.
The above SHOULD work.
If it does not?
Then you have to consider introduction of the office.interop ACE data engine. (I don't think that's a good idea).
Or, if above idea does not work?
You have to modify your query to prevent duplicates in the first place..
The query then becomes this:
INSERT INTO tblHotels2 ( FirstName, LastName, HotelName, EmailName )
SELECT tblHotels.FirstName, tblHotels.LastName, tblHotels.HotelName,
tblHotels.EmailName
FROM tblHotels
WHERE (tblHotels.City="Edmonton")
AND
(tblHotels.EmailName NOT IN (SELECT EmailName from tblHotels2.EmailName));
So, as suggested, you re-write the query to never allow duplicates.
I would however give the commandText setting (StoredProcedure) as per above - since in the past I had that work for me. (the duplicates message did not trigger, or stop the append).

SqlException : Incorrect syntax near '1'

I am currently trying to implement SQL into a project with Unity3D. So far, I was able to do "normal" UPDATE, ADD, DELETE, DROP, ALTER, INSERT".
Trying to go a step further, I am trying to insert prepared statements, using this link as a guide
Here is my code :
SqlConnection sqlConnection = new SqlConnection(Connection.connectionString)
sqlConnection.Open();
SqlCommand cmd = new SqlCommand(null, sqlConnection);
cmd.CommandText = "INSERT INTO IngredientTypes (Name) VALUES (#name)";
SqlParameter nameParam = new SqlParameter("#name", SqlDbType.Text, 155);
nameParam.Value = Name;
cmd.Parameters.Add(nameParam);
cmd.Prepare();
cmd.ExecuteNonQuery();
My table looks like so :
CREATE TABLE IngredientTypes
(
IngredientTypeID INT IDENTITY(1,1) PRIMARY KEY,
Name VARCHAR(155)
);
I get this error :
SQLException : Incorrect systax near '1'.
System.Data.SqlClient.SqlConnection.ErrorHandler (System.Object sender, Mono.Data.Tds. Protocol.TdsInternalErrorMessageEventArgs e)
Help please? Thank you in advance.. I can't find where I did wrong.
You can reduce that code quite a bit with no loss of function, and even some important improvements (for example, this will close the connection even if an exception is thrown):
using (var sqlConnection = new SqlConnection(Connection.connectionString))
using (var cmd = new SqlCommand("INSERT INTO IngredientTypes (Name) VALUES (#name)", sqlConnection))
{
cmd.Parameters.Add("#name", SqlDbType.VarChar, 155).Value = Name;
sqlConnection.Open();
cmd.ExecuteNonQuery();
}
I'm not sure what's causing that exception in your existing code, though, because 1 is not used anywhere in that query. I suspect the problem has something to do with SqlDbType.Text, since that is not the correct type to use with a VarChar column, but it seems just as likely there's code somewhere we haven't seen yet that's changing your SQL command text.
Definitely the Prepare() method in your link is not needed for Sql Server. It's inherited here from DbCommand, where it's included because it's an important part of the API for some other databases, but Sql Server has handled this automatically for more than 10 years now.
SqlDbType.Text Is not the same as varchar. I don’t believe Text types have a length you specify.
Could you try below? Using the "using" structure is safer for sql connections by the way, the connection automatically closes when your process is done.
using (SqlConnection sqlConnection = new SqlConnection(Connection.connectionString))
{
SqlCommand command = new SqlCommand("INSERT INTO IngredientTypes (Name) VALUES (#name)", connection);
command.Parameters.Add("#name", SqlDbType.Varchar, 155);
command.Parameters["#name"].Value = Name; //make sure Name is string.
try
{
sqlConnection.Open();
command.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
I tried your code exactly as it is and found no issue. Though there are few compilation errors (missing ; in line 1 and Name variable should be coming as parameter) but I am sure you know that. If you have posted your table structure and code exactly the same as you have in your project, then there is no problem in this code.

How to get UUID generated by MySQL into a C# variable

Here is the query:
string query = #"INSERT INTO session (PK_Id, user_id, login_time, machine_ip, machine_fingerprint)
VALUES (UUID(), #UId, #LogInTime, #MIp, #MFingerPrint);
";
Now I need this last inserted id back, which is a UUID generated by MySQL. As far as I read there is no select_last_insert_id() function for UUIDs!! And I read for php you could assign UUID() function first to a variable and then return that value. But how to go about that in C#?
Something like this, but not exactly:
string query = #"INSERT INTO session (PK_Id, user_id, login_time, machine_ip, machine_fingerprint)
VALUES (#UUID = SELECT UUID(), #UId, #LogInTime, #MIp, #MFingerPrint);
"; //how to do this here?
Here is more of my code:
string query = #"INSERT INTO session (PK_Id, user_id, login_time, machine_ip, machine_fingerprint)
VALUES (#UUID = SELECT UUID(), #UId, #LogInTime, #MIp, #MFingerPrint);
";
try
{
if (_conn.State != ConnectionState.Open)
_conn.Open();
MySqlCommand cmd = new MySqlCommand(query, _conn);
cmd.Parameters.AddWithValue("#UId", Utility.usr.Id);
cmd.Parameters.AddWithValue("#LogInTime", DateTime.Now);
cmd.Parameters.AddWithValue("#MIp", GetMachineIP());
cmd.Parameters.AddWithValue("#MFingerPrint", GetHardwareFingerPrint());
var s= Convert.ToString(cmd.ExecuteScalar()); //this returns an empty string :(
//I need to get it to any .NET data type, string, or Guid or byte[] or anything.
But I need this datatype of s to be used in another WHERE clause in a query like this:
string query = #"UPDATE session SET logout_time = #LogOutTime
WHERE user_id = #UId AND PK_Id = #SessionId";
try
{
if (_conn.State != ConnectionState.Open)
_conn.Open();
MySqlCommand cmd = new MySqlCommand(query, _conn);
cmd.Parameters.AddWithValue("#UId", Utility.usr.Id);
cmd.Parameters.AddWithValue("#SessionId", s);
cmd.Parameters.AddWithValue("#LogOutTime", DateTime.Now);
cmd.ExecuteScalar();
Here #"SessionId" is the UUID field in the same table. So basically, how can I get the MySQL varbinary field in C# so that I could use that type to update by specifying WHERE in another query?
In MySQL table the UUID field is varbinary (I hope to see some solution that is not another php link or that is not asking me to switch to char datatype in the database :) ).
Edit: The problem here is we have already added plenty of UUIDs generated by MySQL into the table, so I'm a bit apprehensive about changing MySQL UUID to .NET Guid. If that's the only workaround, I'll consider that. Just that this is the first time we needed the inserted UUID value back so that I can update in another query another point of time.
A sub question: Is .NET Guid exactly the same thing as MySQL UUID?
You can use the Guid type which is the MS implementation of UUID. You should be aware that when inserting data into the DB, you may need to convert the Guid to ByteArray if the MySQL driver isn't familiar with handling Guid's. See Store GUID in MySQL from C# for an example of this.
I think you can go ahead with your earlier implementation without having to rely on MS Guid, but I fear I am too late :)
string query = #"INSERT INTO session (PK_Id, user_id, login_time, machine_ip, machine_fingerprint)
VALUES (UUID(), #UId, #LogInTime, #MIp, #MFingerPrint);
SELECT PK_Id FROM session WHERE login_time=#LogInTime AND machine_fingerprint=#MFingerPrint; //or something similar which gives you the exact same id - UUID
";
try
{
if (_conn.State != ConnectionState.Open)
_conn.Open();
MySqlCommand cmd = new MySqlCommand(query, _conn);
cmd.Parameters.AddWithValue("#UId", Utility.usr.Id);
cmd.Parameters.AddWithValue("#LogInTime", DateTime.Now);
cmd.Parameters.AddWithValue("#MIp", GetMachineIP());
cmd.Parameters.AddWithValue("#MFingerPrint", GetHardwareFingerPrint());
MySqlDataReader r = cmd.ExecuteReader();
if (r.Read()) //ensure if it is read only once, else modify your `WHERE` clause accordingly
{
var s = (Guid)r[0];
}
//or even (Guid)cmd.ExecuteScalar() would work
Now you can query in update like this:
string query = #"UPDATE session SET logout_time = #LogOutTime
WHERE user_id = #UId AND PK_Id = #SessionId";
try
{
if (_conn.State != ConnectionState.Open)
_conn.Open();
MySqlCommand cmd = new MySqlCommand(query, _conn);
cmd.Parameters.AddWithValue("#UId", Utility.usr.Id);
cmd.Parameters.AddWithValue("#SessionId", s.ToByteArray());
cmd.Parameters.AddWithValue("#LogOutTime", DateTime.Now);
cmd.ExecuteNonQuery();
Note: Here I have converted the Guid variable s to byte array before querying. This is important, in WHERE clause, be it UPDATE or SELECT statements in query. I would ask you to move to binary field in MySQL table from varbinary.
Edit: If your table would grow dramatically large then inserting and selecting is a bad idea since SELECT query is an additional query being run. In that case #PinnyM's choice is better. I really do not think MySQL or any other database would have a default way to give back "custom" inserted ids which are not something database generated. So in short I advice you to not go for this..
Edit2: See this answer for getting binary value to .NET datatype. Sometimes casting do not work depending on MySQL .NET connector version..

Proper SQL query command with SQL Compact is failing

I've got a function that stores temporary information generated for every user authenticated in the system. This 'session ID' is a string stored in a Sessions table, along the original ID of the user which authenticated and was given said session identifier.
The function to remove/deauthenticate/invalidate an existing session first checks if the user exists through another method implemented as follows:
int userId = 0;
SqlCeCommand cmd = new SqlCeCommand();
SqlCeParameterCollection sqlParams = cmd.Parameters;
sqlParams.AddWithValue("#User", userName);
cmd.Connection = this.conn;
cmd.CommandText = "SELECT Id FROM Users WHERE (Username = #User)";
userId = (int) cmd.ExecuteScalar()
cmd.Dispose();
Afterwards it tries to find an existing session for that user, which is to be removed (via a different method again):
SqlCeCommand cmd = new SqlCeCommand();
SqlCeParameterCollection sqlParams = cmd.Parameters;
sqlParams.AddWithValue("#SID", mysession);
sqlParams.AddWithValue("#UID", myuserid);
cmd.Connection = this.Connection;
cmd.CommandText = "SELECT Id FROM UserSessions WHERE (SessionID = #SID) AND (User_Id = #UID)";
int foo = cmd.ExecuteNonQuery();
...which fails. No exception is raised unfortunately. So I added an insecure equivalent using a non parametrized query string:
cmd.CommandText = String.Format("SELECT Id FROM UserSessions WHERE (SessionID = '{0}') AND (User_Id = {1})", mysession, myuserid);
cmd.Prepare();
int bar = cmd.ExecuteNonQuery();
Added a breakpoint, paused, copy pasted the query into the Visual Studio Query tool and voila, it indeed worked. But after continuing, that query in the code failed as well. I'm unable to find the culprit of this annoying issue since no exception is raised and everything seems correct. The data exists, the parameters are provided in proper types (string and int) and I'm out of things to check. The connection is open and so forth.
Any clues from anyone around? Thanks!
Update: Mea culpa, missed the fact that the function used ExecuteScalar until I modified it for testing. It does use ExecuteScalar and returns null, just in case.
You're using ExecuteNonQuery:
int foo = cmd.ExecuteNonQuery();
... but you're clearly trying to execute a query (a SELECT)! Use ExecuteScalar again, as you did in the first code, or ExecuteReader and look through the results appropriately. If you stick with ExecuteScalar, you should first check whether the result is null to indicate no results.
ExecuteNonQuery returns the number of rows affected by an UPDATE/INSERT/DELETE command - which is what it's intended for. I suspect it's returning -1 for you, as documented:
For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command. When a trigger exists on a table being inserted or updated, the return value includes the number of rows affected by both the insert or update operation and the number of rows affected by the trigger or triggers. For all other types of statements, the return value is -1. If a rollback occurs, the return value is also -1.
(Emphasis mine.)
Use set [] to avoid ambiguity with database keyword.
cmd.CommandText = "SELECT [Id] FROM [Users] WHERE ([Username] = #User)";
and use ExecuteScalar() or ExecureReader() method when working with SELECT statements.

How to save a SQL "Select" result in a variable in C#

I'm using Visual C# connected to MySQL for study purposes and I'm stuck in throwing an error to the user when he types a username that already exists.
Current code to put things into the database (it may be useless, once my question may be much more about SQL):
s = new sql(); // This calls a class that works as an adapter to connect form with the database
Conn = s.Connection;
Conn.Open();
coma = Conn.CreateCommand();
coma.CommandText = "INSERT INTO test.test (`user`,`password`) VALUES ('"+username.Text+"','"+password.Text+"');";
coma.ExecuteNonQuery();
What I want to do it compare "username.Text" ("username" is a TextBox) with the values on database's "test" table and, if some value match, evoke a MessageBox.Show("Hey guy, this username is already in use! Try something different)
Some points about your code sample
You want to be sure that you dispose of your connection and command objects. For my answer, I've wrapped them in using statements which will take care of that for me.
You do not want to go to the database with unsanitized inputs. I am going to use parameterized queries in the example.
It's not a good idea to store passwords in plain text. I am not going to demonstrate more secure techniques, just know to look for information about encrypting passwords, salt keys, etc.
And now for some code. In this, I'm using OleDb objects, retrofit to your particular database. And, of course, provide appropriate names to tables, columns, etc.
using (OleDbConnection connection = SomeMethodReturningConnection())
using (OleDbCommand command = SomeMethodReturningCommand())
{
command.Parameters.Add(new OleDbParameter("#username", username));
command.CommandText = "Select Count(*) From Users where Username = #username";
connection.Open();
int output = (int)command.ExecuteScalar();
if (output > 0)
{
// username already exists, provide appropriate action
}
else
{
// perform insert
// note: #username parameter already exists, do not need to add again
command.Parameters.Add(new OleDbParameter("#password", password));
command.CommandText = "Insert Into Users (Username, Password) Values (#username, #password)";
command.ExecuteNonQuery();
}
}
Thank you Anthony! Your answer put me on the right track. Although there is something that the people who will read this post should change from your code in order to get it working with Odbc connectors: the way as parameters are parsed and the way as the textbox content is extracted:
using (OdbcConnection connection = SomeMethodReturningConnection())
using (OdbcCommand command = SomeMethodReturningCommand())
{
command.Parameters.Add(new OdbcParameter("#username", username.Text));
command.CommandText = "Select Count(*) From Users where Username = ?";
connection.Open();
int output = (int)command.ExecuteScalar();
if (output > 0)
{
// username already exists, provide appropriate action
}
else
{
// perform insert
// note: #username parameter already exists, do not need to add again
command.Parameters.Add(new OdbcParameter("#password", password.Text));
command.CommandText = "Insert Into Users (Username, Password) Values (?,?)**";
command.ExecuteNonQuery();
}
}
Thank you anyway!

Categories