SQL stored procedure sent data for "IN" condition - asp.net

I have a stored procedure and it has where condition using 'IN' key word. So I want to send more than one value to stored procedure .
This is my stored procedure:
ALTER PROCEDURE [dbo].[xxx]
#COM_KEY varchar(max) = NULL
AS
BEGIN
SELECT
UserName, UserId
FROM
company
WHERE
(COM_KEY IN (#COM_KEY))
END
So I pass the value in here
string companyID = "";
for (int i = 0; i < lbCompanies.Items.Count; i++)
{
if (i == 0)
{
companyID += Convert.ToInt32(lbCompanies.Items[i].Value);
}
else
{
companyID += "," + Convert.ToInt32(lbCompanies.Items[i].Value);
}
}
DataSet ApproveList = DataRepository.TUsersProvider.xxx(companyID);
but there is an error
Conversion failed when converting the varchar value '3087,4058' to data type int
How can I solve it?

You cannot pass a comma-delimited string into #COM_KEY. You should pass an array.
One way to accomplish this task would be using Table-Valued Parameters.
Have a look at this article, under Solution #3: TSQL: Passing array/list/set to stored procedure (MS SQL Server).
In essence, you treat your series of ints as a table you apply JOIN upon, and not query it via WHERE ... IN () phrase.

You can create a UserDefinedFunction which parses the string and puts each integer extracted into a table to solve this issue
CREATE Function fnSplitter (#IDs Varchar(100) )
Returns #Tbl_IDs Table (ID Int) As
Begin
-- Append comma
Set #IDs = #IDs + ','
-- Indexes to keep the position of searching
Declare #Pos1 Int
Declare #pos2 Int
-- Start from first character
Set #Pos1=1
Set #Pos2=1
While #Pos1<Len(#IDs)
Begin
Set #Pos1 = CharIndex(',',#IDs,#Pos1)
Insert #Tbl_IDs Select Cast(Substring(#IDs,#Pos2,#Pos1-#Pos2) As Int)
-- Go to next non comma character
Set #Pos2=#Pos1+1
-- Search from the next charcater
Set #Pos1 = #Pos1+1
End
Return
End
Now alter your stored procedure like this
ALTER PROCEDURE [dbo].[xxx]
#COM_KEY varchar(max) = NULL
AS
BEGIN
SELECT UserName, UserId
From company
WHERE COM_KEY IN (Select ID From dbo.fnSplitter(#COM_KEY))
END
Check this link for detailed Implemetation

The most trivial solution to your problem is to vale SQL string(for dynamic Query) and then execute the statement
ALTER PROCEDURE [dbo].[xxx]
#COM_KEY varchar(max) = NULL
AS
BEGIN
DECLARE #SQL nvarchar(MAX)
SET #SQL=N' SELECT
UserName, UserId
FROM
company
WHERE
(COM_KEY IN ('+#COM_KEY+'))'
EXCE #SQL
END
Solution mentioned by #ssilas777 is the sophisticated version for the same.
Also note that using IN is not always advisable. I have run into
query time out errors
when IN has lot of IDs

Change your code from:
if (i == 0)
{
companyID += Convert.ToInt32(lbCompanies.Items[i].Value);
}
else
{
companyID += "," + Convert.ToInt32(lbCompanies.Items[i].Value);
}
to:
if (i == 0)
{
companyID +="'" + Convert.ToInt32(lbCompanies.Items[i].Value) + "'";
}
else
{
companyID += ",'" + Convert.ToInt32(lbCompanies.Items[i].Value) + "'";
}

Related

Sybase IQ, return the result of a query string in a variable inside a function with "execute"?

Hi can you help me please?, That way I can return the result of a query string in a variable inside a function with "execute"?.
for example
create function my _funcion(in # num_celular char (20), in # str_comando char (120))
returns char (120)
- on exception resume
as
begin
set # exec_qry = Select 1 from dummy
execute (# exec_qry)
return (# the return value of the query "execute")
end
go
thanks
Try this way:
create function my_funcion(#num_celular char (20), #str_comando char (120))
returns char(120)
as
begin
DECLARE #Result char(120)
execute('select #Result = ''Aaa'' ' )
RETURN #Result
end
go
Result of below query
select my_funcion('AA','BB')
is
Aaa
Ok, thanks for answering my question, but look at the issue is much more complex because we are running a Dynamic Sql, formed in while loop to the query is then stored in one string variables and running it, the problem are the function promptly made ​​as follows and is attached below.
create function ms_trafico.fn_mov_cambia_tag( in #num_celular char(20), in #str_comando char(120) )
returns char(120)
--on exception resume
as
begin
set nocount on
set temporary option string_rtruncation='OFF'
--aplican para isql
--set option isql_print_result_set=ALL
--set option isql_show_multiple_result_sets=On
declare #comando varchar(100)
declare #exec_qry varchar(500)
declare #cadena varchar(60)
declare #tag char(15)
declare #columna char(20)
declare #pos int
declare #strCadFinal char(150)
declare #strFono char(20)
set #comando = trim(#str_comando)
set #strFono = trim(#num_celular)
set #exec_qry='select XXX '
set #pos = 1
print "comando " + #comando
while(#pos <= locate(#comando,'<',-1))
begin
set #tag = substr(#comando,locate(#comando,'<',#pos),CHARINDEX('>', SUBSTR(#comando,locate(#comando,'<',#pos),length(#comando))))
set #pos = locate(#comando,'<',#pos) + 1
select atributo_campo into #columna from TB_MOV_MAPEO_COMANDOS where parametro = #tag
set #cadena="replace(XXX,'"||trim(#tag)||"',"||trim(#columna)||")"
set #exec_qry=replace(#exec_qry,'XXX',#cadena)
end
set #comando="'"||trim(#comando)||"'"
set #exec_qry=replace(#exec_qry,'XXX',#comando)
set #exec_qry=#exec_qry||' as comando INTO #strCadFinal FROM VM_ALL_SERVICES_MOVIL where num_celular=#strFono'
execute (#exec_qry)
return (#strCadFinal) "The result of my query with execute method"!!!!!!!!!!
end
go
Thanks for you attention.

Is this sql vulnerability in my code?

Is this sql vulnerability in my code?Because I have Parameterized the SQL ,so no sql Injection? anyone give some suggests will appreciate!If yes, How to repair?
ASP.NET CODE:
public DataTable CompanySearchUser(int pageSize, int currentPage, string whereCondition)
{
DbParameter[] parms = {
DbHelper.MakeInParam("#PageSize",(DbType)SqlDbType.Int,4,pageSize),
DbHelper.MakeInParam("#PageNumber",(DbType)SqlDbType.Int,4,currentPage),
DbHelper.MakeInParam("#where",(DbType)SqlDbType.NVarChar,500,whereCondition)
};
DataTable userlist = DbHelper.ExecuteDataset(CommandType.StoredProcedure, "spCompanySearchUser", parms).Tables[0];
return userlist;
}
SQL CODE:
ALTER PROC [dbo].[spCompanySearchUser]
#PageSize INT
#PageNumber INT,
#where nvarchar(550)--like 'and a=1 '
AS
DECLARE #RowStart INT
DECLARE #RowEnd INT
DECLARE #SQL NVARCHAR(4000)
IF #PageNumber > 0
BEGIN
SET #PageNumber = #PageNumber - 1
SET #RowStart = #PageSize * #PageNumber + 1;
SET #RowEnd = #RowStart + #PageSize - 1;
SET #SQL='
WITH AllUsers
AS (SELECT
UB.UserBaicInfoID,
UB.UserName,
UB.HighestEducation,
UB.Age,
UB.Sex,
UB.WorkExperience,
UB.PositionDesired,
UB.UpdateTime,
Row_number() OVER (ORDER BY UB.UpdateTime DESC) AS RowNumber
From UserBasicInfo UB
WHERE ResumeState=1 '+#where+')
SELECT * FROM AllUsers WHERE RowNumber >=' + Str(#RowStart) + ' AND RowNumber <= ' + Str(#RowEnd) + ''
EXEC sp_executesql #SQL
END
Is this sql vulnerability in my code?Because I have Parameterized the SQL ,so no sql Injection? anyone give some suggests will appreciate!If yes, How to repair?
cannot test it right now but i would say yes it is.
you pass in #where and you do a concatenation, your #where is doing actual sql code not a real "where" clause
then you do a exec on the final sql code
this can be exploited
not sure what your whereCondition look like but try this
public DataTable CompanySearchUser(int pageSize, int currentPage, string whereCondition)
{
/**** add this to test ****/
whereCondition += " or 1=1";
/**** add this to test ****/
DbParameter[] parms = {
DbHelper.MakeInParam("#PageSize",(DbType)SqlDbType.Int,4,pageSize),
DbHelper.MakeInParam("#PageNumber",(DbType)SqlDbType.Int,4,currentPage),
DbHelper.MakeInParam("#where",(DbType)SqlDbType.NVarChar,500,whereCondition)
};
DataTable userlist = DbHelper.ExecuteDataset(CommandType.StoredProcedure, "spCompanySearchUser", parms).Tables[0];
return userlist;
}
It's still insecure and non-performant.
You should just do the SQL dynamically and add the parameters there rather than relying on sp_executeSQL.
Building the string with the # placeholder items cmd.Parameters.AddWithValue("#placeholder", value) values, then adding those parameters, will generally be better, more secure, more manageable, and won't waste SQL's time since it won't be able to optimize your statements.
You may actually get better performance doing it dynamically in code rather that in a stored proc since SQL will cache every one of those statements and re-execute them if an identical statement comes along.

How to have a MySQL procedure return a boolean?

I want to create a procedure that takes in a string, searches the table and the specified column for that string, and returns a 1 if it finds it and a zero if it does not. I am relatively new to SQL and do not know the syntax or commands very well. I want something like this:
CREATE PROCEDURE dbo.GetUsername
(
#Username NCHAR(10)
)
AS
#boolVariable
SELECT #Username FROM Accounts
RETURN #boolVariable
You don't return a value, but instead supply that in a result set.
CREATE PROCEDURE GetUsername
(
#Username NCHAR(10)
)
AS
SELECT Username FROM Accounts WHERE Username = #UserName;
In your calling code, simply check for the existence of a row in the result set.
I'm not sure if you're looking for mysql or mssql solution.
delimiter //
drop procedure if exists search_string //
create procedure search_string (in str varchar(100))
begin
declare b,r bool;
select count(*) into r from your_table where your_field = str;
if r > 0 then
set b = 1;
else
set b = 0;
end if;
select b;
end; //
delimiter //
call search_string('searched_string');

SQL use comma-separated values with IN clause

I am developing an ASP.NET application and passing a string value like "1,2,3,4" into a procedure to select those values which are IN (1,2,3,4) but its saying "Conversion failed when converting the varchar value '1,2,3,4' to data type int."
Here is the aspx code:
private void fillRoles()
{
/*Read in User Profile Data from database */
Database db = DatabaseFactory.CreateDatabase();
DbCommand cmd = db.GetStoredProcCommand("sp_getUserRoles");
db.AddInParameter(cmd, "#pGroupIDs", System.Data.DbType.String);
db.SetParameterValue(cmd, "#pGroupIDs", "1,2,3,4");
IDataReader reader = db.ExecuteReader(cmd);
DropDownListRole.DataTextField = "Group";
DropDownListRole.DataValueField = "ID";
while (reader.Read())
{
DropDownListRole.Items.Add((new ListItem(reader[1].ToString(), reader[0].ToString())));
}
reader.Close();
}
Here is my procedure:
CREATE Procedure [dbo].[sp_getUserRoles](#pGroupIDs varchar(50))
AS BEGIN
SELECT * FROM CheckList_Groups Where id in (#pGroupIDs)
END
Here is a workaround I found to do what you are trying to achieve
CREATE Procedure [dbo].[sp_getUserRoles](
#pGroupIDs varchar(50)
)
As
BEGIN
SELECT * FROM CheckList_Groups Where (',' + #pGroupIDs +',' LIKE '%,' + CONVERT(VARCHAR, id) + ',%')
End
This gets your comma delimited list and compares it to the id's(which are represented like so ',1,', ',2,' etc) in the table using LIKE
If you dont want to use dynamic sql, the best way ive found is to create a function which turns a delimited string into a table, something like this works for an Integer list:
CREATE FUNCTION [dbo].[StringToIntList]
(#str VARCHAR (MAX), #delimeter CHAR (1))
RETURNS
#result TABLE (
[ID] INT NULL)
AS
BEGIN
DECLARE #x XML
SET #x = '<t>' + REPLACE(#str, #delimeter, '</t><t>') + '</t>'
INSERT INTO #result
SELECT DISTINCT x.i.value('.', 'int') AS token
FROM #x.nodes('//t') x(i)
ORDER BY 1
RETURN
END
Then use that in your sp:
CREATE Procedure [dbo].[sp_getUserRoles](
#pGroupIDs varchar(50)
)
As
BEGIN
SELECT * FROM CheckList_Groups Where id in (
SELECT ID FROM dbo.StringToIntList(#pGroupIds,',')
)
End
Sure it can't do that,
The generated query would be sth like this
SELECT * FROM CheckList_Groups Where id in ('1,2,3,4')
and sure it can't be executed.
you can build the query in your stored procedure then execute it with exec
'SELECT * FROM CheckList_Groups Where id in (' + #pGroupIDs + ')'
or
SELECT * FROM CheckList_Groups Where charindex(','+id+',' , #pGroupIDs)>0
but you first must add the ',' to start and end of your parameter in your c# code
It is not possible to put those values (the comma separated string) in a parameter-value.
What you'll have to do, is to create the SQL Statement in your stored procedure dynamically, by string concatenation. You'll have to execute it with the sp_executesql stored procedure then.
CREATE PROCEDURE [dbo].[getUserRoles]( #groupIds NVARCHAR(50) )
AS BEGIN
DECLARE #statement NVARCHAR(255)
SELECT #statement = N'SELECT * FROM CheckList_Groups Where id in ( ' + #pGroupIDs + N')'
execute sp_executesql #statement
END
Also, not that I named the SP getUserRoles instead of sp_getUserRoles.
The reason is very simple: when you execute a stored procedure whose name starts with sp_, then SQL Server will first query the master database to find that stored procedure, which causes a performance hit offcourse.
The way you are trying to do this is slightly wrong. You will need to use EXECUTE in order to achieve this.
CREATE PROCEDURE [dbo].[sp_getUserRoles](#pGroupIDs nvarchar(50))
As
BEGIN
EXECUTE (N'SELECT * FROM CheckList_Groups Where id in (' + #pGroupIDs + ')';
END
DECLARE #TagId NVARCHAR(100) = '43,81'
SELECT * FROM TABLE WHERE TagId IN (SELECT TRIM(VALUE) FROM STRING_SPLIT( #TagId , ',' ) )
USE STRING_SPLIT FUNCTION FOR THIS
You need to use SP_executesql to achieve this functionllity
CREATE Procedure [dbo].[sp_getUserRoles](
#pGroupIDs varchar(50)
)
As
BEGIN
EXECUTE sp_executesql
N'SELECT * FROM CheckList_Groups Where id in (#pGroupIDs)',
N'#level varchar(50)',
#level = #pGroupIDs;
End
The IN clause can't take a bound parameter like that. What it's being given when the query is actually created is SELECT * FROM CheckList_Groups Where id in ('1,2,3,4'). Essentially the IN clause is being passed a single string.
First create function -
Just run this code
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[CSVToTable] (#InStr VARCHAR(MAX))
RETURNS #TempTab TABLE
(id int not null)
AS
BEGIN
;-- Ensure input ends with comma
SET #InStr = REPLACE(#InStr + ',', ',,', ',')
DECLARE #SP INT
DECLARE #VALUE VARCHAR(1000)
WHILE PATINDEX('%,%', #INSTR ) <> 0
BEGIN
SELECT #SP = PATINDEX('%,%',#INSTR)
SELECT #VALUE = LEFT(#INSTR , #SP - 1)
SELECT #INSTR = STUFF(#INSTR, 1, #SP, '')
INSERT INTO #TempTab(id) VALUES (#VALUE)
END
RETURN
END
GO
Then -
Use function in bracket with select statment -
DECLARE #LIST VARCHAR(200)
SET #LIST = '1,3'
SELECT Id, Descr FROM CSVDemo WHERE Id IN (SELECT * FROM dbo.CSVToTable(#LIST))

SQL server conditional select statement

Ok, this might be an easy one, but I just can't get it.
I am creating a page which will query a table with many columns and most items are not unique. I need to be able to get a list of records that match as many of the (up to 4) search criteria as possible.
Example:
I am user searching for the following items, I enter at least one and up to 4 of the items below in a text box:
Name, age, gender, weight (user may or may not fill in all of them).
If he just enters "F" for gender, then he will get a list of thousands of females with their name, age, gender and weight.
However if he enters "F" for gender and "300" for weight, he will get a much smaller list of returned records.
I need to be able to create a sql statement that can perform that search with that functionality.
advTHANKSance
I've used similar to the one below to do what you are trying:
DECLARE #Gender varchar(1)
DECLARE #Age int
DECLARE #Weight int
DECLARE #Name varchar(64)
SELECT * FROM MyTable
WHERE
(#Gender is null OR Gender = #gender)
AND (#weight is null OR Weight = #weight)
AND (#Age is null OR age = #Age)
and (#Name is null OR Name = #Name)
if you were to create a stored procedure (which i would recommend) it would look like this:
CREATE PROCEDURE SelectRecords
#Gender varchar(1),
#Age int,
#Weight int,
#Name varchar(64)
AS
SELECT * FROM MyTable
WHERE
(#Gender is null OR Gender = #gender)
AND (#weight is null OR Weight = #weight)
AND (#Age is null OR age = #Age)
and (#Name is null OR Name = #Name)
What this stored procedure is doing is checking to see if you passed a value in for the specific parameter. If you DID NOT then it will be null and the condition will be true. if you DID then it will not be null and the second condition must evaluate to true for the record to be returned.
I've often seen this done with the following SQL statement (where #gender, #weight, #age, and #name are filled in with data from the user, and gender, weight, age, and name are table fields):
SELECT * FROM MyTable
WHERE
gender = COALESCE(#gender, gender)
AND weight = COALESCE(#weight, weight)
AND age = COALESCE(#age, age)
and name= COALESCE(#name, name)
(Edit: I just wanted to add a short explanation of why this works for anyone not familiar with coalesce. The coalesce function takes the first not-null value of the 2 passed to it. So if there is something in the # parameter, which means the user entered data for that field, it will check if the field equals that user-entered value; if the user didn't enter anything and the # parameter is null, it will test against the second value, which is the field itself - and as the field is always equal to itself, this will return all records - it won't filter based on this field at all.)
I did stuff like this by combining a null check with the parameter. If it was null then everything got included, if not then the other part actually mattered
CREATE myFunnyProc ( #p1 nvarchar(10), #p2 nvarchar(10) ) AS BEGIN
SELECT * FROM dbo.myReallyLongTable table
WHERE
(#p1 is null or table.name LIKE #p1)
AND (#p2 is null or table.age = #p2)
END
#Abe - Your solution will work IF Age and Weight are not between Name and Gender in the SELECT statement. I know cuz I tried it :) on SQL Server 2008. I made a table with many records with NULLs scattered throughout. I also made a proc, and running it as you wrote it would not return rows when age or weight were entered as parameters. I moved the int based params in the SELECT statement to the top or bottom of list, both worked. But nesting the int params between the string params in the select statement and then specifying the int params made it fail...
So why did the original not work?? :) Anyone?
BTW, the COALESCE will not work at all (yes, I tested it and reordered the SELECT statement in the proc). It LOOKED like it should work...
Seeing as you are using ASP.NET, you could take a look at LINQ-to-SQL which solves this in a very elegant way:
var query = db.T_Persons;
if (txtGender.Text != string.Empty)
query = query.Where(x => x.Gender == txtGender.Text);
if (txtWeigth.Text != string.Empty)
query = query.Where(x => x.Weight == int.Parse(txtWeight.Text));
...
Of course, you'll need to be using .NET 3.5 or newer.
The answer is something most people try to stay away from it is dynamic sql.
I would suggest creating a stored procedure you can call for this, but here is the code. You need to put the name of your table in place of tablename.
Declare
#gender varchar(1),
#weight int,
#age int,
#name varchar(100),
#sql varchar(200),
#variableCount int
set #variableCount = 0
set #sql = 'select * from tablename'
if(#gender is not null)
Begin
#sql += ' where gender = #gender'
#vaiableCount = #VariableCount+1
End
if(#weight is not null)
Begin
if(#variableCount = 0)
Begin
#sql += ' Where '
End
else
#sql += ' And '
#sql += 'weight = #weight'
End
if(#age is not null)
Begin
if(#VariableCount = 0)
Begin
#sql += ' where '
End
else
#sql += ' And '
#sql += 'age = #age'
End
if(#name is not null)
Begin
if(#VariableCount = 0)
Begin
#sql += ' where '
End
else
#sql += ' And '
#sql += 'name = #name'
End
execute #sql

Resources