C# Using ExecuteScalar() Method With Where Clause in SqlCommand - c#

try
{
SqlCommand sqlCommand = new SqlCommand("SELECT COUNT(*) FROM Doctor WHERE Id = #id", sqlServerConnection);
sqlCommand.Parameters.AddWithValue("#id", id.Text);
int numberOfDoctors = (int) sqlCommand.ExecuteScalar();
if(numberOfDoctors == 1)
{
Console.WriteLine("Doctor is already in database.");
}
else
{
Console.WriteLine("There is no doctor with this Id.");
}
}
catch (Exception exc)
{
Console.WriteLine(exc);
}
I have a code like this. I have an application that has a connection with SQL database. Firstly, sqlServerConnection object is defined in the code correctly. There is no problem with that object. I have a Doctor table in the database. id. Text comes from Text element that is the value user typed in.
I want to be sure about whether this typed id is already in my database or not. Whichever value
I type in I always see "Doctor is already in database." message in the console. When I remove
WHERE clause from sqlCommand code works correctly. But when I add WHERE clause I can't track
whether this user with the given id is in my database or not. Could you please help me? Thank you
for your responses in advance. Have a great day :)

There are a number of issues with your code:
You should specify the type and length of the parameter explicitly
You need to dispose the connection and command objects
There is no need to use SELECT COUNT if there is only one row, you can just do SELECT 1
const string query = #"
SELECT 1
FROM Doctor
WHERE Id = #id;
";
try
{
using (var connection = new SqlConnection(yourConnString))
using (var sqlCommand = new SqlCommand(, sqlServerConnection);
{
sqlCommand.Parameters.Add("#id", SqlDbType.Char, 7).Value = id.Text;
connection.Open();
int numberOfDoctors = (sqlCommand.ExecuteScalar() as int) ?? 0; // will be null if no rows
connection.Close();
if(numberOfDoctors == 1)
{
Console.WriteLine("Doctor is already in database.");
}
else
{
Console.WriteLine("There is no doctor with this Id.");
}
}
}
catch (Exception exc)
{
Console.WriteLine(exc);
}
If there is no unique key on that column, you can instead do EXISTS:
SELECT CASE WHEN EXISTS (SELECT 1
FROM Doctor
WHERE Id = #id)
THEN 1 ELSE 0 END;

In practice you don't need to COUNT the whole table only to discover if your record exists or not.
try
{
string cmdText = #"IF EXISTS(SELECT 1 FROM Doctor WHERE Id = #id)
SELECT 1 ELSE SELECT 0";
SqlCommand sqlCommand = new SqlCommand(cmdText, sqlServerConnection);
sqlCommand.Parameters.Add("#id", SqlDbType.Char, 7).Value = id.Text;
int docExist = (int)sqlCommand.ExecuteScalar();
if(docExist == 1)
{
Console.WriteLine("Doctor is already in database.");
}
else
{
Console.WriteLine("There is no doctor with this Id.");
}
}
catch (Exception exc)
{
Console.WriteLine(exc);
}
The IF EXIST will stop to search if the a record exists while COUNT will do what is supposed to do, count the record that satisfy the condition till the end of the table.

Related

C# winforms Why messageboxes dont work with IF for DataReader

I just write my very first program in C#(winforms) VS2019.
So far, so good, but i stuck here.
In code below i try to
Get data from datapicker named dtDataClear, convert it do string for sql select
Check if textbox 'tbMagClear' is empty
If empty - run delete command for sql base limited by date from dtDataClear
But if not empty i still have two actions to do
If string in tbMagClear exist in table3 get his id and use it for where in delete command
If string do not exist - throw message
Now, i have 2 main problems
first - i tried to check, if select for Table3 actually works,
but testing messagebox with id dont appear (if it will work, message will be change to sql delete)
second - messagebox which is supposed to throw 'Not found' if string dont exist in database gets error in VS:
Cannot assign to 'Show' because it is a 'method group'
What is wrong here?
private void btMagClear_Click(object sender, EventArgs e)
{
string connectionString = $#"Data Source={tbSerwer.Text}; Initial Catalog={tbBaza.Text};User ID={tbLogin.Text}; Password={tbPassword.Text}";
try
{
SqlConnection sqlConnection;
sqlConnection = new SqlConnection(connectionString);
sqlConnection.Open();
string DataMa = dtDataClear.Value.Year.ToString() + dtDataClear.Value.Month.ToString() + dtDataClear.Value.Day.ToString();
if (string.IsNullOrWhiteSpace(tbMagClear.Text))
{
SqlCommand cmd = new SqlCommand($#"delete from TABLE where CODE not in (select CODE from TABLE2 where DATA > '{DataMa}' )", sqlConnection);
cmd.ExecuteNonQuery();
}
else
{
SqlCommand sqlCommandSelect;
SqlDataReader sqlDataReader;
string sqlSelect = "";
sqlSelect = $#"select id from TABLE3 where CODE2 = '{tbMagClear.Text}'";
sqlCommandSelect = new SqlCommand(sqlSelect, sqlConnection);
sqlDataReader = sqlCommandSelect.ExecuteReader();
int MagID = sqlDataReader.GetInt32(0);
if (sqlDataReader.HasRows)
{
MessageBox.Show(Convert.ToString(MagID));
}
else
{
MessageBox.Show = ("Not Found");
}
sqlDataReader.Close();
sqlCommandSelect.Dispose();
}
// MessageBox.Show(DataMa);
}
catch (Exception)
{
}
}
Ok, i got this
Thank You all - second problem was indeed an actual blindness of me
As for problem with DataReader, reading an exception was brilliant ;)
'Invalid attempt to read when no data is present'
so this code works:
sqlCommandSelect = new SqlCommand(sqlSelect, sqlConnection);
sqlDataReader = sqlCommandSelect.ExecuteReader();
if (sqlDataReader.HasRows)
{
while (sqlDataReader.Read())
{
MessageBox.Show(Convert.ToString(sqlDataReader.GetInt32(0)));
}
}
else
{
MessageBox.Show("Not Found");
}

The specified field 'ID' could refer to more than one table

I keep getting this error when I try to find by ID:
system.data.oledb.oledbexception the speciefied field 'ID' could refer
to more than one table listed in the FROM clause of your SQL Statement
Here's my code:
public static Invoice GetInvoice(string id)
{
OleDbConnection conn = GetConnection();
Invoice invoice = null;
if (conn == null)
{
return null;
}
string sqlString = "SELECT * FROM Person INNER JOIN Employee ON " +
"Person.ID=Employee.ID WHERE ID = #ID";
OleDbCommand comm = new OleDbCommand(sqlString, conn);
comm.Parameters.AddWithValue("#ID", id);
OleDbDataReader dr = null;
try
{
conn.Open();
dr = comm.ExecuteReader(CommandBehavior.SingleRow);
if (dr.Read())
{
invoice = new Invoice();
invoice.PersonID = (string)dr["ID"];
invoice.FirstName = (string)dr["FirstName"];
invoice.LastName = (string)dr["LastName"];
invoice.Age = (int)dr["Age"];
}
}
catch (Exception ex)
{
invoice = null;
MessageBox.Show(ex.ToString());
}
finally
{
if (conn.State == ConnectionState.Open)
{
conn.Close();
}
}
return invoice;
}
You need to change your query, at the moment you're selecting a wildcard '*', which means it will pull both the Persons ID and the Employee ID, but wont have a unique reference. Change your wildcard to pull the exact tables ID like below:
SELECT Person.ID, FirstName, LastName FROM...
You will also need to change your WHERE statement to something like:
WHERE Person.ID = #ID
as the where statement doesnt know which tables ID to filter on (i know they're the same values, but SQL doesnt care about that)

C# confirm table row has been deleted from database

I've written a method which will try and delete a row from a db table based on a primary key id. The problem i have is that the try block is always returning "Success" even if a record has already been deleted / or it doesn't exist.
public string delete_visit(int id)
{
string deleteResponse = null;
string cnn = ConfigurationManager.ConnectionStrings[connname].ConnectionString;
using (SqlConnection connection = new SqlConnection(cnn))
{
string SQL = string.Empty;
SQL = "DELETE FROM [" + dbname + "].[dbo].[" + tbname + "] WHERE VisitorNumber = #IDNumber ";
using (SqlCommand command = new SqlCommand(SQL, connection))
{
command.Parameters.Add("#IDNumber", SqlDbType.Int);
command.Parameters["#IDNumber"].Value = id;
try
{
connection.Open();
command.ExecuteNonQuery();
deleteResponse = "Success";
}
catch (Exception ex)
{
deleteResponse = "There was a problem deleting the visit from the database. Error message: " + ex.Message;
}
}
}
return deleteResponse;
}
I want to be able to tell if the row was affected. I can do this in SQL Server Management Studio like so:
DELETE FROM Visits
WHERE VisitorNumber=88;
IF ##ROWCOUNT = 0
PRINT 'Warning: No rows were updated';
So i want to know how do i plug in the ##ROWCOUNT bit into my c# so that i can tell if the row was deleted?
thanks
ExecuteNonQuery() returns an int, indicating how many rows were affected.
So:
int rowsAffected = command.ExecuteNonQuery();
if (rowsAffected == 0)
{
deleteResponse = "No rows affected";
}
The problem is that this number can be influenced based on what the query actually does. Executing triggers or calling stored procedures could mess with the output, changing the affected number of rows. If you really must, then first execute a query where you check that the record with the given ID exists.

How can I avoid duplicate items in a checkListBox

I am working on my windows form application using c#. I have a checkListBox that is binded to db. I am wondering is there a way to delete any duplicate record from the database?
Here is my code
private void fill_checkListBox()
{
try
{
string query = "select * from table_1 ";
SqlCommand myTeacherCommand = new SqlCommand(query, myConn);
//reading the value from the query
dr = myCommand.ExecuteReader();
//Reading all the value one by one
teacherCB.Items.Clear();
while (dr.Read())
{
string name = dr.IsDBNull(2) ? string.Empty : dr.GetString(2);
teacherCB.Items.Add(name);
if (!checkBox.Items.Contains(name))
{
teacherCB.Items.Add(name);
}
}
dr.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
The first answer - use DISTINCT in query:
select distinct * from table_1
Also, I advice you to specify column names in query:
select distinct ID, Name from table_1
But I don't know anything about your data in table.

how to generate unique max id from Data Base

I create a Applicant Registration form where Applicant Registration ID is generated by "Select Max(ID)...." Query. when this query is fetch out the MaxID from Data Base then i increment this ID by Plus one(+1) in this way i generate a Registration ID for all Applicant who register himself. But there is one problem occurred when i run my project from server and multiple clients (Approximately 10) Applicant try to Register then there is "primary key violation exception" occurred. There is 5 Insert Query is executed one-by-one after Max Query is executed
Code is Given Below
public long getUid()
{
try
{
string qry = "select isnull(max(Temp_Applicant_RegNo),0) as appregno FROM Temp_Reg";
if (cs.State == ConnectionState.Closed)
{
cs.Open();
}
cmd = new SqlCommand(qry, cs);
dr = cmd.ExecuteReader();
long cid = 0;
if (dr.Read())
{
cid = long.Parse(dr["appregno"].ToString());
cid++;
}
if (cs.State == ConnectionState.Open)
{
cs.Close();
}
return cid;
}
catch (Exception ex)
{
lbl_ErrorMsg.Text = ex.Message;
return 0;
}
}
protected void Save_btn_Click(object sender, EventArgs e)
{
SqlTransaction trn = null;
try
{
long Regid = getUid();
con.Open();
trn = con.BeginTransaction();
cmd = new SqlCommand("insert into....", con, trn);
cmd.Parameters.AddWithValue("#RegNo", Regid);
cmd.ExecuteNonQuery();
cmd = new SqlCommand("insert into....", con, trn);
cmd.Parameters.AddWithValue("#RegNo", Regid);
cmd.ExecuteNonQuery();
cmd = new SqlCommand("insert into....", con, trn);
cmd.Parameters.AddWithValue("#RegNo", Regid);
cmd.ExecuteNonQuery();
trn.Commit();
}
catch (Exception ex)
{
lbl_ErrorMsg.Text = ex.Message;
trn.Rollback();
}
}
Please give me suggestion that how can i generate Max ID for Applicant so that there is no chance to any duplication. Because i am working in live project.
for there i am using Asp.net C#
Don't do that, let the database generate the key for you when you insert a new row. Here is for example how to do it with SQL Server or MySQL. If you really want to do it on the client, use GUIDs as keys because you can generate them without consulting the database. There are some minor issues with GUIDs as keys because they are usually partially random and this may have negative performance effects on clustered indices, but for 99.9 % of all databases they are just fine.
Solution 1:
You can create a table with only one column, For example "GeneralID", and in your application control this Id to insert in another tables.
Solution 2:
Another solution is create a table and only one column, and a trigger to each table before insert to popule the id getting frmo the "Ids table" to insert.
Before we had auto incrementing columns we would have a table to hold the IDs. It would only have 2 columns IdType varchar(10) and NextId int. Then in a stored proc we would have something like:
while(1=1)
begin
select #nextId = nextId
from MyIds
where IdType=#IdType
update MyIds
set nextId = #nextId + 1
where IdType=#IdType
and nextId = #nextId
if(##ROWCOUNT > 0)
break;
end
select #nextId as nextId
Note that this would only update in the second statement if the nextId didn't change. If it did change it would try again.
I don't see when do you save the new value into Temp_Reg, but how if you save that value immediately after calculation, then you can safety update others tables:
public long getUid()
{
try
{
string qry = "select isnull(max(Temp_Applicant_RegNo),0) as appregno FROM Temp_Reg";
if (cs.State == ConnectionState.Closed)
{
cs.Open();
}
cmd = new SqlCommand(qry, cs);
dr = cmd.ExecuteReader();
long cid = 0;
if (dr.Read())
{
cid = long.Parse(dr["appregno"].ToString());
cid++;
}
UPDATE Temp_reg HERE !!!!
if (cs.State == ConnectionState.Open)
{
cs.Close();
}
return cid;
}
catch (Exception ex)
{
lbl_ErrorMsg.Text = ex.Message;
return 0;
}
}
Then you'll have to take this update into account inside the catch block of Save_btn_Click.
All of this if you can't use Autonumbers but If you can, use it.
If you could change your field Temp_Applicant_RegNo in an IDENTITY column, then you don't need to worry about the next number assigned to your table Temp_Reg. It is all a jopb made by your database.
All you need to know is what number has been assigned and use that number in your subsequent inserts.
This could be easily done using the SELECT SCOPE_IDENTITY() after the insert in your
protected void Save_btn_Click(object sender, EventArgs e)
{
SqlTransaction trn = null;
try
{
con.Open();
trn = con.BeginTransaction();
cmd = new SqlCommand("insert into....; SELECT SCOPE_IDENTITY()", con, trn);
int Regid = Convert.ToInt32(cmd.ExecuteScalar());
cmd = new SqlCommand("insert into....", con, trn);
cmd.Parameters.AddWithValue("#RegNo", Regid);
cmd.ExecuteNonQuery();
cmd = new SqlCommand("insert into....", con, trn);
cmd.Parameters.AddWithValue("#RegNo", Regid);
cmd.ExecuteNonQuery();
trn.Commit();
}
catch (Exception ex)
{
lbl_ErrorMsg.Text = ex.Message;
trn.Rollback();
}
The trick is possible adding the SELECT SCOPE_IDENTITY() as a second query to the first insert and then call ExecuteScalar. ExecuteScalar executes the queries and then returns the value of the first column in the first row of the last query executed.

Categories