FnSplit not working for SQL stored procedure to take multiple parameters - sql

I have a stored procedure that currently takes in one value(ChainId) for a parameter. I am trying to allow the user to select multiple values of(ChainId). My where statement is below. Could someone help point me in a better direction than I am going now. Currently the query will run and return no data if I select multiple values for the parameter.
WHERE EndAuth is null AND CL.CHIND in(
SELECT [Value] FROM dbo.FnSplit(#ChainId, ','))
ORDER BY CL.CHIND

This is a popular function in SQL Server, so I'll assume you're working with that. Make sure your parameter is of type Varchar(MAX). #ChainId is passed as your string (ideally for SSRS) and ',' is passed as your delimiter. In SSRS, if you have a text box for your users to manually enter multiple values, they will enter something like 'value1, value2, value3'.
Test this out:
Declare #Yes_No Varchar(Max)
Set #Yes_No = 'y,n'
Select #yes_no
Select * from SplitString('y,n',',')
Select * from SplitString(#Yes_No,',')
Your results will be
y,n
----
y
n
----
y
n
Why I say to use Varchar(Max) and not int, or Varchar(10) for example, is because that would stop the function from reading all the values prematurely.
Try this:
Declare #Yes_No Varchar(1)
Set #Yes_No = 'y,n'
Select * from SplitString(#Yes_No,',')
The result will be:
y
The reason is because the function only accepts a value of 1 character in length, and splits that. As you can see, there isn't much to split.
This is just the way SSRS accepts parameters. FN_Split isn't necessarily a built-in function, but a widely popular one designed to allow you to pass multiple values to a string, with a pre-specified delimiter. So make sure you also go to your parameter in the report and specify that it will allow for multiple values. You will also want to supply a list of potential values for your users to select from. You'll either do this by manually populating a small list or providing another data source in the form of a stored procedure or table.

Related

Is it better to use Custom TABLE TYPE as parameter instead of SQL "IN" clause when passing a large comma separated value

I have a stored procedure it takes comma separated string as input. Which might be too large some times approximately more than 8 thousand characters or more. In that situation, query performance goes down sometimes. And I think there is a limitation for the character length inside the IN clause. For that, sometimes I get errors. Now, I need to know is it better to use a Custom TABLE TYPE as parameter and use Inner JOIN to find the result. If it is then why is it. Here are my 2 stored procedures (minimal code):
CREATE TYPE [dbo].[INTList] AS TABLE(
[ID] [int] NULL
)
Procedure 1
CREATE PROCEDURE [report].[GetSKU]
#list [INTList] READONLY,
AS
Select sk.SKUID,sk.Code SCode,sk.SName
FROM SKUs sk
INNER JOIN #list sst ON sst.ID=sk.SKUID
Procedure 2
CREATE PROCEDURE [report].[GetSKU]
#params varchar(max),
AS
Select sk.SKUID,sk.Code SCode,sk.SName
FROM SKUs sk
WHere CHARINDEX(','+cast( sk.SKUID as varchar(MAX))+',', #params) > 0
Now, which procedures is better to use.
Note: Original Stored Procedures does have few more Joins.
As this question did raise quite some discussion in comments but did not get any viable answer, I'd like to add the major points in order to help future research.
This question is about: How do I pass a (large) list of values into a query?
In most cases, people need this either in a WHERE SomeColumn IN(SomeValueList)-filter or to JOIN against this with something like FROM MyTable INNER JOIN SomeValueList ON....
Very important is the SQL-Server's version, as with v2016 we got two great tools: native STRING_SPLIT() (not position-safe!) and JSON support.
Furthermore, and rather obvious, we have to think about the scales and values.
Do we pass in a simple list of some IDs or a huge list with thousands of values?
Do we talk about simple integers or GUIDs?
And what's about text values, where we have to think about dangerous characters (like [ { " in JSON or < & in XML - there are many more...)?
What about CSV-lists, where the separating character might appear within the content (quoting / escaping)?
In some cases we might even want to pass several columns at once...
There are several options:
Table valued parameter (TVP, CREATE TYPE ...),
CSV together with string splitting functions (native since v2016, various home brewed, CLR...),
and text-based containers: XML or JSON (since v2016)
Table valued paramter (TVP - the best choice)
A table valued parameter (TVP) must be created in advance (this might be a draw back) but will behave as any other table once created. You can add indexes, you can use it in various use cases and you do not have to bother about anything under the hood.
Sometimes we cannot use this due to missing rights to use CREATE TYPE...
Character separated values (CSV)
With CSV we see three approaches
Dynamic Sql: Create a statement, where the CSV list is simply stuffed into the IN() and execute this dynamically. This can be a very efficient approach, but will be open to various obstacles (no ad-hoc-usage, injection threat, breaking on bad values...)
String splitting functions: There are tons of examples around... All of them have in common that the separated string will be returned as a list of items. Common issues here: performance, missing ordinal position, limits for the separator, handling of duplicate or empty values, handling of quoted or escaped values, handling of separators within the content. Aaron Bertrand did some great research about the various approaches of string splitting. Similar to TVPs one draw back might be, that this function must exist in the database in advance or that we need to be allowed to execute CREATE FUNCTION if not.
ad-hoc-splitters: Before v2016 the most used approach was XML based, since then we have moved to JSON based splitters. Both use some string methods to transform the CSV string to 1) separated elements (XML) or 2) into a JSON-array. The result is queried by 1) XQuery (.value() and .nodes()) or 2) JSON's OPENJSON() or JSON_VALUE().
Text based containers
We can pass the list as string, but within a defined format:
Using ["a","b","c"] instead of a,b,c allows for immediate usage of OPENJSON().
Using <x>a</x><x>b</x><x>c</x> instead allows for XML queries.
The biggest advantage here: Any programming language provides support for these formats.
Common obstacles like date and number formatting is solved implicitly. Passing JSON or XML is - in most cases - just some few lines of code.
Both approaches allow for type- and position-safe queries.
We can solve our needs without the need to rely on anything existing in advance.
For the very best performance you can use this function:
CREATE FUNCTION [dbo].StringSplit
(
#String VARCHAR(MAX), #Separator CHAR(1)
)
RETURNS #RESULT TABLE(Value VARCHAR(MAX))
AS
BEGIN
DECLARE #SeparatorPosition INT = CHARINDEX(#Separator, #String ),
#Value VARCHAR(MAX), #StartPosition INT = 1
IF #SeparatorPosition = 0
BEGIN
INSERT INTO #RESULT VALUES(#String)
RETURN
END
SET #String = #String + #Separator
WHILE #SeparatorPosition > 0
BEGIN
SET #Value = SUBSTRING(#String , #StartPosition, #SeparatorPosition- #StartPosition)
IF( #Value <> '' )
INSERT INTO #RESULT VALUES(#Value)
SET #StartPosition = #SeparatorPosition + 1
SET #SeparatorPosition = CHARINDEX(#Separator, #String , #StartPosition)
END
RETURN
END
This function return table - select * from StringSplit('12,13,14,15,16', ',') so you can join this function to your table or can use IN on the where clause.

How do I use collection as a parameter in SQL Server (2008) SP?

So, I have this stored procedure (let's call it SP1) that gets as parameters two strings (say string1 and string2)
However, I need to call this stored procedure a lot from my code, so in order to make things faster, I was thinking of doing it in bulk. Collecting all of the parameters into a collection of some sort, and then send this.
From what I understand I need to use a DataTable on the code side, and a custom table type as the parameter on the SQL Server side - ok, cool. But...now what?
But... how do I get from there to the point where I actually go
EXEC SP1 string1, string2 or something along those lines?
Not sure whether this is what you like to achieve, instead of parsing those two string parameters, you would like to get a table holding all of the string row?
If so, you could you use UDF within SP,
Check here:
CREATE FUNCTION [dbo].[Name]
(
#parameter
)
RETURNS #Table TABLE
(
col1 varchar(50),
col2 varchar(50)
)
AS
BEGIN
**the query that inserts each records to TABLE**
END
Then using this [Name] UDF in your SP
Create a table type
CREATE TYPE Strings AS TABLE ( String1 VARCHAR(50), String2 VARCHAR(50) )
Alter your procedure to accept table type as input
ALTER PROCEDURE Usp_procname (#strings STRINGS Readonly)
AS
..
To call the procedure
DECLARE #strings STRINGS
INSERT INTO #strings
(String1,String2)
SELECT 'String1','String2'
UNION ALL
SELECT 'String3','String4'
UNION ALL
SELECT 'String5','String6'
EXEC Usp_procname #strings
By this way you can pass more than one string in a single call. Make sure you update the logic inside the procedure to handle more than one string
In a case like this one, I usually concatenate strings and split them on the server side or pass even an xml if I have multiple columns. Sql it's very fast when processing xml. You can try all the methods and check the processing time and after that choose the best method.

How do you pass values for a parameter by position when you need to check multiple values?

I created a stored procedure (spBalanceRange) with 2 optional parameters. They've been set to a default value and the sp works fine when I pass only 1 value per parameter by position. However, I have a situation where I'm trying to pass, by position, two strings immediately followed by a wildcard. I want the user to be able to search for Vendor names that start with either 'C%' or 'F%'. Here's the gist of the CREATE PROC statement:
CREATE PROC spBalanceRange
#VendorVar varchar(40) = '%',
#BalanceMin money = 1.0
...
Here's what I've tried so far, but doesn't work:
EXEC spBalanceRange '(C%|F%)', 200.00;
EXEC spBalanceRange 'C%|F%', 200.00;
Is there a way to check for 2 or more string values with a wildcard when passed by position? Thanks.
EDIT: According to your comments you are looking for the first letter of a vendor's name only.
In this special case I could suggest an easy, not well performing but really simple approach. CHARINDEX returns a number greater than zero, if a character appears within a string. So you just have to pass in all your lookup-first-characters as a simple "chain":
DECLARE #DummyVendors TABLE(VendorName VARCHAR(100));
INSERT INTO #DummyVendors VALUES
('Camel Industries')
,('Fritz and Fox')
,('some other');
DECLARE #ListOfFirstLetters VARCHAR(100)='CF';
SELECT VendorName
FROM #DummyVendors AS dv
WHERE CHARINDEX(LEFT(dv.VendorName,1),#ListOfFirstLetters)>0
This was the former answer
Checking against more than one value needs either a dedicated list of compares
WHERE val=#prm1 OR val=#prm2 OR ... (you know the count before)
...or you use the IN-clause
WHERE LEFT(VenoderName,1) IN ('C','F', ...)
...but you cannot pass the IN-list with a parameter like ... IN(#allValues)
You might think about a created TYPE to pass in all your values like a table and use an INNER JOIN as filter: https://stackoverflow.com/a/337864/5089204 (and a lot of other examples there...)
Or you might think of dynamic SQL: https://stackoverflow.com/a/5192765/5089204
And last but not least you might think of one of the many split string approaches. This is one of my own answers, section "dynamic IN-statement": https://stackoverflow.com/a/33658220/5089204
I'm answering my own question, and maybe other solutions exist but here is what had to happen with my stored procedure in order to pass variables by position:
CREATE PROC spBalanceRange
#VendorVar varchar(40) = '%',
#BalanceMin money = 1.0
AS
IF (#VendorVar = '%' AND #BalanceMin IS NULL OR #BalanceMin = '')
BEGIN
PRINT 'BalanceMin cannot be null.';
END
IF (#VendorVar = % AND #BalanceMin IS NOT NULL)
BEGIN
(sql statement using parameters)
END
EXEC spBalanceRange '[C,F]%', 200.00;
That's what I know.

comparing input parameter with xml value using like in sql

I have an SQL table with a column which stores xml like this
<AdditionalInfo><RegistrantID>16279</RegistrantID></AdditionalInfo>
I have created a stored procedure like this:
CREATE PROC hr_GetJobStatusByRegistrantId
#registrantId VARCHAR
AS
BEGIN
SELECT TOP 1
[IsSubscribed]
FROM [Hrge].[dbo].[hr_Jobs]
where AdditionalInfo LIKE '%<AdditionalInfo><RegistrantID>%' + #registrantId + '%</RegistrantID></AdditionalInfo>%'
END
When I run this stored procedure, I get null:
exec hr_GetJobStatusByRegistrantId '16279'
If I make this parameter integer then I get convertion to int error.
Please suggest me solution to this.
(Just expanding the comment into an answer)
You should always specify the width of a char or a varchar field, because unless you do the default kicks in. The documentation says:
When n is not specified in a data definition or variable declaration
statement, the default length is 1. When n is not specified when using
the CAST and CONVERT functions, the default length is 30.
which means that in your case you have actually defined #registrantId as VARCHAR(1) so the value of '16279' was trimmed to a single character ('1') and you actually searched for
%<AdditionalInfo><RegistrantID>%1%</RegistrantID></AdditionalInfo>%
in the database. This actually returned the IsSubscribed flag for the first record it found in the DB that had a '1' anywhere in the RegistrantID field. You got lucky that the value was something wrong, so you noticed it.
Additionally you are using % around your parameter. This means that when you search for a RegistrantID of 123, you'll get results for 123, 1234, 2123, 51236, etc, etc, and then just take the first one, whichever that one is (decided by the database, since there is no order clause). It's my guess that you need an exact match, so you should remove those, and just use
'%<AdditionalInfo><RegistrantID>' + #registrantId
+ '</RegistrantID></AdditionalInfo>%'
Also, it the RegistrantId is actually a number, it would be nice if the interface of the procedure reflected that, so it could be defined with
#registrantId int
and then converted to a string in the query
'%<AdditionalInfo><RegistrantID>' + cast(#registrantId as varchar(10))
+ '</RegistrantID></AdditionalInfo>%'

Passing parameters from Stored Procedure to Function (inside Stored Procedure)

I have a problem and I have researched Stackoverflow for answers without any luck. I have manage to build a Stored Procedure that uses a function called GetSubtree_relvalue within it. The stored procedure and its function works just fine when the function has its parameters hard coded. But now I want the function to inherent the parameters that is sent in with the Stored procedure when it is executed.
When I replace XX and USA with #attribute_id and USA with #Client it doesn't return anything. I have debugged it so far so that I can conclude that the function doesn't get any values with it when it runs. This, even though these parameters in the Stored Procedure carries exactly the same value as the one that I have hard coded . (I checked with a regular select #client within the Procedure and it returns USA.)
Am I parametrizing the function wrongly? Do I need to initiate the functions parameter to be able to send it/pass it on to the function? How do get functions to inhere parameters?
I would be glad for all input regarding passing parameters forward from SP to functions.
Using SQL server 2008
Thanks
/Daniel
Function values hard coded
Insert into att_value_lookup (t.attribute_ID, t.att_value)
Select
t.attribute_ID, t.att_value
From
(Select attribute_id, att_value from relvalue
where attribute_id in (
Select attribute_id from (
select attribute_id
from dbo.GetSubtree_relvalue('XX','USA'))
) as t
Function values parameterized
Insert into att_value_lookup (t.attribute_ID, t.att_value)
Select
t.attribute_ID, t.att_value
From
(Select attribute_id, att_value from relvalue
where attribute_id in (
Select attribute_id from (
select attribute_id
from dbo.GetSubtree_relvalue('#attribute_id','#client'))
) as t
remove the quotes!!
dbo.GetSubtree_relvalue('#attribute_id','#client')
should be:
dbo.GetSubtree_relvalue(#attribute_id,#client)
you only need them when passing in literal string values like 'XX' and 'USA', not when passing in variables. You were actually passing in strings that contained the variable names: '#attribute_id', '#client' and not the values contained within the variables.
you can check it out:
DECLARE #x varchar(10)
SET #x='wow wee!!'
print '#x'
print #x
Remove the quotes from around your parameters. You're currently passing strings that happen to be the names of the parameters, but not passing the parameters themselves.
from dbo.GetSubtree_relvalue(#attribute_id,#client)