Is there a way to set a parameter from a stored procedure to a var query? - sql

I'm working in a .net app specifically in a validation scripted in a stored procedure. This simple validation checks if there is already a policy with the same policy number that the user is trying to insert and throws an error message.
The problem that I'm facing is of the kind
Procedure or function 'pr_Policy_CheckPolicyNumber' expects parameter '#companyID', which was not supplied.
That's because I added a new field from the same PolicyBill table named (CompanyID).
My question is if it's there a way to set the #CompanyID value directly to the function and fix that error
The stored procedure is
CREATE OR ALTER PROCEDURE [dbo].[pr_Policy_CheckPolicyNumber]
(
#policyNumber VARCHAR(50) ,
#effectiveOn DATETIME ,
#companyID BIGINT
)
AS
IF ( ( SELECT COUNT(pb.policyBillID)
FROM PolicyBill pb
WHERE pb.policyNumber = #policyNumber
AND ( #effectiveOn >= pb.effectiveOn ) and (#effectiveOn <= pb.expiresOn )
AND pb.isActive = 1
AND pb.statusID = 56
AND pb.documentTypeID = 1
AND pb.companyID = #companyID
) > 0 )
BEGIN
PRINT N'The Policy already exists';
SELECT 1
END
ELSE
BEGIN
SELECT 0
END
and the Function is:
private bool CheckPolicyNumber()
{
var result = true;
var query = $"EXEC pr_Policy_CheckPolicyNumber '{txtPolicyNumber.Text}','{deEffectiveON.Date:yyyy/MM/dd}', '#companyID'";
var dt = AhkSqlHelper.ahkExecuteDataTable(query);
result = Convert.ToBoolean(Convert.ToInt16(dt.Rows[0][0].ToString()));
return result;
}
Any help would be appreciated.

Ad mentioned in the comment, the original code is fairly dangerous and you could be open to SQL injection attacks. The best solution is parameterize your command, something like this should do the trick:
using System.Data.SqlClient;
using System.Data;
string connString = "YourConnectionString";
string spName = "pr_Policy_CheckPolicyNumber";
private bool CheckPolicyNumber(string policyNumber, DateTime effectiveOn, long companyID)
{
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(spName, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter parameter1 = new SqlParameter("#policyNumber ", policyNumber);
cmd.Parameters.Add(parameter1);
SqlParameter parameter2 = new SqlParameter("#effectiveOn ", effectiveOn);
cmd.Parameters.Add(parameter2);
SqlParameter parameter3 = new SqlParameter("#companyID ", companyID);
cmd.Parameters.Add(parameter3);
var result = cmd.ExecuteScalar()
return (bool)result;
}
}
}

Related

Insert a record if it does not exist in database and show a message to user if a record is already in database

I want to insert a record if it does not exist in our database .If the data to be inserted already exist in database then a messagebox should be displayed to user that the data that you want to insert is already in the database.
ALTER PROCEDURE sp_InsertRoles
(
#roleName varchar(50),
#roleStatus tinyint
)
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS(SELECT * FROM tblRole WHERE roleName = #roleName and roleStatus=#roleStatus)
BEGIN
SELECT 'FALSE'
END
ELSE
BEGIN
INSERT INTO tblRole
VALUES(#roleName,#roleStatus)
SELECT 'TRUE'
END
END
string cs =
ConfigurationManager.ConnectionStrings["School_Management_System.Properties.
Settings.schoolManagementDBConnectionString"].ConnectionString;
SqlConnection con = new SqlConnection(cs);
SqlCommand cmd = new SqlCommand("sp_InsertRoles", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#roleName",txtRole.Text);
cmd.Parameters.AddWithValue("#roleStatus",ddlStatus.SelectedItem);
con.Open();//connection open
cmd.ExecuteNonQuery();
int count = Convert.ToInt32(cmd.ExecuteScalar());
if (count > 0)
{
MessageBox.Show("Role already exits...", "Failed",MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else if (count <= 0)
{
MessageBox.Show("Role inserted successfully...", "Success",MessageBoxButtons.OK, MessageBoxIcon.Information);
}
con.Close();
Use using blocks for you database objects. That will ensure they are closed and disposed even if there is an error. You will want to declare count outside of the using blocks so you can use it after the connection is closed. You don't want to show message boxes while a connection is open. Connections are precious resources and should be opened right before the .Execute... and closed as soon as possible.
Use the .Add method instead of .AddWithValue See http://www.dbdelta.com/addwithvalue-is-evil/
and
https://blogs.msmvps.com/jcoehoorn/blog/2014/05/12/can-we-stop-using-addwithvalue-already/
and another one:
https://dba.stackexchange.com/questions/195937/addwithvalue-performance-and-plan-cache-implications
Here is another
https://andrevdm.blogspot.com/2010/12/parameterised-queriesdont-use.html
Since your stored procedure returns the strings True or False, I changed count to a boolean and adjusted tne If block.
private void UpdateDatabse()
{
string cs = ConfigurationManager.ConnectionStrings["School_Management_System.Properties.Settings.schoolManagementDBConnectionString"].ConnectionString;
bool count;
using (SqlConnection con = new SqlConnection(cs))
{
using (SqlCommand cmd = new SqlCommand("sp_InsertRoles", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#roleName",SqlDbType.VarChar, 50 ).Value = txtRole.Text;
cmd.Parameters.Add("#roleStatus",SqlDbType.TinyInt).Value = (int)ddlStatus.SelectedItem;
con.Open();
count = Convert.ToBoolean(cmd.ExecuteScalar());
}
}
if (count)
{
MessageBox.Show("Role inserted successfully...", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
MessageBox.Show("Role already exits...", "Failed", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}

Invalid column name when performing update

I have been trying to update data to database however i met this problem.I tried deleting the table and creating a new table yet the problem still persist.Below are the codes.Any help will be greatly appreciated
public int UpdateNOK(string wardClass, DateTime admissionDT, string nokFName, string nokLName, string nokNRIC, DateTime nokDOB, string nokGender, string nokNationality, string nokRelationship, int nokContactH, int nokContactHP, string nokEmail, string nokAddr1, string nokAddr2, string nokState, int nokZIP, string nokCountry, DateTime dischargeDT, string patientNRIC)
{
StringBuilder sqlStr = new StringBuilder();
int result = 0;
SqlCommand sqlCmd = new SqlCommand();
sqlStr.AppendLine("Update patientAdmission");
sqlStr.AppendLine("SET wardClass = #parawardClass,admissionDT = #paraadmissonDT, nokFName = #parapatientNokFname, nokLName = #parapatientNokLname,nokNRIC = #parapatientNokNRIC, nokDOB = #parapatientNOKDOB, nokGender = #parapatientNokGender, nokNationality = #parapatientNokNationality,");
sqlStr.AppendLine("nokRelationship = #parapatientNokRelationship,nokContactH = #parapatientNokContactH,nokContactHP = #parapatientNokContactHP, nokEmail = #parapatientNokEmail, nokAddr1 = #parapatientNokAddr1,nokAddr2 = #parapatientNokAddr2,nokState = #parapatientNokState, nokZIP = #parapatientNokZIP,");
sqlStr.AppendLine("nokCountry = #parapatientNokCountry, dischargeDT = #paradischargeDateTime");
sqlStr.AppendLine("WHERE patientNRIC = #parapatientNRIC");
try
{
SqlConnection myConn = new SqlConnection(DBConnect);
sqlCmd = new SqlCommand(sqlStr.ToString(), myConn);
sqlCmd.Parameters.AddWithValue("#parawardClass", wardClass);
sqlCmd.Parameters.AddWithValue("#paraadmissonDT", admissionDT);
sqlCmd.Parameters.AddWithValue("#parapatientNokFname", nokFName);
sqlCmd.Parameters.AddWithValue("#parapatientNokLname", nokLName);
sqlCmd.Parameters.AddWithValue("#parapatientNokNRIC", nokNRIC);
sqlCmd.Parameters.AddWithValue("#parapatientNOKDOB", nokDOB);
sqlCmd.Parameters.AddWithValue("#parapatientNokGender", nokGender);
sqlCmd.Parameters.AddWithValue("#parapatientNokNationality", nokNationality);
sqlCmd.Parameters.AddWithValue("#parapatientNokRelationship", nokRelationship);
sqlCmd.Parameters.AddWithValue("#parapatientNokContactH", nokContactH);
sqlCmd.Parameters.AddWithValue("#parapatientNokContactHP", nokContactHP);
sqlCmd.Parameters.AddWithValue("#parapatientNokEmail", nokEmail);
sqlCmd.Parameters.AddWithValue("#parapatientNokAddr1", nokAddr1);
sqlCmd.Parameters.AddWithValue("#parapatientNokAddr2", nokAddr2);
sqlCmd.Parameters.AddWithValue("#parapatientNokState", nokState);
sqlCmd.Parameters.AddWithValue("#parapatientNokZIP", nokZIP);
sqlCmd.Parameters.AddWithValue("#parapatientNokCountry", nokCountry);
sqlCmd.Parameters.AddWithValue("#paradischargeDateTime", dischargeDT);
sqlCmd.Parameters.AddWithValue("#parapatientNRIC", patientNRIC);
myConn.Open();
result = sqlCmd.ExecuteNonQuery();
myConn.Close();
Console.WriteLine(result);
}
catch (Exception ex)
{
logManager log = new logManager();
log.addLog("patientNOKDAO.UpdateNOK", sqlStr.ToString(), ex);
}
return result;
}
}
You should check table definition (sp_help) against your used columns in the table patientAdmission:
wardClass
admissionDT
nokFName
nokLName
nokNRIC
nokDOB
nokGender
nokNationality
nokRelationship
nokContactH
nokContactHP
nokEmail
nokAddr1
nokAddr2
nokState
nokZIP
nokCountry
dischargeDT
patientNRIC
If database default collation is a case-sensitive one, column names above must be exactly as defined in the table (case cannot be different).
One way to find the problem faster is to run SQL profiler and see the exact query against the database. Copy-paste it from there and run it into an Management Studio (SSMS) query file (use BEGIN TRAN .. ROLLBACK to ensure that nothing will actually be changed when you make it work). SSMS will try to indicate the exact column with the problem when clicking on the error.

Retrieve value of a table and using the value in stored procedure

I'm beginner in SQL Server 2012; I need to generate a product ID in a stored procedure, I generated part of the ID in C#, that part of ID includes Industrialist ID and I pass this to my stored procedure. In the stored procedure I need the last product of my Industrialist number and save in to as SQL variable on my stored procedure. How can I do this?
There are many ways to pass items between SQL and C#, you could use an output parameter where you will populate the parameter within the stored procedure.
string variableName;
using (var conn = new SqlConnection("**connection string**"))
using (var cmd = new SqlCommand("storedProcedureName", conn))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("inputParameter", inputParameter);
var outputParameter = new SqlParameter(){
ParameterName="ParameterName"
,Direction = ParameterDirection.Output
,SqlDbType = SqlDbType.VarChar
,DbType = DbType.VarChar
};
conn.Open();
try
{
cmd.ExecuteNonQuery();
variableName = string.Format("{0}", outputParameter.Value);
}
catch{}
finally
{
conn.Close();
}
}
You could return the value using something along the lines of RETURN #returnValue in your procedure, or you could return it within a table.
string variableName;
using (var conn = new SqlConnection("**connection string**"))
using (var cmd = new SqlCommand("storedProcedureName", conn))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("inputParameter", inputParameter);
conn.Open();
try
{
using (var dbReader = cmd.ExecuteReader())
{
if (dbReader.Read())
{
variableName = string.Format("{0}", dbReader["ColumnName"]);
}
}
}
catch{}
finally
{
conn.Close();
}
}
A generic example of a stored procedure which might work:
CREATE PROC [dbo].[storedProcedureName]
#InputParameter VARCHAR
,#OutputParameter VARCHAR OUTPUT
AS BEGIN
DECLARE #insertedId INT;
BEGIN TRANSACTION
INSERT INTO TableName (...Column Names...)
VALUES (... Values...)
SET #insertedId = SCOPE_IDENTITY()
COMMIT
SELECT #OutputParameter = ColumnName
FROM TableName
WHERE IdColumnName = #insertedId
END
EDIT: Possibly more relevant:
CREATE PROC [dbo].[storedProcedureName]
#IndustrialistId INT
,#OutputParameter VARCHAR OUTPUT -- This might be an int, but it's unclear what you want
AS BEGIN
DECLARE #productId INT;
SELECT #productId = MAX(ProductId)
FROM Products
WHERE IndustrialistId = #IndustrialistId;
SET #OutputParameter = CONVERT(VARCHAR,#IndustrialistId) + '-' + CONVERT(VARCHAR,#productId)
END
If you were to provide some code it might be easier for someone to give you a more tailored response. None of the above code has been syntax checked etc. so should be considered more pseudo code but hopefully it gives you something to work with.

Get the value from Output parameter C#

hi i'm try to create sp in sql with output value
this is the code
ALTER PROCEDURE [dbo].[usp_SelectHospital_IfExiste_Department]
#HospitalDepartmentID INT,
#IfExiste INT OUTPUT
AS
SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
IF NOT EXISTS (SELECT c.DeptID FROM Clinic c WHERE DeptID=#HospitalDepartmentID )
BEGIN
SET #IfExiste=0
SELECT
[HospitalDepartmentID],
[NAME]
FROM
[dbo].[Hospital_Department]
WHERE
[HospitalDepartmentID] = #HospitalDepartmentID
END
ELSE
BEGIN
SET #IfExiste=1
SELECT
[HospitalDepartmentID],
[NAME]
FROM
[dbo].[Hospital_Department]
WHERE
[HospitalDepartmentID] = #HospitalDepartmentID
END
and the C# code
public static Hospital_Department GetDepartmentInfo(int ID,int OutIfExist)
{
SqlCommand cmd;
SqlDataReader dr;
Hospital_Department HD = new Hospital_Department();
using (cmd = new SqlCommand("usp_SelectHospital_IfExiste_Department", ProjectCon.GetCon()))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#HospitalDepartmentID", ID);
//cmd.Parameters.Add("#IfExiste",SqlDbType.Int).Direction = ParameterDirection.Output;
cmd.Parameters.Add("#IfExiste",SqlDbType.Int);
cmd.Parameters["#IfExiste"].Direction = ParameterDirection.Output;
dr = cmd.ExecuteReader();
while (dr.Read())
{
HD.NAME = dr["NAME"].ToString();
HD.HospitalDepartmentID = Convert.ToInt32(dr["HospitalDepartmentID"]);
}
OutIfExist = Convert.ToInt32(cmd.Parameters["#IfExiste"].Value);
return HD;
}
}
when i try to get the output value is always null
and i run the stored procedure in sql Was run and return the value
so plz tell me what's wrong in my code
thx
Maybe this question has useful info:
According to, http://msdn.microsoft.com/en-us/library/ms971497, you must close the datareader before you process the output parameters.
Hope it helps.

How do you specify 'DEFAULT' as a SQL parameter value in ADO.NET?

I have a parameterized SQL query targetted for SQL2005 which is dynamically created in code, so I used the ADO.NET SqlParameter class to add sql parameters to SqlCommand.
In the aforementioned SQL I select from a Table Valued Function with has defaults. I want my dynamic sql to sometimes specify a value for these default parameters, and other times I want to specify that the SQL DEFAULT - as defined in the Table Valued Function - should be used.
To keep the code clean I didn't want to dynamically add the SQL DEFAULT keyword and parameterize it when a non-default is to be used, I just wanted to set DEFAULT as the value of my SQLParameter.
Can I? What is best practice in such an instance?
SQL query parameters take the place of literal values only.
You can't send an SQL keyword as the value of a parameter, just as you cannot send a table identifier, column identifier, list of values (e.g. for an IN predicate), or an expression. The value of the parameter is always interpreted as a literal value, as if you had included a quoted string literal or a numeric literal in your query.
Sorry, but you have to include an SQL keyword as part of the SQL query before you prepare that query.
AFAIK, the only way to tell SQL Server to use a default value is via the DEFAULT keyword or to exclude it from parameter list. That means that the use of the DEFAULT keyword must be in your parameterized SQL Statement. So, something like:
Select ...
From dbo.udf_Foo( DEFAULT, #Param2, #Param3, DEFAULT, .... )
I suppose another approach would be to query the system catalogs for the actual value of the various DEFAULT values and determine whether to set the SqlParameter to the default value that way, but that requires a convoluted second query to get the default values.
If you have the following function (for example):
CREATE FUNCTION dbo.UFN_SAMPLE_FUNCTION
(
#Param1 nvarchar(10),
#Param2 int = NULL
)
RETURNS TABLE
AS
RETURN
SELECT #Param1 AS Col1, #Param2 AS Col2;
GO
Then you can use it the following way (option 1):
SELECT * FROM dbo.UFN_SAMPLE_FUNCTION ('ABC', DEFAULT);
which is correct way and you get the following result:
Col1 Col2
---------- -----------
ABC NULL
But if you try to use parametrized query (option 2):
exec sp_executesql N'SELECT * FROM dbo.UFN_SAMPLE_FUNCTION (#P1, #P2)',N'#P1 nvarchar(10),#P2 int',#P1=N'abc',#P2=default;
you will get an error:
Msg 8178, Level 16, State 1, Line 0
The parameterized query '(#P1 nvarchar(10),#P2 int)SELECT * FROM dbo.UFN_SAMPLE_FUNCTION' expects the parameter '#P2', which was not supplied.
If you have the following .net code:
public void RunTVF(string param1, int? param2)
{
using (SqlConnection con = GetProdConection())
{
using (var cmd = new SqlCommand("SELECT * FROM dbo.UFN_SAMPLE_FUNCTION (#P1, #P2)", con))
{
cmd.CommandType = CommandType.Text;
var param = new SqlParameter
{
ParameterName = "#P1",
SqlDbType = SqlDbType.NVarChar,
Size = 10 ,
Value = param1
};
cmd.Parameters.Add(param);
param = new SqlParameter
{
ParameterName = "#P2",
SqlDbType = SqlDbType.Int,
Value = param2
};
cmd.Parameters.Add(param);
cmd.Connection.Open();
using (IDataReader dataReader = cmd.ExecuteReader())
{
//...
}
}
}
}
then, in case param2 = null as Jack suggested above, the script produced by the code will be identical to the option 2 and will result to the same error. So you cannot use NULL in this case.You cannot set DEFAULT as the value of SQLParameter either.
What you can do is to create a stored procedure to wrap the call to your funcion and move your default value from the function to the SP. Example:
CREATE PROCEDURE dbo.USP_SAMPLE_PROCEDURE
(
#Param1 nvarchar(10),
#Param2 int = NULL, --DEFAULT value now is here (remove it from the function)
#Statement nvarchar(max)
)
AS
BEGIN
SET NOCOUNT ON;
EXEC sp_executesql #Statement,N'#P1 nvarchar(10),#P2 int',#P1=#Param1,#P2=#Param2;
END
The .NET code will look the following way:
public void RunWrapper(string param1, int? param2)
{
using (SqlConnection con = GetProdConection())
{
using (var cmd = new SqlCommand("USP_SAMPLE_PROCEDURE", con))
{
cmd.CommandType = CommandType.StoredProcedure;
var param = new SqlParameter
{
ParameterName = "#Param1",
SqlDbType = SqlDbType.NVarChar,
Size = 10,
Value = param1
};
cmd.Parameters.Add(param);
param = new SqlParameter
{
ParameterName = "#Param2",
SqlDbType = SqlDbType.Int,
Value = param2
};
cmd.Parameters.Add(param);
param = new SqlParameter
{
ParameterName = "#Statement",
SqlDbType = SqlDbType.NVarChar,
Size = -1, //-1 used in case you need to specify nvarchar(MAX)
Value = "SELECT * FROM dbo.UFN_SAMPLE_FUNCTION (#P1, #P2)"
};
cmd.Parameters.Add(param);
cmd.Connection.Open();
using (IDataReader dataReader = cmd.ExecuteReader())
{
//...
}
}
}
}
In this case null as a value for the param2 will be translated to the correct DEFAULT and the following script will be produced:
exec USP_SAMPLE_PROCEDURE #Param1=N'ABC',#Param2=default,#Statement=N'SELECT * FROM dbo.UFN_SAMPLE_FUNCTION (#P1, #P2)'
which will give you the following result:
Col1 Col2
---------- -----------
ABC NULL
I am not sure that this is the best practice. This is just the work-around.
Though you can't set an SQL keyword as the value of a parameter, you could in this case go and get the DEFAULT VALUE.
SELECT COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'table_name' AND COLUMN_NAME = 'column_name'"
if you pass a dot net null value as the parameter value it will use sql DEFAULT
if you pass a dot net DBNull.Value it will use sql NULL