I am a newbie in ASP.NET, having trouble in how to call an inline User Defined Function in my ASP.NET web application.
Here, I have passed two arguments in my function - one is available leave(lv) and another one is duration (dr). I am simply subtracting dr from lv and returning the value. But I am having problem in calling the function.
I have tried "SELECT dbo.emp_leave_sub(lv,dr) as remaining" instead of the query "SELECT dbo.emp_leave_sub(lv,dr) FROM Employee1 where Employee1.emp_id='" + emp_id + "'" but it didn't work. I can not understand what I am doing wrong.
Looking forward to your kind reply. Any help will be highly appreciated.
Below is my function :
ALTER FUNCTION dbo.emp_leave_sub(#available int, #duration int)
RETURNS int
AS
-- Returns the availabe leave after deduction for the employee.
BEGIN
DECLARE #ret int;
SELECT #ret = #available - #duration;
RETURN #ret;
END;
And this is from where I am calling my function :
try
{
SqlDataReader rdr;
SqlConnection conn = new SqlConnection (ConfigurationManager.
ConnectionStrings["PMSConnectionString"].ConnectionString);
conn.Open();
string sub_leave = "SELECT dbo.emp_leave_sub(lv,dr) FROM ` ` Employee1 where Employee1.emp_id='" + emp_id + "'";
SqlCommand com2 = new SqlCommand(sub_leave, conn);
com2.CommandType = CommandType.Text;
using (conn)
{
//read data from the table to our data reader
rdr = com2.ExecuteReader();
//loop through each row we have read
while (rdr.Read())
{
remaining = rdr.GetInt32(0);
}
rdr.Close();
}
Try to do this:
SqlDataReader rdr;
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["PMSConnectionString"].ConnectionString))
{
conn.Open();
string sub_leave = "SELECT dbo.emp_leave_sub(#available,#duration) FROM Employee1 where Employee1.emp_id=#empid";
SqlCommand com2 = new SqlCommand(sub_leave, conn);
com2.Parameters.AddWithValue("#available", your value);
com2.Parameters.AddWithValue("#duration", your value);
com2.Parameters.AddWithValue("#empid", emp_id);
com2.CommandType = CommandType.Text;
//read data from the table to our data reader
rdr = com2.ExecuteReader();
//loop through each row we have read
while (rdr.Read())
{
remaining = rdr.GetInt32(0);
}
}
rdr.Close();
Related
I have looked into materials in www.npgsql.org, but couldn't find how to solve my problem...
Table, PostgreSQL
[City], [State]
"Austin", "TX"
"Houston", "TX"
"Los Angeles", "CA"
"San Diego", "CA"
"San Fransisco";"CA"
"St.Louis", "MO"
Function (stored procedure), PostgreSQL
-- Procedure that returns a single result set (cursor)
CREATE OR REPLACE FUNCTION show_cities() RETURNS refcursor AS $$
DECLARE
ref refcursor;
BEGIN
OPEN ref FOR SELECT city, state FROM cities;
RETURN ref;
END;
$$ LANGUAGE plpgsql;
Code, C#
using (NpgsqlConnection conn = new NpgsqlConnection(ConfigurationManager.ConnectionStrings["dbConnection"].ConnectionString))
{
conn.Open();
using (NpgsqlTransaction tran = conn.BeginTransaction())
{
using (var command = new NpgsqlCommand("show_cities", conn))
{
command.Transaction = tran;
command.CommandType = CommandType.StoredProcedure;
NpgsqlDataReader dr = command.ExecuteReader();
while (dr.Read())
str += dr.GetValue(0);
dr.Close();
}
tran.Commit();
}
}
This returns "unnamed portal 1" and it's a cursor to be fetched not data,
Is there any way to convert this to data like Austin, Houston, Los Angeles... ?
There are some posts over internet about this, but I'm not sure what I'm doing wrong.
npgsql : ver3.0.3
c# : vs2012
(added)
I have found this is happening at npgsql ver3.x, while in ver2.x it is working fine with my code. Is there any change in usage for fetching cursor ?
(reference)
http://www.sqlines.com/postgresql/npgsql_cs_result_sets
With Shay's help, I figured out how we can fetch cursor in v3.x after removal of "dereferenced" feature.
I think there are not much of good example on this, I hope this might help people save time to search example.
You can do this in npgsql ver3.x
1. CommandType.StoredProcedure (cursor name not defined)
conn.Open();
NpgsqlTransaction tran = conn.BeginTransaction();
NpgsqlCommand command = new NpgsqlCommand("show_cities", conn);
command.CommandType = CommandType.StoredProcedure;
command.ExecuteNonQuery();
command.CommandText = "fetch all in \"<unnamed portal 1>\"";
command.CommandType = CommandType.Text;
NpgsqlDataReader dr = command.ExecuteReader();
while (dr.Read())
{
// do what you want with data, convert this to json or...
Console.WriteLine(dr[0]);
}
dr.Close();
tran.Commit();
conn.Close();
2. CommandType.StoredProcedure (cursor name defined)
conn.Open();
NpgsqlTransaction tran = conn.BeginTransaction();
NpgsqlCommand command = new NpgsqlCommand("select show_cities(#ref)", conn);
command.CommandType = CommandType.Text;
NpgsqlParameter p = new NpgsqlParameter();
p.ParameterName = "#ref";
p.NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Refcursor;
p.Direction = ParameterDirection.InputOutput;
p.Value = "ref";
command.Parameters.Add(p);
command.ExecuteNonQuery();
command.CommandText = "fetch all in \"ref\"";
command.CommandType = CommandType.Text;
NpgsqlDataReader dr = command.ExecuteReader();
while (dr.Read())
{
// do what you want with data, convert this to json or...
Console.WriteLine(dr[0]);
}
dr.Close();
tran.Commit();
conn.Close();
3. CommandType.Text (cursor name defined)
conn.Open();
NpgsqlTransaction tran = conn.BeginTransaction();
NpgsqlCommand command = new NpgsqlCommand("select show_cities(#ref)", conn);
command.CommandType = CommandType.Text;
NpgsqlParameter p = new NpgsqlParameter();
p.ParameterName = "#ref";
p.NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Refcursor;
p.Direction = ParameterDirection.InputOutput;
p.Value = "ref";
command.Parameters.Add(p);
command.ExecuteNonQuery();
command.CommandText = "fetch all in \"ref\"";
command.CommandType = CommandType.Text;
NpgsqlDataReader dr = command.ExecuteReader();
while (dr.Read())
{
// do what you want with data, convert this to json or...
Console.WriteLine(dr[0]);
}
dr.Close();
tran.Commit();
conn.Close();
(reference for v2.x) http://www.sqlines.com/postgresql/npgsql_cs_result_sets
This is example for easy understanding, so if you want your function to return single result set, then instead of returning cursor, please consider it to return table as #Shay suggested or create a view not a function as #CeOnSql suggested.
Thanks !
Npgsql 2.x had a feature whereby it automatically "dereferenced" cursors returned from functions. This feature was dropped from Npgsql 3.0; this is mentioned in our migration nodes for 3.0, and the discussion is in this issue. Since the cursor is simply returned and isn't dereferenced, Npgsql returns the cursor name itself (unnamed portal 1); you can now fetch results from this query by sending FETCH etc.
However, as was mentioned, wrapping a single SELECT in a function doesn't make much sense. If you do need to write a function that returns a single resultset, make it return a SETOF or a TABLE instead of a cursor: CREATE FUNCTION ... RETURNS TABLE (column_name column_type [, ...]). Apart from being simpler and cleaner, this is also more efficient, as the query results are returned directly (dereferencing the cursor involves another database roundtrip).
See the PostgreSQL docs for more info on how to define a function returning a table.
I have following working code with Npgsql 2.2.7 version:
private static DataSet ExecuteFunction(string functionName)
{
DataSet ds = new DataSet();
var conn = new NpgsqlConnection("replace with connection string");
conn.Open();
var tran = conn.BeginTransaction();
var cmd = new NpgsqlCommand(functionName, conn);
cmd.CommandType = CommandType.StoredProcedure;
NpgsqlDataAdapter da = new NpgsqlDataAdapter(cmd);
da.Fill(ds);
//foreach (DataRow r in ds.Tables[0].Rows)
//{
// Console.WriteLine("{0}", r[0]);
//}
tran.Commit();
conn.Close();
return ds;
}
I am calling a MySQL stored proc from a C# app.
Depending on the number of records available, I want to call different sql statements, so I used an if statement. i.e. if n records exist, then use this statement, else...
For some reason, as soon as use an if statement in the stored proc, although it will work when tested using MySQL, it won't work when calling it from my C# app. Focus just does not seem to return to the calling proc - the app just hangs on the cmd.ExecuteNonQuery(); statement.
If I remove the if statement in the stored proc and use one sql select and call it from the C# app, it works fine.
Any idea what is up?
C#:
private List<Record> GetListOfRecordsToProcess(string exchange, string shareCode, DateTime lastProcessedDate, Indicator indicator)
{
List<Record> list = new List<Record>();
MySqlConnection conn = new MySqlConnection(Data.cs);
MySqlCommand cmd = new MySqlCommand("get_records_to_process1", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("#p_exchange", exchange);
cmd.Parameters.AddWithValue("#p_share_code", shareCode);
cmd.Parameters.AddWithValue("#p_from_date", lastProcessedDate);
cmd.Parameters.AddWithValue("#p_num_days", (int)indicator);
conn.Open();
//Create MySqlDataAdapter object and assign the query and connection to it
MySqlDataAdapter adapter = new MySqlDataAdapter(cmd);
cmd.ExecuteNonQuery();
MySQL:
CREATE DEFINER=`root`#`localhost` PROCEDURE `get_records_to_process1`(
p_exchange varchar(10),
p_share_code varchar(10),
p_from_date date,
p_num_days int)
BEGIN
-- variables
declare data_count int;
-- for debugging
declare counter int;
set counter = 1;
-- for debugging
call log_message_insert (5, concat(counter,' Start'));
set counter = counter + 1;
call log_message_insert (5, concat(counter, ' p_exchange: ', p_exchange, ', p_share_code: ', p_share_code, ', p_from_date: ', p_from_date, ', p_num_days: ', p_num_days));
set counter = counter + 1;
-- find out if we have enough data to calculate the ema
select count(1) into data_count
from eod_data_sharenet s1
where s1.exchange = p_exchange
and s1.share_code = p_share_code;
if data_count < p_num_days then
select *
from eod_data_sharenet
where 1 = 2; -- insufficient data, return nothing
else
select *
from eod_data_sharenet eds
where eds.exchange = p_exchange
and eds.share_code = p_share_code
and eds.trading_date > p_from_date;
end if;
END
Proc modified as per Alex suggestion and works:
private List<Record> GetListOfRecordsToProcess(string exchange, string shareCode, DateTime lastProcessedDate, Indicator indicator)
{
List<Record> list = new List<Record>();
MySqlConnection connection = new MySqlConnection(Data.cs);
using (connection)
{
MySqlCommand cmd = new MySqlCommand("get_records_to_process", connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("#p_exchange", exchange);
cmd.Parameters.AddWithValue("#p_share_code", shareCode);
cmd.Parameters.AddWithValue("#p_from_date", lastProcessedDate);
cmd.Parameters.AddWithValue("#p_num_days", (int)indicator);
connection.Open();
MySqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
Record newRecord = new Record(Convert.ToDateTime(reader["trading_date"].ToString()),
reader["exchange"].ToString(),
reader["share_code"].ToString(),
Convert.ToInt32(Convert.ToDouble(reader["close"].ToString())));
list.Add(newRecord);
}
}
else
{
Console.WriteLine("No rows found.");
}
reader.Close();
}
connection.Close();
return list;
}
Solution as suggested by Alex:
private List<Record> GetListOfRecordsToProcess(string exchange, string shareCode, DateTime lastProcessedDate, Indicator indicator)
{
List<Record> list = new List<Record>();
using (MySqlConnection conn = new MySqlConnection(Data.cs))
{
conn.Open();
using (MySqlCommand cmd = new MySqlCommand("get_records_to_process", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("#p_exchange", exchange);
cmd.Parameters.AddWithValue("#p_share_code", shareCode);
cmd.Parameters.AddWithValue("#p_from_date", lastProcessedDate);
cmd.Parameters.AddWithValue("#p_num_days", (int)indicator);
conn.Open();
using (MySqlDataReader reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
Record newRecord = new Record(Convert.ToDateTime(reader["trading_date"].ToString()),
reader["exchange"].ToString(),
reader["share_code"].ToString(),
Convert.ToInt32(Convert.ToDouble(reader["close"].ToString())));
list.Add(newRecord);
}
}
}
}
}
return list;
}
con.Open();
cmd = new SqlCommand("USE PRODUCTS SELECT BOUGHT FROM " +
DropDownList1.SelectedItem.Text +
" WHERE ID = #ID", con);
cmd.Parameters.Add("ID", SqlDbType.Int).Value = DropDownList2.SelectedIndex;
int i = cmd.ExecuteReader().GetInt32(0);
con.Close();
I can't read integer values with reader like this. I get runtime error System.InvalidOperationException. What is wrong with my code ? if you can't find the mistake, can you explain how can i read integer values with reader ? By the way this part of code gives the error:
int i = cmd.ExecuteReader().GetInt32(0);
Try this:
int x=0;
using (
SqlConnection connection = new SqlConnection(strCon))
{
SqlCommand command = new SqlCommand(sql_string, connection);
connection.Open();
DataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
x = reader.GetInt32(0);
}
}
reader.Close();
}
You need to initialise a reader and then read it
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read()) // or just rdr.Read() if you know only one row is returned
{
int i = rdr.GetInt32(0);
What I have done to make things a lot easier on my end, since I mainly use SQL all over the place, is make some extensions.
eg
public static Int32 GetInt32(this SqlDataReader rdr, string column)
{
return Convert.ToInt32(rdr[column]);
}
I have question about using why i can not use the same instance of SQLCommand more than one time in the same code?
I tried the code down here and it runs good for the gridview but when i changed the query by using cmd.CommandText() method it keeps saying:
There is already an open DataReader associated with this Command which must be closed first.
This is the code:
string cs = ConfigurationManager.ConnectionStrings["MyDB"].ConnectionString;
SqlConnection con = new SqlConnection(cs);
try
{
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
con.Open();
cmd.CommandText = "Select top 10 FirstName, LastName, Address, City, State from Customers";
GridView1.DataSource = cmd.ExecuteReader();
GridView1.DataBind();
cmd.CommandText = "SELECT TOP 10 COUNT(CreditLimit) FROM Customers";
int total = (int)cmd.ExecuteScalar();
TotalCreditLble.Text = "The total Credit :" + total.ToString();
}
catch(Exception exp)
{
Response.Write(exp.Message);
}
finally
{
con.Close();
}
The problem is that you are using the SqlCommand object to generate a DataReader via the command.ExecuteReader() command. While that is open, you can't re-use the command.
This should work:
using (var reader = cmd.ExecuteReader())
{
GridView1.DataSource = reader;
GridView1.DataBind();
}
//now the DataReader is closed/disposed and can re-use command
cmd.CommandText = "SELECT TOP 10 COUNT(CreditLimit) FROM Customers";
int total = (int)cmd.ExecuteScalar();
TotalCreditLble.Text = "The total Credit :" + total.ToString();
There is already an open DataReader associated with this Command which must be closed first.
This is the very reason you don't share a command. Somewhere in your code you did this:
cmd.ExecuteReader();
but you didn't leverage the using statement around the command because you wanted to share it. You can't do that. See, ExecuteReader leaves a connection to the server open while you read one row at a time; however that command is locked now because it's stateful at this point. The proper approach, always, is this:
using (SqlConnection c = new SqlConnection(cString))
{
using (SqlCommand cmd = new SqlCommand(sql, c))
{
// inside of here you can use ExecuteReader
using (SqlDataReader rdr = cmd.ExecuteReader())
{
// use the reader
}
}
}
These are unmanaged resources and need to be handled with care. That's why wrapping them with the using is imperative.
Do not share these objects. Build them, open them, use them, and dispose them.
By leveraging the using you will never have to worry about getting these objects closed and disposed.
Your code, written a little differently:
var cs = ConfigurationManager.ConnectionStrings["MyDB"].ConnectionString;
var gridSql = "Select top 10 FirstName, LastName, Address, City, State from Customers";
var cntSql = "SELECT TOP 10 COUNT(CreditLimit) FROM Customers";
using (SqlConnection con = new SqlConnection(cs))
{
con.Open();
try
{
using (SqlCommand cmd = new SqlCommand(gridSql, con))
{
GridView1.DataSource = cmd.ExecuteReader();
GridView1.DataBind();
}
using (SqlCommand cmd = new SqlCommand(cntSql, con))
{
int total = (int)cmd.ExecuteScalar();
TotalCreditLble.Text = "The total Credit :" + total.ToString();
}
}
catch(Exception exp)
{
Response.Write(exp.Message);
}
}
Thank u quys but for the guys who where talking about using block !
why this code work fine which i seen it on example on a video ! It's the same thing using the same instance of SqlCommand and passing diffrent queries by using the method CommanText with the same instance of SqlCommand and it's execute just fine , this is the code :
using (SqlConnection con = new SqlConnection(cs))
{
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
con.Open();
cmd.CommandText = "Delete from tbleProduct where ProductID= 4";
int TotalRowsAffected = cmd.ExecuteNonQuery();
Response.Write("Total rows affected :" + TotalRowsAffected );
cmd.CommandText = "Insert into tbleProduct values (4, 'Calculator', 100, 230)";
TotalRowsAffected = cmd.ExecuteNonQuery();
Response.Write("Total rows affected :" + TotalRowsAffected );
cmd.CommandText = "ypdate tbleProduct set QtyAvailbe = 234 where ProductID = 2";
TotalRowsAffected = cmd.ExecuteNonQuery();
Response.Write("Total rows affected :" + TotalRowsAffected );
}
i m trying to retrieve the Specialization ID from a table called Specializationtbl, using C# MSVS 2008 and the table includes SpecializationName and SpecializationID beside some other rows and my question is related to some error " No Data to present ", the command goes as bellow:
SqlCommand READSpecID = new SqlCommand("SELECT * FROM Specializationtbl WHERE SpecializationName='" + comboBox1.Text + "'" , DBcnction);
DBcnction.Open();
SqlDataReader ReadSpecID_ = READSpecID.ExecuteReader();
ReadSpecID_.Read();
int SpecID_ = Convert.ToInt16(ReadSpecID_["SpecID"].ToString());
DBcnction.Close();
i also tried to Select the "SpecID" instead of all the rows, but cant seem to seal the query correctly and keep receiving "No data present " error, any idea where am i making the mistake?
1) Try opening DBcnction before assigning the value to READSPecID
DBcnction.Open();
SqlCommand READSpecID = new SqlCommand("SELECT * FROM Specializationtbl WHERE SpecializationName='" + comboBox1.Text + "'" , DBcnction);
2) Run the command in SSMS:
SELECT * FROM Specializationtbl WHERE SpecializationName ='yourvalue'
and see if any results are returned
3) Check comboBox1.Text has a value in it
4) Validate the contents of comboBox1.Text (Or use paremetrised queries or a stored procedure) to ensure you do not become a victim of SQL Injection: http://en.wikipedia.org/wiki/SQL_injection
Refactor to solve your TWO problems:
Your SQL injection problem when building your SQL statement.
Use ExecuteScalar if you only need one value.
Implement using blocks.
string retVal;
using (var conn = new SqlConnection(SomeConnectionString))
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "SELECT SpecID FROM Specializationtbl WHERE SpecializationName= #Name";
cmd.Parameters.AddWithValue("#Name", comboBox1.Text);
conn.Open();
retVal = cmd.ExecuteScalar().ToString();
}
int specID = int.Parse(retVal);
If you really needed more than one value from your statement:
using (var conn = new SqlConnection(SomeConnectionString))
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "SELECT SpecID, Value2 FROM Specializationtbl WHERE SpecializationName= #Name";
cmd.Parameters.AddWithValue("#Name", comboBox1.Text);
conn.Open();
var dr = cmd.ExecuteReader();
while (dr.Read())
{
Customer c = new Customer {
ID = dr["SpecID"].ToString(),
Value = dr["Value2"].ToString(),
};
}
}
Need to first test if there are any rows. I suspect the query is returning zero rows.
if (ReadSpecID_.HasRows)
{
ReadSpecID_.Read();
}