C# - SELECT Query number of rows affected is always -1 - c#

I have this code and it always returns -1.I have three tables (a picture is more suggestive ):
I want to see if the row is already in the ReservationDetails table, and if it's not to insert it.
try
{
SqlConnection conn = new SqlConnection...
SqlCommand slct = new SqlCommand("SELECT * FROM ReservationDetails WHERE rID=#rID AND RNumber=#RNumber", conn);
slct.Parameters.AddWithValue("#rID", (int)comboBox1.SelectedValue);
slct.Parameters.AddWithValue("#RNumber", dataGridView1.SelectedRows[0].Cells[0].Value);
int noRows;//counts if we already have the entry in the table
conn.Open();
noRows = slct.ExecuteNonQuery();
conn.Close();
MessageBox.Show("The result of select="+noRows);
if (noRows ==0) //we can insert the new row

Have you read the documentation of SqlCommand.ExecuteNonQuery?
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.
And your query is SELECT.

You should
1) Change your TSQL to
SELECT COUNT(*) FROM ReservationDetails WHERE ...
(better still, use IF EXISTS ...)
2) and use ExecuteScalar():
noRows = (int) slct.ExecuteScalar();
Also: you will need to use a transaction (or some other atomic technique), or else someone could insert a row in-between you testing and trying to insert it...
All that said, it would be better to create a stored procedure that given your parameters, atomically tests and inserts into the table, returning 1 if successful, or 0 if the row already existed.

It is better to do it in a single query so that you do not need to request server two times.
Create a procedure like this and call it from the code.
IF EXISTS (SELECT 1 from ReservationDetails WHERE rID=#rID AND RNumber=#RNumber)
BEGIN
insert into ReservationDetails values(#rID,#RNumber)
END

As per Microsoft:
You can use the ExecuteNonQuery to perform catalog operations (for example, querying the structure of a database or creating database objects such as tables), or to change the data in a database without using a DataSet by executing UPDATE, INSERT, or DELETE statements.
What you may need, instead of ExecuteNonQuery is ExecuteScalar and put the COUNT in your select query.
i.e.
SqlCommand slct = new SqlCommand("SELECT COUNT(*) FROM ReservationDetails WHERE rID=#rID AND RNumber=#RNumber", conn);
Also, try to make use of the using statement in C#, so you don't need to worry about closing the connection manually, even if things fail.
i.e.
using (SqlConnection conn = new SqlConnection(connString))
{
SqlCommand cmd = new SqlCommand(sql, conn);
try
{
conn.Open();
newProdID = (Int32)cmd.ExecuteScalar();
}
catch (Exception ex)
{
//Do stuff
}
}
see:
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executescalar.aspx

#nickNatra
When ever you use
Select command
It will return you values. Which can be either used by
DataSet or SqlDataReader
But
command.ExecuteNonQuery()
is used only when you are using
Insert , Update , Delete where the Rows are getting effected in your table
Yes, If you do want to know how much records are there in your query.
You can perform
a) Modify your query "select count(*) from table"
where you will only get one value ie. Number of Rows.
b) Using this query perform command. ExecuteScalar() which will return only First row and first column which is the Row Count
Hence this satisfy's your requirement.
Cheers!!

Related

Select query to get data from SQL Server

I am trying to run the SQL Select query in my C# code. But I always get the -1 output on
int result = command.ExecuteNonQuery();
However, the same table if I use for delete or insert works...
ConnectString is also fine.
Please check below code
SqlConnection conn = new SqlConnection("Data Source=;Initial Catalog=;Persist Security Info=True;User ID=;Password=");
conn.Open();
SqlCommand command = new SqlCommand("Select id from [table1] where name=#zip", conn);
//command.Parameters.AddWithValue("#zip","india");
int result = command.ExecuteNonQuery();
// result gives the -1 output.. but on insert its 1
using (SqlDataReader reader = command.ExecuteReader())
{
// iterate your results here
Console.WriteLine(String.Format("{0}",reader["id"]));
}
conn.Close();
The query works fine on SQL Server, but I am not getting why only select query is not working.
All other queries are working.
SqlCommand.ExecuteNonQuery Method
You can use the ExecuteNonQuery to perform catalog operations (for example, querying the structure of a database or creating database objects such as tables), or to change the data in a database without using a DataSet by executing UPDATE, INSERT, or DELETE statements.
Although the ExecuteNonQuery returns no rows, any output parameters or return values mapped to parameters are populated with data.
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.
SqlCommand.ExecuteScalar Method
Executes a Transact-SQL statement against the connection and returns the number of rows affected.
So to get no. of statements returned by SELECT statement you have to use ExecuteScalar method.
Reference: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executenonquery(v=vs.110).aspx
So try below code:
SqlConnection conn = new SqlConnection("Data Source=;Initial Catalog=;Persist Security Info=True;User ID=;Password=");
conn.Open();
SqlCommand command = new SqlCommand("Select id from [table1] where name=#zip", conn);
command.Parameters.AddWithValue("#zip","india");
// int result = command.ExecuteNonQuery();
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.Read())
{
Console.WriteLine(String.Format("{0}",reader["id"]));
}
}
conn.Close();
According to MSDN
http://msdn.microsoft.com/ru-ru/library/system.data.sqlclient.sqlcommand.executenonquery(v=vs.110).aspx
result is the number of lines affected, and since your query is select no lines are affected (i.e. inserted, deleted or updated) anyhow.
If you want to return a single row of the query, use ExecuteScalar() instead of ExecuteNonQuery():
int result = (int) (command.ExecuteScalar());
However, if you expect many rows to be returned, ExecuteReader() is the only option:
using (SqlDataReader reader = command.ExecuteReader()) {
while (reader.Read()) {
int result = reader.GetInt32(0);
...
}
}
you can use ExecuteScalar() in place of ExecuteNonQuery() to get a single result
use it like this
Int32 result= (Int32) command.ExecuteScalar();
Console.WriteLine(String.Format("{0}", result));
It will execute the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.
As you want only one row in return, remove this use of SqlDataReader from your code
using (SqlDataReader reader = command.ExecuteReader())
{
// iterate your results here
Console.WriteLine(String.Format("{0}",reader["id"]));
}
because it will again execute your command and effect your page performance.
That is by design.
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executenonquery(v=vs.110).aspx
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.
you have to add parameter also #zip
SqlConnection conn = new SqlConnection("Data Source=;Initial Catalog=;Persist Security Info=True;User ID=;Password=");
conn.Open();
SqlCommand command = new SqlCommand("Select id from [table1] where name=#zip", conn);
//
// Add new SqlParameter to the command.
//
command.Parameters.AddWithValue("#zip","india");
int result = (Int32) (command.ExecuteScalar());
using (SqlDataReader reader = command.ExecuteReader())
{
// iterate your results here
Console.WriteLine(String.Format("{0}",reader["id"]));
}
conn.Close();
You should use ExecuteScalar() (which returns the first row first column) instead of ExecuteNonQuery() (which returns the no. of rows affected).
You should refer differences between executescalar and executenonquery for more details.
Hope it helps!

c# alter table name add column

Command executed successfully look at here,..
string strConn = "Data Source=USER\\SQLExpress; Initial Catalog=empdata; Integrated Security=true";
SqlConnection conn = new SqlConnection(strConn);
conn.Open();
SqlCommand sqlCmd = new SqlCommand("alter table empreg ADD '"+ this.comboBox1.Text +"' varchar(50)", conn);
int res=sqlCmd.ExecuteNonQuery();
if (res == 1)
{ MessageBox.Show("New column created"); }
else
{ MessageBox.Show("sorry unable to create"); }
But it goes to 2nd part of if "sorry unable to create" result and the thing is that in SQL studio we can see that column created.
How is it possible ???
your result is the amount of affected rows, 1 doesn't mean you got an error.
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executenonquery%28v=vs.110%29.aspx
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.
From the MSDN page:
"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."
You need to handle res ==-1, not res ==1
from http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executenonquery(v=vs.110).aspx
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.
SqlCommand.ExecuteNonQuery => "Executes a Transact-SQL statement against the connection and returns the number of rows affected."
It will always return number of rows altered or not
Read more SqlCommand.ExecuteNonQuery
qlCommand sqlCmd = new SqlCommand("alter table empreg ADD '"+ this.comboBox1.Text +"' varchar(50)", conn);
correct
qlCommand sqlCmd = new SqlCommand("alter table empreg ADD "+this.comboBox1.Text+" varchar(50)", conn);
and dont need
if (res == 1)
{ MessageBox.Show("New column created"); }
else
{ MessageBox.Show("sorry unable to create"); }

Return last inserted ID without using a second query

I'm working on an ASP.NET project (C#) with SQL Server 2008.
When I insert a row into a table in the database, I would like to get the last inserted ID, which is the table's IDENTITY (Auto Incremented).
I do not wish to use another query, and do something like...
SELECT MAX(ID) FROM USERS;
Because - even though it's only one query - it feels lame...
When I insert something I usually use ExecuteNonQuery(), which returns the number of affected rows.
int y = Command.ExecuteNonQuery();
Isn't there a way to return the last inserted ID without using another query?
Most folks do this in the following way:
INSERT dbo.Users(Username)
VALUES('my new name');
SELECT NewID = SCOPE_IDENTITY();
(Or instead of a query, assigning that to a variable.)
So it's not really two queries against the table...
However there is also the following way:
INSERT dbo.Users(Username)
OUTPUT inserted.ID
VALUES('my new name');
You won't really be able to retrieve this with ExecuteNonQuery, though.
You can return the id as an output parameter from the stored procedure, e.g. #userId int output
Then, after the insert, SET #userId = scope_identity()
even though it's only one query - it feels lame...
It actually is also wrong as you can have multiple overlapping iserts.
That is one thing that I always fuind funny - people not reading the documentation.
SELECT SCOPE_IDENTITY()
returns the last identity value generated in a specific scope and is syntactically correct. It also is properly documented.
Isn't there a way to return the last inserted ID without using another query?
Yes. Ask for the number in the saame SQL batch.
INSERT (blablab9a); SELECT SCOPE_IDENTITY ();
as ONE string. ExecuteScalar.
You can have more than one SQL statement in one batch.
If you want to execute query from C# code & want to get last inserted id then you have to find the following code.
SqlConnection connection = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
connection.Open();
string sql = "Insert into [Order] (customer_id) values (" + Session["Customer_id"] + "); SELECT SCOPE_IDENTITY()";
SqlCommand cmd = new SqlCommand();
cmd.Connection = connection;
cmd.CommandText = sql;
cmd.CommandType = CommandType.Text;
var order_id = cmd.ExecuteScalar();
connection.Close();
Console.Write(order_id);

how to get the next autoincrement value in sql

I am creating a winform application in c#.and using sql database.
I have one table, employee_master, which has columns like Id, name, address and phone no. Id is auto increment and all other datatypes are varchar.
I am using this code to get the next auto increment value:
string s = "select max(id) as Id from Employee_Master";
SqlCommand cmd = new SqlCommand(s, obj.con);
SqlDataReader dr = cmd.ExecuteReader();
dr.Read();
int i = Convert.ToInt16(dr["Id"].ToString());
txtId.Text = (i + 1).ToString();
I am displaying on a textBox.
But when last row from table is deleted, still I get that value which is recently deleted in textbox
How should I get the next autoincrement value?
To get the next auto-increment value from SQLServer :
This will fetch the present auto-increment value.
SELECT IDENT_CURRENT('table_name');
Next auto-increment value.
SELECT IDENT_CURRENT('table_name')+1;
------> This will work even if you add a row and then delete it because IDENT_CURRENT returns the last identity value generated for a specific table in any session and any scope.
try this:
SELECT IDENT_CURRENT('tbl_name') + IDENT_INCR('tbl_name');
If you are using Microsoft SQL Server. Use this statement to get current identity value of table. Then add your seed value which you have specified at time of designing table if you want to get next id.
SELECT IDENT_CURRENT(<TableName>)
As for me, the best answer is:
dbcc checkident(table_name)
You will see two values (probably same)
current identity value , current column value
When you delete a row from the table the next number will stay the same as it doesnt decrement in any way.
So if you have 100 rows and you deleted row 100. You would have 99 rows but the next number is still going to be 101.
select isnull((max(AddressID)+1),1) from AddressDetails
the max(id) will get you maximum number in the list pf employee_master
e.g. id = 10, 20, 100 so max will get you 100
But when you delete the record it must have been not 100
So you still get 100 back
One important reason for me to say this might be the issue because you are not using order by id in your query
For MS SQL 2005 and greater:
Select Cast(IsNULL(last_value,seed_value) As Int) + Cast(increment_value As Int) As NextID
From sys.identity_columns
WHERE NAME = <Table_Name>
Just a thought, if what you wanted was the last auto-number that you inserted on an already open connection try using:
SELECT ##IDENTITY FROM...
from that connection. That's the best way to keep track of what has just happened on a given connection and avoids race conditions w/ other connections. Getting the maximum identity is not generally feasible.
SqlConnection con = new SqlConnection("Data Source=.\SQLEXPRESS;Initial Catalog=databasename;User ID=sa;Password=123");
con.Open();
SqlCommand cmd = new SqlCommand("SELECT TOP(1) UID FROM InvoiceDetails ORDER BY 1 DESC", con);
SqlDataReader reader = cmd.ExecuteReader();
//won't need a while since it will only retrieve one row
while (reader.Read())
{
string data = reader["UID"].ToString();
//txtuniqueno.Text = data;
//here is your data
//cal();
//txtuniqueno.Text = data.ToString();
int i = Int32.Parse(data);
i++;
txtuid.Text = i.ToString();
}

Why does IDataReader lose a row?

I am facing this problem. I have a stored procedure which returns 6 rows when I execute it.
But when I am retrieving the rows in my app by using ExecuteReader, it only returns only 5 rows. Why is it losing a row??
My stored procedure consists of 5 union statements which are getting filled from a single table:
dbase.AddInParameter(cmd, "#LoginUser", DbType.String, UserID);
try
{
using (IDataReader dr = dbase.ExecuteReader(cmd))
if (dr.Read())
{
dt = new DataTable("DashBoard");
dt.Load(dr);
}
}
dbase is my database object. And cmd is the SqlCommand used to call the stored procedure.
UserID is parameter is passing
Stored procedure code is:
ALTER PROCEDURE [dbo].[USP_ViewAdminDashBoard](#LoginUser varchar(75))
-- Add the parameters for the stored procedure here
AS
BEGIN
SET NOCOUNT ON;
SET DATEFORMAT DMY;
DECLARE #LastLoginDate as DateTime
Select #LastLoginDate = dbo.UDF_GetLastLoginByUser(#LoginUser)
Select 'Last Login Date', convert(varchar(12),#LastLoginDate,105)
Union
Select 'Nos. Records pending for Upload' as Title, convert(varchar(5),COUNT(s.BatchID)) Total from dbo.BREGISTRATIONENTRY s, Dbo.TBL_iBATCH B
where B.BatchID = s.BatchID And b.Forwarded = 0 and b.isBatchClosed = 1
END
Your first dr.Read is advancing to the first row. The DataTable.Load is reading the remaining rows but not the first row
Use HasRows to test for existence and don't use Read before the DataTable.Load
Edit:
Just load the DataTable without testing first: then test on the DataTable. There is no HasRows in the IDataReader interface.
IDataReader is a forward reader,it means when you read a row,that row will be deleted from the reader and you can't get it from the reader any more.
joshua i also has this problem while using enterprise library more than two time even i use same code written down but both time i found a problem in my store procedure there must be any wrong selection in query or any think else which db server does not detect and i solve it and
my IDataReader select all rows and also use same way as suggest by 'gbn'
using (IDataReader dr = oDb.ExecuteReader(p_oDbCommand))
{
if (dr != null)
{
ds.Tables[0].Load(dr);
}
}
return dt;

Categories

Resources