I have a function that executes stored procedures. The thing is, I wish to use this one function for multiple stored procedures that take different arguments.
Of course, if I do so, I will get an error saying that
Procedure or function has too many arguments specified
What I want to do is to create the function such that when it executes a stored procedure, that stored procedure only takes the parameters that it needs and ignore the others. Is this possible?
Here is what I have so far:
try
{
using (SqlConnection con = new SqlConnection(consr))
{
SqlCommand cmd = new SqlCommand();
SqlDataReader reader;
cmd.CommandText = stp;
cmd.Parameters.Add(new SqlParameter("#proc1", cmb1.SelectedItem.ToString()));
cmd.Parameters.Add(new SqlParameter("#proc2", cmb2.SelectedItem.ToString()));
cmd.Parameters.Add(new SqlParameter("#proc3", cmb3.SelectedItem.ToString()));
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = con;
con.Open();
reader = cmd.ExecuteReader();
con.Close();
}
}
catch (SqlException exp)
{
throw new InvalidOperationException(exp.Message);
}
Here are two of the procedures:
ALTER PROCEDURE [dbo].[test1]
#proc1 varchar(20)
AS
Begin
select * from tab where name=#proc1
END
and
ALTER PROCEDURE [dbo].[test1]
#proc2 varchar(20)
AS
Begin
select * from tab where name=#proc2
END
I want to use the same function to execute both
You can set the params you don't want to use to null, then check for which values are available and respond accordingly
ALTER PROCEDURE [dbo].[test1]
#proc1 varchar(20) = null
#proc2 varchar(20) = null
#proc3 varchar(20) = null
AS
Begin
IF #proc1 IS NOT NULL BEGIN
select * from tab where name=#proc1
END
END
Make your function take two arguments, the name of the SP and a 2d array of SP argument names and their corresponding values. Then set up your sql command by iterating over the 2d array and adding the parameters in the loop.
If you need to return several result sets, see here: How do I return multiple result sets with SqlCommand? and modify the function and args so that they take an array of SP names and an array of 2d arrays, containing the SP arg names and values for each SP.
I think you should use this structure :
CREATE PROCEDURE MyProcName
#Parameter1 INT = 1,
#Parameter2 VARCHAR (100) = 'StringValue',
#Parameter3 VARCHAR (100) = NULL
AS
/* check for the NULL / default value (indicating nothing was passed */
if (#Parameter3 IS NULL)
BEGIN
/* whatever code you desire for a missing parameter*/
INSERT INTO ........
END
/* and use it in the query as so*/
SELECT *
FROM Table
WHERE Column = #Parameter
Can you please share the stored procedure code? I think you need to use if else or case statement in your function and call appropriate stored procedure with required parameters inside your function.
Related
I'm a beginner at C#. I can't call a stored procedure.
My stored procedure is this:
CREATE PROCEDURE USP_login
#us VARCHAR(20),
#pwd VARCHAR(20)
AS
BEGIN TRAN
BEGIN TRY
SELECT *
FROM dbo.KhachHang
WHERE tenDangNhap = #us AND matKhau = #pwd
END TRY
BEGIN CATCH
ROLLBACK TRAN
RETURN 0
END CATCH
COMMIT TRAN
RETURN 1
GO
In my C# code, I use this function to call the USP_login stored procedure but it doesn't work:
public bool loginStored(string us, string pwd)
{
object[] sqlParams =
{
new SqlParameter ("#userName", us),
new SqlParameter ("#passWord", pwd),
};
var rs = db.Database.SqlQuery<bool>("USP_login #userName, #passWord", sqlParams).SingleOrDefault();
return rs;
}
Error message in screenshot:
Looks like SELECT * ... is returning more than just a single bool. (Based on the query, clearly the table has at least two fields, tenDangNhap and matKhau.) But that's what you told the code to expect:
db.Database.SqlQuery<bool>(/.../)
Either select only the column you want:
SELECT SomeBooleanValue FROM dbo.KhachHang WHERE tenDangNhap=#us AND matKhau=#pwd
Or specify the correct type that can be expected for each record (which may be a custom class that you need to define):
db.Database.SqlQuery<SomeObjectType>(/.../)
What I need:
In PLS/SQL on an Oracle DB, create a stored procedure or function with parameters, which given a declared table of , where is a ROW of a table (with all the fields), returns the resultset following the conditions given in the parameters. After, I need to call them from Microsoft Entity Framework with edmx file.
Basically the need is to being able to provide a quick report of the table contents into a pdf, matching some filters, with an oracle db.
The mantainer must be able, provided a script I give, to create and add new reports, so this needs to be dynamic.
Here's what I've got so far:
CREATE OR REPLACE type THETABLEIWANTTYPE as table of THETABLEIWANT%TYPE
create function
SCHEMA.THETABLEIWANT_FUNCTION(PARAM_GR in number default 1)
return THETABLEIWANTTYPE
PIPELINED
as
result_table THETABLEIWANTTYPE
begin
SELECT S.id, S.idg, S.sta, S.tab
Bulk collect into result_table
from SCHEMA.THETABLEIWANT S
WHERE IDGR = PARAM_GR
IF result_table.count > 0 THEN
for i in result_table.FIRST .. result_table.LAST loop
pipe row (result_table(i))
end loop
end if
return
end;
But it's not working. It gives errors.
Running CREATE TYPE I get:
Compilation errors for TYPE SCHEMA.THETABLEIWANT
Error: PLS-00329: schema-level type has illegal reference to
SCHEMA.THETABLEIWANT
The mantainer will launch the script creating a TYPE of the row of the table I need, then the function should return a table with the records.
Then calling it from Entity Framework I should be able to execute it like I'm calling a normal select from my table, IE:
``_dbContext.THETABLEIWANT.Where(x => x.IDGR = Param_gr).ToList();
The problem is that mantainers should be able to generate new kind of reports with any select inside without the need of my intervention on the software code.
Any hint?
It's ok also to bulk all the select result into a temp table but it has to be dynamic as column will be changing
I ended up to write a PLS/SQL procedure that returns a cursor and managing it from C# code with Oracle.ManagedDataAccess Library.
Here's the procedure, for anyone interested:
CREATE OR REPLACE PROCEDURE SCHEMA.PROC_NAME(
PARAM_1 VARCHAR2,
RESULT OUT SYS_REFCURSOR)
IS
BEGIN
OPEN RESULT FOR
SELECT A, V, C AS MY_ALIAS from SCHEMA.TABLE WHERE FIELD = PARAM_1 AND FIELD_2 = 'X';
END;
And here's the C# code for calling and getting the result:
OracleConnection conn = new OracleConnection("CONNECTIONSTRING");
try
{
if (conn.State != ConnectionState.Open)
conn.Open();
List<OracleParameter> parametri = new List<OracleParameter>()
{
new OracleParameter
{
ParameterName = nameof(filter.PARAM_1),
Direction = ParameterDirection.Input,
OracleDbType = OracleDbType.NVarchar2,
Value = filter.PARAM_1
}
};
OracleCommand cmd = conn.CreateCommand();
cmd.Parameters.AddRange(parametri.ToArray());
OracleParameter cursor = cmd.Parameters.Add(
new OracleParameter
{
ParameterName = "RESULT",
Direction = ParameterDirection.Output,
OracleDbType = OracleDbType.RefCursor
}
);
cmd.CommandText = procedureName;
cmd.CommandType = CommandType.StoredProcedure;
cmd.ExecuteNonQuery();
using (OracleDataReader reader = ((OracleRefCursor)cursor.Value).GetDataReader())
{
if (reader.HasRows)
while (reader.Read())
{
//Iterate the result set
}
}
}
catch(Exception ex)
{
//Manage exception
}
I have a form with inputs 'name' and 'phone'.
When they have values, everything works: a record is inserted into the database using a stored procedure, spFoo:
String sp = "spFoo 'test name','test phone'";
OdbcCommand command = new OdbcCommand(sp, connection);
command.CommandType = System.Data.CommandType.StoredProcedure;
connection.Open();
Response.Write(command.ExecuteNonQuery().ToString());
Works on application
Works on Mgmt Studio
But when they don't have values I get -1 as a response in the application:
String sp = "spFoo '',''";
Does not work in application (-1)
Works on Mgmt Studio
I want the user to be able to create a record without any input.
Why does this work in management studio, but not on the application?
Update: I added defaults to the params in the stored procedure, it didn't work; I gave empty strings "NULL" as values in the code, still no luck. Is this a server setting or something that won't allow empty variables?
You'll need to remove the SET NOCOUNT ON; from your stored procedure.
From the documentation:
Stops the message that shows the count of the number of rows affected by a Transact-SQL statement or stored procedure from being returned as part of the result set.
As others have pointed out you should parameterise your query too but the -1 is caused by NOCOUNT being ON.
Edit
I realise it's not what you're asking but to use a parameterised query with ODBC you need to use ?'s as ordinal place holders as per the documentation here. For example:
using (OdbcConnection connection = new OdbcConnection(connectionString))
{
string sp = "{call spFoo (?, ?)}";
using (OdbcCommand command = new OdbcCommand(sp, connection))
{
command.CommandType = System.Data.CommandType.StoredProcedure;
connection.Open();
//the order here is important, the names are not!
command.Parameters.Add("#name", OdbcType.VarChar).Value = "test name";
command.Parameters.Add("#phone", OdbcType.VarChar).Value = "test phone";
Console.WriteLine(command.ExecuteNonQuery().ToString());
}
}
When you are calling a stored procedure from code, you should use the Parameters property on the command. Try this:
String sp = "spFoo";
command.Parameters.Add("#name", "test name");
command.Parameters.Add("#phone", "test phone");
As JimmyV said, you should use the command.Parameters.Add method to setup your parameters, passing in null whenever a parameter value is not specified. To address your comment about the error 'procedure or function 'spFoo' expects parameter '#name', which was not supplied', you'll also need to modify your stored procedure to use default values when a param is not supplied (e.g. null):
CREATE PROCEDURE MyStoredProcedure
#foo int = null
AS
BEGIN
...
END
Sorry for not adding this a comment on the above post. Not enough reputation!
You shouldn't be calling a stored procedure the way that you currently are. You should be using parameters. Your code is susceptible to SQL injection.
Never string concat user inputted values.
What you should have, is a stored procedure setup similarly:
CREATE PROCEDURE spFoo
#name varchar(50) = 'Jim', -- default
#phone varchar(50) = null -- optional
AS
BEGIN
SET NOCOUNT ON;
-- INSERT STATEMENT
END
GO
And then supply the parameters in the code:
string name = this.nameTextBox.Text;
string phone = this.phoneTextBox.Text;
if (string.IsNullOrWhiteSpace(name))
name = null;
if (string.IsNullOrWhiteSpace(phone))
phone = null;
SqlConnection connection = new SqlConnection(#"<connection string>");
using (SqlCommand command = connection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
// leave this as the stored procedure name only
command.CommandText = "spFoo";
// if name is null, then Jim gets passed (see stored procedure definition)
// if phone is null, then null gets passed (see stored procedure definition)
command.Parameters.AddWithValue("#name", name);
command.Parameters.AddWithValue("#phone", phone);
try
{
connection.Open();
int result = command.ExecuteNonQuery();
Console.WriteLine(result);
}
finally
{
if (connection.State != ConnectionState.Closed)
connection.Close();
}
}
I'm not sure why you used the Odbc namespace objects since it sounds like you are using MS-SQL. You should be using objects from the System.Data.SqlClient namespace.
The answer to your actual question would most likely involve executing a script (not a stored procedure) similar to:
DECLARE #RC int
DECLARE #name varchar(50)
DECLARE #phone varchar(50)
-- TODO: Set parameter values here.
EXECUTE #RC = spFoo
#name,
#phone
GO
Which is not recommended.
I'm trying to call the below stored procedure but I am unsure on what to pass through one of the parameters (#UnsubscribeTypes) I've tried passing in a list but got a compile error. I'm using c#, Visual Studio 2010, web forms. Any ideas on what I should pass in when calling the stored procedure in my c# code (ado.net)?
Here is my stored procedure:
ALTER PROCEDURE [czone].[SetAccountEmailPreference]
(
#EmailAddress VARCHAR(255),
#UnsubscribeTypes dbo.ListOfIDs READONLY,
#SentEmailID INT = NULL
)
AS
SET NOCOUNT ON;
EXEC dbo.LogObjectExecution ##PROCID;
DECLARE #UnsubscribeID INT = (SELECT TOP 1 UnsubscribeID
FROM Email.dbo.Unsubscribe
WHERE EmailAddress = #EmailAddress
ORDER BY UnsubscribeID DESC);
-- Unsubscribe
IF ((SELECT COUNT(*) FROM #UnsubscribeTypes) > 0)
BEGIN
IF(#UnsubscribeID IS NULL)
BEGIN
-- ADD UNSUBSCRIBE
INSERT INTO Email.dbo.Unsubscribe (EmailAddress, CreatedDate)
VALUES (#EmailAddress, CURRENT_TIMESTAMP)
SET #UnsubscribeID = ##IDENTITY;
END
-- Remove current mappings
DELETE FROM Email.dbo.UnsubscribeTypeMapping
WHERE UnsubscribeFK = #UnsubscribeID;
-- Add new mappings
INSERT INTO Email.dbo.UnsubscribeTypeMapping (UnsubscribeFK, UnsubscribeTypeFK, SentEmailFK)
SELECT
#UnsubscribeID, ID, #SentEmailID
FROM
#UnsubscribeTypes;
END
-- Subscribe
ELSE IF (#UnsubscribeID IS NOT NULL)
BEGIN
DELETE FROM Email.dbo.Unsubscribe
WHERE UnsubscribeID = #UnsubscribeID;
END
dbo.ListOfIDs is a table type. First, find out the type in your database, then check columns. generate a datatable with rows containing the UnsubscribeTypeFK ids.
The ADO.net code (not compiled)
Creating table
DataTable dt = new DataTable("Items");
dt.Columns.Add("ID", typeof(int));
dt.Rows.Add(4);
Calling proc
con = new SqlConnection(conStr);
con.Open();
using (con) {
// Configure the SqlCommand and SqlParameter.
SqlCommand sqlCmd = new SqlCommand("czone.SetAccountEmailPreference", con);
sqlCmd.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParam = sqlCmd.Parameters.AddWithValue("#UnsubscribeTypes", _dt); // TVP
tvpParam.SqlDbType = SqlDbType.Structured; //tells ADO.NET we are passing TVP
//pass other parameters
sqlCmd.ExecuteNonQuery();
}
con.Close();
You will find more about Table-Valued parameters here
I have created a stored procedure that takes a single argument, the name of a table, and returns 1 if it exists in the database, 0 if it does not. In SQL Server Management Studio testing my stored procedure works exactly as I'd like it to, however I'm having trouble getting that value for use in my C# program.
My options seem to be ExecuteScalar(), ExecuteNonQuery() or ExecuteReader(), none of which seem appropriate for the task, nor can I get them to even retrieve my stored procedure's result.
I have tried assigning my parameter with both cmd.Parameters.AddWithValue and cmd.Parameters.Add again to no avail.
Assuming you have a stored procedure like this which selects either a 0 (table does not exist) or 1 (table does exist)
CREATE PROCEDURE dbo.DoesTableExist (#TableName NVARCHAR(100))
AS
BEGIN
IF EXISTS (SELECT * FROM sys.tables WHERE Name = #TableName)
SELECT 1
ELSE
SELECT 0
END
then you can write this C# code to get the value - use .ExecuteScalar() since you're expecting only a single row, single column:
// set up connection and command
using (SqlConnection conn = new SqlConnection("your-connection-string-here"))
using (SqlCommand cmd = new SqlCommand("dbo.DoesTableExist", conn))
{
// define command to be stored procedure
cmd.CommandType = CommandType.StoredProcedure;
// add parameter
cmd.Parameters.Add("#TableName", SqlDbType.NVarChar, 100).Value = "your-table-name-here";
// open connection, execute command, close connection
conn.Open();
int result = (int)cmd.ExecuteScalar();
conn.Close();
}
Now result will contain either a 0 if the table doesn't exist - or 1, if it does exist.
Use this:
var returnParameter = cmd.Parameters.Add("#ReturnVal", SqlDbType.Int);
returnParameter.Direction = ParameterDirection.ReturnValue;
Your stored procedure should return 0 or 1.