I am working on a query page where a user selects a value which represents different types, each identified by an ID.
The problem is selecting these IDs from the data base using the WHERE IN method.
This is my SQL statement
SELECT M.REG_NO, T.TYPE_ID
FROM MAIN AS M
INNER JOIN CLASSIFICATION AS C
ON M.REG_NO = C.REG_NO
INNER JOIN TYPE AS T
ON T.TYPE_ID = C.TYPE_ID
WHERE T.TYPE_ID IN (#Types)
it will work for one single value, eg. 46, but NOT if the value is in brackets, eg. (46) or ('46'), the way it should be for the IN.
I am using visual studio which is auto generating the method to access the table adapter to get the values so I think I HAVE to do this through SQL.
I am passing a string, eg. Types = "46,267,2010" , into the adapter method which passes the string into the #Types in the SQL statement.
Any help would be great. Thanks!
This is a pretty common problem -- not sure why TSQL hasn't dealt with it yet. Anyway, the solution I've found works best for me is to convert the variable to a table, and then you can use IN() on it just fine.
Starting with the function:
CREATE Function [dbo].[ParseStringList] (#StringArray nvarchar(max) )
Returns #tbl_string Table (ParsedString nvarchar(max)) As
BEGIN
DECLARE #end Int,
#start Int
SET #stringArray = #StringArray + ','
SET #start=1
SET #end=1
WHILE #end<Len(#StringArray)
BEGIN
SET #end = CharIndex(',', #StringArray, #end)
INSERT INTO #tbl_string
SELECT
Substring(#StringArray, #start, #end-#start)
SET #start=#end+1
SET #end = #end+1
END
RETURN
END
And then to use the function...
SELECT *
FROM table
WHERE SomeFieldValue In (Select ParsedString From dbo.ParseStringList(#Types))
It's because you need to have a set of values separated by commas. Like
WHERE
T.TYPE_ID IN ('46', '45', '44');
Are you usign SQL Server or Oracle, MySQL?
If you have a string composed of your multiple values such as
T.TYPE_ID IN ('46,45,44');
You might need a function that will explode your string usign a delimiter (split or whatever it is called depending on you DBMS)
The following Select working fine for me:
SELECT M.REG_NO, T.TYPE_ID
FROM MAIN AS M
INNER JOIN CLASSIFICATION AS C
ON M.REG_NO = C.REG_NO
INNER JOIN TYPE AS T
ON T.TYPE_ID = C.TYPE_ID
WHERE (','+#Types+',') LIKE '%,' +T.TYPE_ID+ ',%'
Related
I am looking to get an order number from a column named KEY_Ref, this ref column have various contents, but some rows look like this
LINE_NO=15^ORDER_NO=176572^RELEASE_NO=1^
Now I am interested in getting the value for ORDER_NO (176572 in this case)
How would I (In SQL Server) go about getting this (Or other) value from the main string
The logic is always
key1=value1^key2=value2^key3=value3^
You can use string_split():
select t.*, s.orderno
from t outer apply
(select stuff(s.value, 1, 9, '') as orderno
from string_split(t.key_ref, '^') s
where s.value like 'ORDER_NO=%'
) s;
Here is a db<>fiddle.
this is going to be a bit lengthy answer however if your SQL server version doesn't support string_split function you may use this.
declare #str varchar(100) = 'LINE_NO=15^ORDER_NO=176572^RELEASE_NO=1^'
declare #substr1 varchar(50) = substring(#str,charindex('^',#str)+1,len(#str))
declare #substr2 varchar(50) = substring(#substr1,charindex('=',#substr1)+1,charindex('^',#substr1)-charindex('=',#substr1)-1)
select #substr2 as 'order number'
the final variable will produce the desired value and you must merge the above queries to a single query that can fetch the value from the table in a single select statement.
this will work only if the pattern doesn't deviate from the one you've mentioned.
Editing as requested.
I was able to get the data from a long string into a table so the question has changed to a degree.
My question is how do I get results returned based on a match of ALL values passed via a comma-delimited string?
I can, and have, used STRING_SPLIT. The problem I have is when using string split with IN or INNER JOIN or OUTER APPLY or CROSS APPLY, a row is returned if any one (01)value in the passed string of values matches the value(s) in the column of the row; NOT ALL of the values in the string.
Again, the important point is that the rows returned need to match ON ALL values from the STRING_SPLIT of the values passed in the character string.
Also, as previously requested, here is a table example, a mocked-up piece of code and the expected results:
Code:
DECLARE #VALUES NVARCHAR(100) = 'X,G'
SELECT T5.COL1, T5.COL2, STRING_AGG(TRIM(TABLE1.COL3),', ') AS UNIQUE_STRING_OF_VALUES
FROM [SOME_DB].[dbo].[TABLE1] AS T5
INNER JOIN STRING_SPLIT(#VALUES, ',') AS T10 ON T10.VALUE = T5.COL3
GROUP BY T5.COL1, T5.COL2
ORDER BY T5.COL1 ASC, T5.COL2 ASC
You can achieve this by using a built-in function that splits the strings:
declare
#String varchar(1000) = 'This,is,a,test,search,function'
, #SplitCharacter varchar(10) = ','
select
*
from string_split(#String,#SplitCharacter)
This splits the strings in this format:
Now when you alter the script like this, you can search for particular fields in the splits:
declare
#String varchar(1000) = 'This,is,a,test,search,function'
, #SplitCharacter varchar(10) = ','
select
*
from string_split(#String,#SplitCharacter)
where Value like '%sea%'
This gives you the following results:
See DEMO
Let me know if this works...
How can I create a function like this?
function FN_something (#entrada char(50))
declare #consulta table
declare #notificacao varchar(50)
declare #multa float
declare #saida varchar(50)
set #consulta as = (select num_notificacao,num_multa from table where field = #entrada)
set #notificacao = #consulta.num_notificacao
set #multa = #consulta.num_multa
set #saida = "resultado: "+ #notificacao +";"+#multa
return #saida
Thanks in advance
I would not use a function... Scalar functions tend to be a real performance killer. Try to use something like this inline
SELECT 'resultado: '
+ ISNULL(CAST(t.num_notificacao AS VARCHAR(MAX)),'???')
+ ';'
+ ISNULL(CAST(t.num_multa AS VARCHAR(MAX)),'???')
FROM SomeTable AS t WHERE t.SomeField=#entrada;
If you need a function it was much better to use an inlined TVF (syntax without BEGIN...END and bind it into your query with CROSS APPLY.
Might be simplified:
If your columns are NOT NULL you can go without ISNULL()-function. If your columns are strings, you can do without CAST()... My code is defensive proramming :-D
Hint
If this is something you need more often, you might introduce a VIEW carrying this calculated column and use it instead of your table. You might include this value into your table as computed column as well...
UPDATE
Great, the VIEW you show in the comment is an inline TVF actually, which is very good!
My magic crystall ball tells me, that you might need something like this:
SELECT cl.*
,'resultado: ' + t.num_notificacao + ';' + t.num_multa AS CalculatedResult
FROM dbo.[CampoLivre876]('SomeParameter') AS cl
LEFT JOIN SomeOtherTable AS t ON cl.entrada=t.SomeField --should be only one related row per main row!
This will call the iTFV and join it to the other Table, where the two columns are living. I Assume, that the CampoLivre876-row knows its entrada key.
Hint 2:
If this works for you, you might include this approach directly into your existing iTVF.
UPDATE 2
You might try to change your function like here:
ALTER FUNCTION [dbo].[CampoLivre876] ()
RETURNS TABLE
RETURN
Select cl.mul_numero_notificacao + ';' + CAST(cl.mul_valor_multa as varchar(max)) AS ExistingColumn
,'resultado: ' + t.num_notificacao + ';' + CAST(t.num_multa AS varchar(max)) AS CalculatedResult
From Campo_Livre AS cl With(NoLock)
INNER JOIN SomeOtherTable AS t ON cl.entrada=t.SomeField;
This should read all lines in one go. Reading 1 row after the other is - in almost all cases - something really, really bad...
Here is an example of a function with correct SQL Server syntax:
create function FN_something (
#entrada char(50) -- should probably be `varchar(50)` rather than `char(50)`
) returns varchar(50)
begin
declare #saida varchar(50);
select #saida = 'resultado: ' + num_notificacao + ';' + num_multa
from table
where field = #entrada;
return #saida;
end;
Note: This assumes that the num_ columns are strings, not numbers. If they are numbers, you need to convert them or use concat().
EDIT:
A function really isn't appropriate for this. Probably the best solution is a computed column:
alter table t add something as (concat('resultado: ', num_notificacao, ';', num_multa);
Then you can get the value directly from the table. In earlier versions of SQL Server, you would use a view rather than computed column.
I have two tables tbl_Products and tbl_Brands, both are joined on BrandId.
I have a stored procedure which should return all the products belong to the brand ids passed to it as parameter.
My code is as follows.
create proc Sp_ReturnPrdoucts
#BrandIds varchar(500) = '6,7,8'
AS
BEGIN
SELECT *
FROM tbl_Products as p
JOIN tbl_Brands b ON p.ProductBrandId = b.BrandId
WHERE b.BrandId IN (#BrandIds)
END
But this is giving error as BrandId is INT and #BrandIds is VARCHAR
When I hard code it this way as follows it works fine and returns the desired data from db ..
create proc Sp_ReturnPrdoucts
#BrandIds varchar(500) = '6,7,8'
AS
BEGIN
SELECT *
FROM tbl_Products AS p
JOIN tbl_Brands b ON p.ProductBrandId = b.BrandId
WHERE b.BrandId IN (6,7,8)
END
Any help :)
If possible, don't use varchar for this kind of things, use a table valued parameter instead.
To use a tabled value parameter you should first declare a user defined table type:
CREATE TYPE IntList As Table
(
IntValue int
)
Then change your stored procedure to accept this variable instead of the nvarchar:
create proc Sp_ReturnPrdoucts
#BrandIds dbo.IntList readonly -- Note: readonly is a must!
AS
BEGIN
SELECT *
FROM tbl_Products as p
join tbl_Brands b on p.ProductBrandId=b.BrandId
join #BrandIds ON(b.BrandId = IntValue)
END
The problem is that the IN() operator expects a list of variables separated by commas, while you provide a single variable that it's value is a comma separated string.
If you can't use a table valued parameter, you can use a string spliting function in sql to convert the value of the varchar to a table of ints. there are many splitters out there, I would recommend reading this article before picking one.
Another alternative is to use 'indirection' (as I've always called it)
You can then do..
create proc Sp_ReturnPrdoucts
#BrandIds varchar(500) = '6,7,8'
AS
BEGIN
if (isnumeric(replace(#BrandIds,',',''))=1)
begin
exec('SELECT * FROM tbl_Products as p join tbl_Brands b on p.ProductBrandId=b.BrandId WHERE b.BrandId IN ('+#BrandIds+')')
end
END
This way the select statement is built as a string, then executed.
I've now added validation to ensure that the string being passed in is purely numeric (after removing all the commas)
OK I have a question to which an answer might be simple but im not sure how to do it:
Question:
How do i use IN with LIKE?
Why Not Duplicate:
Well I know if i have multiple strings i can use OR to check. But this is not the case with what i am trying to do.
Question Explaination:
I have an SP with a parameter #path, now i would like to send multiple paths, separated by delimiter, (to avoid calling sp multiple times). I split the string using a custom function which returns a table with splited values.
Now how would i go about using the values from that splited values table to be used with LIKE operator.
What I have done so far:
declare
#path varchar(max) = 'CompanyRules/Billing/IntegrationServices|CompanyRules/Reports/IntegrationServices',
#default_code varchar(max) = '1'
declare #tempTable TABLE(path varchar(max))
INSERT INTO #tempTable (path)
SELECT split from fn_splitby(#path, '|')
select prg.path, prg.default_code, prmd.optional_property_1, prmd.optional_property_2, prmd.optional_property_3, prmd.optional_property_4, prmd.optional_property_5, prmd.optional_property_6
from pdm_rule_group prg, pdi_rule_master prmd
where prg.path = prmd.path
AND prg.path in (select path from #tempTable)
AND prg.default_code != #default_code
The this will not yield any result.
Possible Solution:
What i though was that i have to loop through the #tempTable and then create separate strings to be used with LIKE. Which im sure is a bad solution, and may have some other solution to it.
Replace this statement
AND prg.path in (select path from #tempTable)
with
AND EXISTS (select 1 from #tempTable where prg.path like "%"+path+"%" )
Just join to the table:
select prg.path, prg.default_code, prmd.optional_property_1, prmd.optional_property_2, prmd.optional_property_3, prmd.optional_property_4, prmd.optional_property_5, prmd.optional_property_6
from
pdm_rule_group prg
inner join
pdi_rule_master prmd
on prg.path = prmd.path
inner join
#tempTable tt
on
prg.path like tt.path
where
prg.default_code != #default_code