How to do Sql server procedure using parameters like where #para1 = #para2 - c#

I have a procedure with a single select statement. I am need to create some 50 procedures like the one below..
create procedure foo1 as
select cityid, cityname from footballteam
the footballteam will be common in all my procedures, Instead of creating 50 single procedures, I want to code like below and send 3 parameters from my c# page
create procedure foo1 (#id bigint, #name varchar(50), #param bigint)as
select #id, #name from footballtem where #id =#param
can i pass like this in sql server ?/ How to do like this
will I am able to do procedure overloading in sql server, some time I need to pass only two parameters and i want to get a particular value , I will pass three or more parameters ....

For a pure TSQL answer:
create table footballtem(id int identity(1,1),cityid int, cityname varchar(50))
go
insert footballtem(cityid, cityname) values (123, 'abc')
insert footballtem(cityid, cityname) values (456, 'def')
go
create procedure foo1 (#id sysname, #name sysname, #param bigint) as
declare #sql nvarchar(100) = 'select ' + QUOTENAME(#id) + ','
+ QUOTENAME(#name) + ' from footballtem where '
+ QUOTENAME(#id) + '=#param'
exec sp_ExecuteSql #sql, N'#param bigint', #param
go
exec foo1 'cityid','cityname',123
(credit is due to Mikael Eriksson re QUOTENAME)
Note that QUOTENAME makes the #name and #id injection safe.
Note also, though, that the varying parameter (#param) is safe from injection - we don't need to validate that anywhere; and that this will allow query-plan re-use via sp_ExecuteSql

No; that would do a comparison on the parameter values, and return the parameter values. To do that, you would have to substitute the values at the caller, for example:
string idColumn = "id", nameColumn = "name";
string tsql = string.Format(#"
create procedure foo1 (#param bigint)
as select [{0}], [{1}] from footballtem where [{0}]=#param", idColumn,nameColumn);
and have 50 SPs; you can do the same in TSQL, using sp_ExecuteSQL against an already replaced string, but IMO it would be better to do this at the app tier than inside the database.
Also; question whether you really need stored procedures... that one isn't really going to help much; a parameterised TSQL query is much simpler, just as fast, and easier to deploy.

I'm not sure if I understand you correctly, but you can specify a default value for a stored procedure parameter in T-SQL. So you can omit it while calling.
CREATE PROCEDURE Proc1 #param1 int, #param2 int = -1 AS SELECT case when #param2=-1 then somefield else #param2 end as column from sometable where somekeyfield=#param1; GO

(assuming MS SQL Server)
MS SQL server does not support procedure overloading (as Oracle Does) but does support input and output parameters like this:
create procedure foo1 (
#param bigint
, #id bigint out
, #name varchar(50) out
)as
select
#id = fbt.id
,#name = fbt.name
from
footballteam fbt
where fbt.id =#param
#id and #name have to be passed in as null value output paramters of the correct type. After execution (cmd.executeNonQuery) you can inspect the command object to get the new parameter values back out.
I am not sure I am reading your question correctly, but if I am then this should get what you want..
*Adding better code sample after question *
//_assumes the following using statements at the top of code file:_
//using System.Data;
//using System.Data.SqlClient;
public string getTeam(int CityID)
{
string name;
using (var cmd = new SqlCommand("foo1",new SqlConnection("myConnectionStringGoesHere")))
{
cmd.Parameters.Add(new SqlParameter("#param", CityID));
cmd.Parameters.Add(new SqlParameter("#id", SqlDbType.BigInt){Direction=ParameterDirection.Output});
cmd.Parameters.Add(new SqlParameter("#name", SqlDbType.VarChar,50) { Direction = ParameterDirection.Output });
cmd.Connection.Open();
cmd.ExecuteNonQuery();
name = cmd.Parameters["#name"].Value.ToString();
cmd.Connection.Close();
}
return name;
}

I think you were asking for the following:
create procedure foo1 (#id bitint out, #name bigint out, #param bigint)
as
select #id=cityid, #name=cityname from footballteam where teamname = #param
But your question makes it seem like you are trying to dynamically change the column names per query.

There is a way to do overloading on MSSQL. Here how it goes:
For example we have a sp_Personel procedure which takes personel type as parameter and lists personel of that type.
CREATE PROCEDURE [dbo].[sp_Personel]
#PersonelType int
AS
SELECT Name, JoinDate, PersonelType, Salary
FROM Personel
WHERE PersonelType = #PersonelType
END
Now, you want another procedure which will be for personel join dates.
CREATE PROCEDURE [dbo].[sp_Personel];2
#JoinDate datetime
AS
SELECT Name, JoinDate, PersonelType, Salary
FROM Personel
WHERE JoinDate <= #JoinDate
END
To call second procedure from management studio;
[dbo].[sp_Personel];2 N'9/26/2010'

Related

C# Web API using Dapper to insert data using a stored procedure - unable to get inserted record Id

I'm new to Dapper - please help me. How can I get the inserted record value after a successful insert?
Stored procedure:
ALTER PROCEDURE Sp_InsertTestData
#Name varchar(50),
#gender int,
#refres int OUTPUT
AS
BEGIN
INSERT INTO Test_Master (Name, Gender)
VALUES (#Name, #gender);
SELECT #refres = SCOPE_IDENTITY()
SELECT #refres as M_SID
END
When I execute this stored procedure in SQL like this:
DECLARE #refres INT
EXEC Sp_InsertTestData 'test12',1,#refres
I'm getting an output showing the last inserted row's value.
But when this stored procedure is executed from C# code, every time I'm getting a value of 1:
using (SqlConnection con = new SqlConnection(_configuration.GetConnectionString("DatabaseConnection")))
{
con.Open();
SqlTransaction sqltrans = con.BeginTransaction();
var param = new DynamicParameters();
param.Add("#Name", Bindtestmaster.Name);
param.Add("#gender", Bindtestmaster.Gender);
param.Add("#refres");
res = con.Execute("Sp_InsertTestData", param, sqltrans, 0, CommandType.StoredProcedure);
}
That's because you are getting the result of the stored procedure call, which tells you the number of rows inserted (which is 1).
You want to read the output parameter #refres (and add it to your DynamicParameters as an output parameter)
/* ... */
param.Add("#refres", dbType: DbType.Int32, direction: ParameterDirection.Output);
con.Execute("Sp_InsertTestData", param, sqltrans,0,CommandType.StoredProcedure);
var yourId = param.Get<int>("#refres");
Btw, on your stored procedure instead of:
select #refres=SCOPE_IDENTITY()
You might want to prefer this:
SET #refres = SCOPE_IDENTITY() AS INT
And I'm not sure what that last SELECT is there for
Or directly output the inserted ID (using the OUTPUT SQL clause on the INSERT) and then you could read the result, and not have an output parameter at all.
Since your stored procedure also selects the output:
select #refres as M_SID
An easier way of doing this might be:
var id = con.ExecuteScalar<int>("Sp_InsertTestData", new {
Name = Bindtestmaster.Name,
gender = Bindtestmaster.Gender
}, sqltrans, 0, CommandType.StoredProcedure);
and forget DynamicParameters etc. You could also consider using the OUTPUT clause in the SQL, to simplify it:
ALTER PROCEDURE Sp_InsertTestData
#Name varchar(50), #gender int
AS
BEGIN
INSERT INTO Test_Master(Name, Gender)
OUTPUT INSERTED.Id -- whatever the column is here
VALUES (#Name, #gender);
END

Must declare scalar variable when passing bool

SQL Server is 2014. Visual Studio 2017.
I am trying to pass 3 parameters to SQL Server, but getting this error:
System.Data.SqlClient.SqlException: 'Must declare the scalar variable "#Title1Item".'
I've included both the C# and SQL Server side code. Can anyone tell me what I am doing wrong?
public DataTable GetInventoryByAssetDescription (string Desc, string DispositionText, bool Title1Item) {
DataTable myRecords = new DataTable();
using (SqlConnection cn = new SqlConnection (ConnectionString)) {
// Specify which stored procedure to use and add a parameter.
SqlCommand cmd = new SqlCommand ("GetInventoryByAssetDescription", cn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue ("#SearchString", Desc);
cmd.Parameters.AddWithValue ("#DispositionText", DispositionText);
cmd.Parameters.AddWithValue ("#Title1Item", Title1Item);
SqlDataAdapter da = new SqlDataAdapter (cmd);
cn.Open();
da.Fill (myRecords);
}
ALTER PROCEDURE [dbo].[GetInventoryByAssetDescription] (
#SearchString varchar(30),
#DispositionText varchar(200),
#Title1Item bit
)
I expect the 3 parameters to be passed through to the stored procedure, but am getting this error on the da.fill (myRecords) line:
System.Data.SqlClient.SqlException: 'Must declare the scalar variable "#Title1Item".'
EDIT: So sorry, guys. Rusty old programmer here. Stored procedure code:
USE [Inventory]
GO
/****** Object: StoredProcedure [dbo].[GetInventoryByAssetDescription] Script Date: 6/4/2019 8:30:15 AM ******/
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO
ALTER PROCEDURE [dbo].[GetInventoryByAssetDescription]
(
#SearchString varchar(30),
#DispositionText varchar(200),
#Title1Item bit
)
AS
begin
SET NOCOUNT ON
declare #sql nvarchar (2000)
select #SearchString=UPPER(#SearchString)
set #sql = ' select in_eq_ID,
in_eq_TagNumber as TagNumber,
Title1Item,
in_eq_AssetDescription as Description,
in_eq_ExtendedDescription as ExtendedDescription,
in_eq_SerialNumber as SerialNumber,
in_eq_ValuationAmount as TotalValue,
in_eq_CustodianName as Name,
in_eq_ComplexBuilding as ShortLocation,
in_eq_SubLocationCode as ShortRoomNumber,
in_ca_Categories.in_ca_CategoryName as CategoryName,
in_eq_DispositionDate as DispositionDate,
DATEADD (dd, 0, DATEDIFF (dd, 0, in_eq_Equipment.in_eq_AcquisitionDate)) as AcquisitionDate
from in_eq_Equipment
LEFT JOIN in_ca_Categories ON in_eq_Equipment.in_eq_CategoryID_fk = in_ca_Categories.in_ca_CategoryID
where #Title1Item = Title1Item
AND upper (in_eq_AssetDescription) LIKE upper ('''+ #SearchString + ''')'
set #sql=#sql+' ' + ISNULL(#DispositionText,' ') + ' order by in_eq_AssetDescription'
execute (#sql)
return
end
When you use the datatype BIT in SQLServer as SP parameter, from C# you have to send the values 1 or 0 not true or false so you have to convert bool to int (1 for true and 0 for false)
For example
cmd.Parameters.AddWithValue ("#Title1Item", Title1Item ? 1 : 0);
or
cmd.Parameters.AddWithValue ("#Title1Item", (int)Title1Item);
Right; the problem is your SP, not the C# code. It cannot work in the way written. Parameters and local variables are not "ambient" - they won't be defined inside an EXEC or EXECUTE block, because they are not scoped in there. In some cases there is sp_executesql that allows you to pass through parameters/values by declaring them and adding them as additional parameters, but in your case this would not be a good idea, because fundamentally you are doing something incredibly dangerous, i.e. concatenating inputs to create SQL. Your stored procedure right now is a SQL injection hole and could be used to take over your server with minimal effort. If you're OK with having your data stolen and your database destroyed, you could inline/concatenate #Title1Item just like you did with the others, but: this is a terrible idea and it will hurt you.

passing string as a table type from c# code in to stored procedure

I want to pass a table as a string into stored procedure as a parameter and want to retrieve data from that particular table for e.g. I have made some tables which is year wised like purchase20162017,transfer20162017.
so problem is this i have created a dropdown box in which i have shown the years list like 2016-2017,2017-2018,2018-2019 user select the year from the dropdown box and click a button called generate ledger button. so i take a string from this dropdown box like this
string str = DropDownList1.SelectedItem.ToString();
str = str.Replace(#"-", "");
string purdtb = "purchase" + str;
now i have got exact table in purdtb but it is in string, i want to pass this string value as a parameter in stored procedure. Please tell me how to do this so that i can convert this string as a table name.
There are 2 ways.
A) The best one is:
string connectionString =
ConsoleApplication1.Properties.Settings.Default.ConnectionString;
//
// In a using statement, acquire the SqlConnection as a resource.
//
using (SqlConnection con = new SqlConnection(connectionString))
{
//
// Open the SqlConnection.
//
con.Open();
//
// The following code uses an SqlCommand based on the SqlConnection.
//
using (SqlCommand command = new SqlCommand("SELECT TOP 2 * FROM Dogs1", con))
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine("{0} {1} {2}",
reader.GetInt32(0), reader.GetString(1), reader.GetString(2));
}
}
}
In Dogs1 put your table, use variables to modify the query via code. Why you want to put dynamic table names which is pretty awkward in you stored procedures when you can manage everything from the code with a variable?
B) The second way (not good but works):
Use in your stored procedure sp_executesql, pass the table names as parameters and build the query inside the stored procedure as shown in the documentation and catch the result.
This way you can build the query piece by piece and put in it everything you want (except for SQL reserved keyword, with which you should be careful anyway).
declare #SqlString nvarchar(2000)
declare #ParamDef nvarchar(2000)
set #SqlString = N'exec proc1 #param1, #param2, #param3'
set #ParamDef = N'#param1 bit, #param2 bit, #param3 bit'
EXECUTE sp_executesql #SqlString ,#ParamDef, #param1 = 0, #param2 = 1, #param3 = 1
To explain, sp_executesql works something like this
EXECUTE sp_executesql
N'proc1', -- SQL
N'#param1 bit, #param2 bit, #param3 bit', -- DECLARE
#param1 = 0, #param2 = 1, #param3 = 1 -- VALUES
Which gets translated to
EXECUTE sp_executesql
N'proc1', -- SQL
N'#param1 bit, #param2 bit, #param3 bit', -- DECLARE
#param1 = 0, #param2 = 1, #param3 = 1 -- VALUES
-- DECLARE
declare #param1 bit, #param2 bit, #param3 bit
-- VALUES
select #param1 = 0, #param2 = 1, #param3 = 1
-- SQL
proc1
3) Worst scenario:
Calling sp_executesql from C# code via Sqlcommand

Store multiple values from SELECT statement and then insert into different table

I want to use a SELECT statement into a table based on multiple values from ListBox and get multiple IDs and then run an INSERT statement and store the multiple IDs into a different table from INSERT statement.
My code below is not working as I am getting "NULL" in single row instead of multiple IDs in multiple rows.
I am using a stored procedure for all the SQL statements.
Please see my code below:
Code-behind of my ASPX web page:
string listboxvalue = "";
foreach (ListItem item in listbox.Items)
{
if (item.Selected)
{
listboxvalue += item.Text + ',';
}
}
listboxvalue = listboxvalue.Substring(0, listboxvalue.Length - 1);
cmd.Parameters.AddWithValue("spselectvalue", listboxvalue);
Stored procedure:
#spselectvalue nvarchar(MAX),
// Select multiple Ids based on multiple items from list box
DECLARE #Dis TABLE (DisID int)
INSERT INTO #Dis
SELECT DId
FROM [table name]
WHERE [COLUMN] IN ('+#spselectvalue +')
EXEC sp_executesql #Dis
// Insert multiple Ids (from above select statement) into different table
INSERT INTO [dbo].[DifferentTable] ([SelectedIds])
VALUES
(
(SELECT DisID from #Dis)
)
Seems like your problem is that you don't know how to get a list of ID's into a stored procedure for processing. Hopefully this example will help. If you do it this way, you won't have to mess with comma-delimited strings or dynamic SQL.
First, define a SQL type that can contain your list of IDs:
CREATE TYPE dbo.MyList
AS TABLE
(
ID VarChar(50)
);
Then, write a stored procedure that accepts this type as its input parameter:
CREATE PROCEDURE dbo.InsertAList
#List AS dbo.MyList READONLY
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [dbo].[DifferentTable]
SELECT ID FROM #List
END
Now to bind your data to the stored procedure from the c# end. To bind it, you have to store the data in a DataTable first, which is relatively easy:
var table = new DataTable();
table.Columns.Add("ID", typeof(string));
foreach (ListItem item in listBox.Items.Where(i => i.Selected))
{
table.Rows.Add(item.Text);
}
Then submit the table to the stored procedure like so:
var cmd = new SqlCommand()
{
CommandType = CommandType.StoredProcedure,
CommandText = "InsertAList",
Connection = myConnection
};
cmd.Parameters.Add(new SqlParameter("#List", SqlDbType.Structured)
{
TypeName = "dbo.MyList",
Value = table
});
cmd.ExecuteNonQuery();
That doesn't seem like a valid (or logical) SQL. The exec sp_executesql #Dis shouldn't be there or #Dis shouldn't be a table.
When using sp_executesql the parameter should be a string containing the statement to execute (documentation).
Detailed answer can be found here.
Also: the INSERT INTO #Dis ... line won't work as you expect when written that way, for the reasons mentioned in the linked question (and Erland Sommarskog blog mentioned there).
Selected values considered as numeric.
DECLARE #Sqltext nvarchar(max) =
'INSERT INTO [dbo].[DifferentTable] ([SelectedIds])
SELECT DId
FROM [table name]
WHERE [COLUMN] IN (' + #spselectvalue + ')'
EXEC sp_executesql #Sqltext

Safely get next SQL Server sequence value from .NET

I'm experimenting with SEQUENCE objects in SQL Server, and getting the next value with C# by specifying the sequence name. Ranges are simple, because there is a stored procedure for them, and you can pass the sequence name;
public static T Reserve<T>(string name, int count, SqlConnection sqlConn)
{
using (var sqlCmd = new SqlCommand("sp_sequence_get_range", sqlConn))
{
sqlCmd.CommandType = CommandType.StoredProcedure;
var firstValueParam = new SqlParameter("#range_first_value", SqlDbType.Variant) { Direction = ParameterDirection.Output };
sqlCmd.Parameters.AddWithValue("#sequence_name", name);
sqlCmd.Parameters.AddWithValue("#range_size", count);
sqlCmd.Parameters.Add(firstValueParam);
sqlCmd.ExecuteNonQuery();
return (T)firstValueParam.Value;
}
}
But what about single values? It seems to me that I can either call the above with a count of '1', or I can construct the SQL dynamically. i.e.
var sqlCmdStr = string.Format("SELECT NEXT VALUE FOR {0}", name);
Which I know to generally be bad practice (i.e. SQL injection).
What would anyone suggest?
Which I know to generally be bad practice (i.e. SQL injection).
Not every dynamic SQL is evil.
Whether you are open to SQL injection depends on where the value (that gets inserted in SQL text) comes from. If it comes from a place under a tight control of your code (e.g. a switch statement that chooses from a set of string constants) then SQL injection is not an issue.
Or, you could simply have a separate query for each sequence (assuming you don't have very many of them).
My suggestion is a combination of both #Gserg's answer and your current solution. Write a stored procedure that takes a VARCHAR parameter #Name. Build the sql string in the stored procedure, using QUOTENAME as suggested by #GSerg. Use EXEC or sp_executesql to run the script.
Something like this (freehand):
CREATE PROCEDURE [GetNext]
#Name VARCHAR(50)
AS
BEGIN
DECLARE #sql VARCHAR(200);
SET #Name = QUOTENAME(#Name, '[');
SET #sql = 'SELECT NEXT VALUE FOR ' + #Name;
EXEC (#sql);
END
Another version of Paul's solution, which will return formatted alphanumeric Key from SQL Sequence
CREATE PROCEDURE [sp_GetNextKey]
#Name NVARCHAR(50),
#FormatText NVARCHAR(50)
AS
--DECLARE #Name NVARCHAR(50)='CustomerKeySequence'
--DECLARE #FormatText NVARCHAR(50) = 'CUS0000#'
DECLARE #sql NVARCHAR(200) = 'SELECT FORMAT((NEXT VALUE FOR ' + QUOTENAME(#Name, '"') + '),'+QUOTENAME(#FormatText, '''')+')';
EXEC (#sql)
/*
RETURNS i.e CUS00184
*/
When I need to do a similar thing, I do this:
string sanitized_name;
using (var sqlCmd = new SqlCommand("select quotename(#unsafe_name, '[');", sqlConn))
{
sqlCmd.Parameters.AddWithValue("#unsafe_name", name);
sanitized_name = (string)sqlCmd.ExecuteScalar();
}
using (var sqlCmd = new SqlCommand(string.Format("select next value for {0};", sanitized_name), sqlConn))
{
...
}
Or create a server-side procedure that does the same.

Categories