Pass String into #Parameter from a VS report - sql

I did ask a question about this but still cant work out how to get this working, ive looked at some examples where people have used functions, table variables, create types and cant really get any working so wondering if someone could help and maybe explain a little bit of the code they write if its complicated.
Im using Visual studio 2012 to create reports and have on the report a drop down list populated with company names that a user can tick to view info about each of the companies.
What I want to do is get all the company IDs from this drop down and pass it into an In clause in my sql where statement. This is a simplified version of what I have got, im new to this so just trying to see what works for when I have to look at more complex stuff in the future.
Ideally the line ive comented out will be the line that should be used and the values passed into this, but ive added the equivalent line below which I have been using for testing, I get an error on the comma between the 1,2 and
error message syntax error near ','.
If someone can help with how to get this to work using the #companynameParam I have comented out that would be great. CompanyNameParam is the name of the drop down on the VS report.
Declare #ContactID int
--set #ContactID = #CompanyNameParam
set #ContactID = 1,2,3,4,5,6,7,8,9
select CompanyName as 'reportcompanyname'
from company co inner join contacts c on c.CompanyID = co.CompanyID
where ContactID in (#ContactID);

You are getting an error because SET command is used to assign single / scalar value to a single variable at a time and you are trying to assign multiple comma separated string values to a single integer variable #ContactID in one go.
Correct approach is to first declare a local variable of type varchar and not Int as
#CompanyNameParam must be a string.
Declare #ContactID varchar(500);
Then convert comma separated string into a table. [There can be many ways to do this] I'm just giving an example. Create a user defined function as:
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
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
Once done you can re-write the sproc as:
select CompanyName as 'reportcompanyname'
from company co inner join contacts c on c.CompanyID = co.CompanyID
where ContactID in (SELECT * FROM dbo.CSVToTable(#CompanyNameParam));
Check DEMO here..

Related

I'm having trouble with the sql language functions

I have been working in the SQL language for 1 month. That's why I may not understand things. But I always get an error when creating these functions. I wrote the codes down there. The error message is as follows:
Only one expression can be specified in the select list when
the subquery is not introduced with EXISTS.
CREATE FUNCTION deneme(
#ID int
)
RETURNS nvarchar(max)
AS
BEGIN
DECLARE #value nvarchar(max)
SET #value = (
SELECT * FROM information
WHERE #ID = Person_id
)
RETURN #value
END
You cannot assign all columns values into one variable like that, and since you're passing an ID of the person and you want your function to returns the info of that person
CREATE FUNCTION dbo.deneme
(
#ID int
)
RETURNS NVARCHAR(300)
AS
BEGIN
DECLARE #Value NVARCHAR(300) = N'';
SELECT #Value = CONCAT(I.FirstName, N' ', I.LastName)
FROM Information I
WHERE I.PersonId = #ID;
RETURN #Value;
END
As others have pointed out you are trying to place multiple columns/fields in a single column/field.
#ID is a single column. "Select *" is presumably returning more than a single column or else it wouldn't be much help!
In order to change this and make it work as you are trying here, you would need to concat the columns you are trying to return. This is almost surely not the best way to accomplish this but sometimes concating names (for example) is fine.
The other issue you may be running into is even if you changed this to "Select ID" but still have errors it may be because the query returns more than one row matching that criteria. You can work around this (it is a work around most of the time) by limiting the number of rows returned with "TOP 1". But be careful as this may not return the information you want. You can use an order by statement to help ensure it is the correct information (such as order by Time_entered).
The code below with "TOP 1" and concatenating multiple columns (and casting as the same type) will always work.
Again, these are not Best Practices and shouldn't be used to sanitize data in production... but it does show you why you are getting these errors and how to prevent them.
CREATE FUNCTION deneme(
#ID int
)
RETURNS nvarchar(max)
AS
BEGIN
DECLARE #value nvarchar(max)
SET #value = (
SELECT TOP 1 cast(First_name as nvarchar) + N' ' + cast(Last_name as nvarchar) FROM information
WHERE #ID = Person_id
Order by Time_entered desc
)
RETURN #value
END

Using uniqueidentifier with IN Clause in SQL server

I have a stored procedure that takes as an input a string of GUIDs and selects from table where table GUID IN (#Param).
#Param = 'b2e16cdc-1f1b-40e2-a979-f87a6a2457af,
c275dd13-bb54-4b8c-aa12-220b5980cabd,
af3552ec-37b1-4a76-81ad-1bd6b8c4cd6c,
3a7fda02-558b-49a9-a870-30350254d8c0,'
SELECT * FROM dbo.Table1 WHERE
TableGUID IN (#Param)
However, I noticed that the query return values, only if the first GUID matches, otherwise it will not return anything. which means that it only compares with the first GUID in the string.
anyone knows how solve the problem?
declare #sql varchar(max)
set #sql='SELECT * FROM dbo.Table1 WHERE
TableGUID IN ('+#Param+') '
exec (#sql)
We can't do it, because SQL has no concept of Lists, or array or other useful data structures - it only knows about tables (and table based information) so it converts the string list into a table structure when it compiles the command - and it can't compile a variable string, so it complains and you get annoyed. Or at least, I do.
What we have to do is convert the comma separated values into a table first. My initial version was inline, and rather messy, so I re-worked it to a user function and made it a bit more general purpose.
USE [Testing] GO
CREATE FUNCTION [dbo].[VarcharToTable] (#InStr NVARCHAR(MAX))
RETURNS #TempTab TABLE
(id UNIQUEIDENTIFIER NOT NULL)
AS
BEGIN
;-- Ensure input ends with comma
SET #InStr = REPLACE(#InStr + ',', ',,', ',')
DECLARE #SP INT
DECLARE #VALUE NVARCHAR(MAX)
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
This creates a user function that takes a comma separated value string and converts it into a table that SQL does understand - just pass it the sting, and it works it all out. It's pretty obvious how it works, the only complexity is the REPLACE part which ensures the string is terminated with a single comma by appending one, and removing all double commas from the string. Without this, while loop becomes harder to process, as the final number might or might not have a terminating comma and that would have to be dealt with separately.
DECLARE #LIST NVARCHAR(MAX)
SET #LIST = '973150D4-0D5E-4AD0-87E1-037B9D4FC03B,973150d4-0d5e-4ad0-87e1-037b9d4fc03c'
SELECT Id, Descr FROM TableA WHERE Id IN (SELECT * FROM dbo.VarcharToTable(#LIST))
In addition to MikkaRin's answer: a GUID has to be unclosed in apostrophes, so the value in the parameter should look like
'b2e16cdc-1f1b-40e2-a979-f87a6a2457af',
'c275dd13-bb54-4b8c-aa12-220b5980cabd',
'af3552ec-37b1-4a76-81ad-1bd6b8c4cd6c',
'3a7fda02-558b-49a9-a870-30350254d8c0'
In the end, you have to pass something like:
#Param = '''b2e16cdc-1f1b-40e2-a979-f87a6a2457af'',
''c275dd13-bb54-4b8c-aa12-220b5980cabd'',
''af3552ec-37b1-4a76-81ad-1bd6b8c4cd6c'',
''3a7fda02-558b-49a9-a870-30350254d8c0'''
Pay attention to the last comma of the list. It should be removed.

How to perform a WHERE similar to "NOT IN" with a list of value coming from a HTTP FORM?

I have web application developed in C#, where a page is showing the output of a SQL query; the query is a linq call to a store procedure and I am using SQL Server 2008.
One of the column display the tags associated with the result line; on the same page a list of check-boxes is displayed, each check-box correspond to a tag, and if the user turn on or off one or multiple check-box I want to filter the query.
My simplest SQL solution would be to us "NOT IN" like the following:
select * from [mytable] where [tags] not in ('tag1','tag2, etc...)
Given I convert the FORM POST to a string with comma separated values.
Example:
string tags = ParseFormAndConvertCheckBoxToCSV(Page.Request.Form);
string sqlcmd = "select * from [mytable] where [tags] not in (" + tags + ")";
But I don't want to dynamically build the SQL because as far as I know that would be bad.
I can imagine few way of splitting the string with comma separated list of values in SQL nd store the values into a in-memory table:
declare #tagscsv nvarchar(MAX)
declare #notags TABLE(Value nvarchar(50))
set #tagscsv = 'taxi,ivf'
Declare #x XML
select #x = cast('<A>'+ replace(#tagscsv,',','</A><A>')+ '</A>' as xml)
insert into #notags
select t.value('.', 'nvarchar(50)') as v from #x.nodes('/A') as x(t)
select * from [mytable] where [tags] not in (select * from #notags)
...but this too sounds like a dirty trick.
To me it looks like this should be a very common situation, and I figure out a lot of people out there faced this problem in the past, but searching on google I could not find an elegant solution.
Anyone can help?
Edit: Missed the stored procedure part.
With stored procedures you do not have a lot of options. I usually do what you did, split the comma seperated string and save the values to a temp table (or table variable).
CREATE PROCEDURE SplitAndStuff
#List nvarchar(MAX) = NULL,
#SplitOn nvarchar(5) = ',' --This can be replaced with a literal if it is always a comma.
AS
BEGIN
DECLARE #Pos int;
DECLARE #SplitOnLength int;
DECLARE #Results TABLE (value nvarchar(MAX))
SET #SplitOnLength = DATALENGTH(#SplitOn) / 2;
IF (RIGHT(#List, #SplitOnLength) <> #SplitOn) SET #List = #List + #SplitOn; --Add trailling split string if there is not one already.
SET #Pos = CHARINDEX(#SplitOn, #List, 1) --Initalize for loop. (The starting position returned is 1-based, not 0-based.)
WHILE #Pos > 0
BEGIN
INSERT INTO #Results (value) SELECT CAST(SUBSTRING(#List,1,#Pos-1)AS int);
SET #List = SUBSTRING(#List, #Pos+#SplitOnLength, DATALENGTH(#List) / 2);
SET #Pos = CHARINDEX(#SplitOn, #List, 1);
END
END
GO
If your stored procedure is not doing anything else (besides the look up) you could use my original LINQ answer below:
Since you are using LINQ you need to split tagscsv and use the resulting array to perform an "where in" query:
string[] tags = tagscsv.Split(',');
var output = from q in db.Table where !tags.Contains(q) select q;
See also:
http://msdn.microsoft.com/en-us/library/ms132407.aspx
Using LINQ to Perform "WHERE IN (Value1,Value2)" Queries
The NOT IN clause in LINQ to SQL

Variable "IN" expression in SQL [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicates:
SQL Multiple Parameter Values
SQL Server (2008) Pass ArrayList or String to SP for IN()
I would like to SELECT some rows from a table that have certain values which are not known at the time a stored procedure is written. For example, searching for books of a particular type or types in a library database:
SELECT * FROM Books WHERE Type IN (_expr_);
Where I want _expr_ to be ('Humor', 'Thriller') one run, and maybe ('Education') the next, depending on the user's choices. How can I vary the expression at run-time?
Unfortunately, I still have a lot to learn about SQL in general and am not sure if I'm even asking a question that makes sense. I would appreciate any guidance!
This is trickier than you might think in SQL Server 2005 (2008 has table valued parameters which makes it easier)
See http://www.sommarskog.se/arrays-in-sql-2005.html for a review of the methods.
I feel like I've answered this question before...
anyway, I've long used the following user defined split function:
Usage: dbo.Split("#ParamName", ",") where the 2nd parameter is the separator.
You can then join this onto a table, as it returns a table value function with the elementID and Element.
CREATE FUNCTION [dbo].[Split]
(
#vcDelimitedString varchar(max),
#vcDelimiter varchar(100)
)
RETURNS #tblArray TABLE
(
ElementID smallint IDENTITY(1,1), --Array index
Element varchar(1000) --Array element contents
)
AS
BEGIN
DECLARE #siIndex smallint, #siStart smallint, #siDelSize smallint
SET #siDelSize = LEN(#vcDelimiter)
--loop through source string and add elements to destination table array
WHILE LEN(#vcDelimitedString) > 0
BEGIN
SET #siIndex = CHARINDEX(#vcDelimiter, #vcDelimitedString)
IF #siIndex = 0
BEGIN
INSERT INTO #tblArray VALUES(#vcDelimitedString)
BREAK
END
ELSE
BEGIN
INSERT INTO #tblArray VALUES(SUBSTRING(#vcDelimitedString, 1,#siIndex - 1))
SET #siStart = #siIndex + #siDelSize
SET #vcDelimitedString = SUBSTRING(#vcDelimitedString, #siStart , LEN(#vcDelimitedString) - #siStart + 1)
END
END
RETURN
END
another approach, is to build a sql string and use execute to execute it. The string is of "INSERT...SELECT form" and inserts the results into a temporary table. Then you select from the temp.
declare #sql varchar(1000)
set #sql = 'INSERT INTO sometemptable SELECT * FROM Books WHERE Type IN ('
set #sql = #sql + {code that builds a syntactically correct list}
set #sql = #sql + ')'
execute #s_sql
select * from sometemptable
What you do here for sql server 2005 and prior is put the user parameters in a table, and then select from the table:
select columns
from books
where type in
(
select choices
from userchoices
where sessionkey= #sessionkey and userid= #userid
)

TSQL Statement IN

I am having a small problem with the IN SQL statement. I was just wondering if anyone could help me?
#Ids = "1,2,3,4,5"
SELECT * FROM Nav WHERE CONVERT(VARCHAR,NavigationID) IN (CONVERT(VARCHAR,#Ids))
This is coming back with the error below, I am sure this is pretty simple!
Conversion failed when converting the varchar value '1,' to data type int.
The SQL IN clause does not accept a single variable to represent a list of values -- no database does, without using dynamic SQL. Otherwise, you could use a Table Valued Function (SQL Server 2000+) to pull the values out of the list & return them as a table that you can join against.
Dynamic SQL example:
EXEC('SELECT *
FROM Nav
WHERE NavigationID IN ('+ #Ids +')')
I recommend reading The curse and blessings of dynamic SQL before using dynamic SQL on SQL Server.
Jason:
First create a function like this
Create FUNCTION [dbo].[ftDelimitedAsTable](#dlm char, #string varchar(8000))
RETURNS
--------------------------------------------------------------------------*/
/*------------------------------------------------------------------------
declare #dlm char, #string varchar(1000)
set #dlm=','; set #string='t1,t2,t3';
-- tHIS FUNCION RETUNRS IN THE ASCENDING ORDER
-- 19TH Apr 06
------------------------------------------------------------------------*/
--declare
#table_var TABLE
(id int identity(1,1),
r varchar(1000)
)
AS
BEGIN
declare #n int,#i int
set #n=dbo.fnCountChars(#dlm,#string)+1
SET #I =1
while #I <= #N
begin
insert #table_var
select dbo.fsDelimitedString(#dlm,#string,#i)
set #I= #I+1
end
if #n =1 insert #TABLE_VAR VALUES(#STRING)
delete from #table_var where r=''
return
END
And then
set quoted_identifier off
declare #ids varchar(max)
select #Ids = "1,2,3,4,5"
declare #nav table ( navigationid int identity(1,1),theother bigint)
insert #nav(theother) select 10 union select 11 union select 15
SELECT * FROM #Nav WHERE CONVERT(VARCHAR,NavigationID) IN (select id from dbo.ftDelimitedAsTable(',',#Ids))
select * from dbo.ftDelimitedAsTable(',',#Ids)
What you're doing is not possible with the SQL IN statement. You cannot pass a string to it and expect that string to be parsed. IN is for specific, hard-coded values.
There are two ways to do what you want to do here.
One is to create a 'dynamic sql' query and execute it, after substituting in your IN list.
DECLARE #query varchar(max);
SET #query = 'SELECT * FROM Nav WHERE CONVERT(VARCHAR,NavigationID) IN (' + #Ids + ')'
exec (#query)
This can have performance impacts and other complications. Generally I'd try to avoid it.
The other method is to use a User Defined Function (UDF) to split the string into its component parts and then query against that.
There's a post detailing how to create that function here
Once the function exists, it's trivial to join onto it
SELECT * FROM Nav
CROSS APPLY dbo.StringSplit(#Ids) a
WHERE a.s = CONVERT(varchar, Nav.NavigationId)
NB- the 'a.s' field reference is based on the linked function, which stores the split value in a column named 's'. This may differ based on the implementation of your string split function
This is nice because it uses a set based approach to the query rather than an IN subquery, but a CROSS JOIN may be a little complex for the moment, so if you want to maintain the IN syntax then the following should work:
SELECT * FROM Nav
WHERE Nav.NavigationId IN
(SELECT CONVERT(int, a.s) AS Value
FROM dbo.StringSplit(#Ids) a