I have Product table and has values for category path as "2210>2215>2219" and so on. Where 2210, 2215, 2219 are category Ids from Category table.
Few of the categories are inactive in database which is maintained at Category table.
To find products with inactive category in category path, I wish to write a query.
I wish to do something like this:
select p.* from product p, category c
where c.categoryId in (split(p.categoryPath,'>'));
which should basically, split category path by '>' and return me few strings to be passed in 'in'.
Is there any way where I can split this and get separate strings to be passed in in clause for above query?
Try this
create a function
CREATE FUNCTION [dbo].[fn_Split](#text varchar(8000), #delimiter varchar(20))
RETURNS #Strings TABLE
(
position int IDENTITY PRIMARY KEY,
value varchar(8000)
)
AS
BEGIN
DECLARE #index int
SET #index = -1
WHILE (LEN(#text) > 0)
BEGIN
SET #index = CHARINDEX(#delimiter , #text)
IF (#index = 0) AND (LEN(#text) > 0)
BEGIN
INSERT INTO #Strings VALUES (#text)
BREAK
END
IF (#index > 1)
BEGIN
INSERT INTO #Strings VALUES (LEFT(#text, #index - 1))
SET #text = RIGHT(#text, (LEN(#text) - #index))
END
ELSE
SET #text = RIGHT(#text, (LEN(#text) - #index))
END
RETURN
END
Sql query would be
select p.* from product p, category c
where c.categoryId in (select value from fn_split(p.categoryPath,'>'));
Related
I have a string value which has numeric values separated by comma and then by a pipe. I want to split them into a table with two columns. I could split the string by one delimiter but unfortunately couldn't find a way to split by two. Please help.
DECLARE #list NVARCHAR(MAX) = '1,101|2,202|3,303';
The result should be like below.
1 101
2 202
3 303
Thanks in advance.
If you're using SQL Server 2016 or Azure, you have access to the new SPLIT_STRING function. If not I recommend using Jeff Moden's DelimitedSplit8K function, which is widely regarded as the fastest, most efficient SQL based string splitter available...
DECLARE #list NVARCHAR(MAX) = '1,101|2,202|3,303';
SELECT
Col1 = LEFT(dsk.Item, sl.SplitLocation - 1),
Col2 = SUBSTRING(dsk.Item, sl.SplitLocation + 1, LEN(dsk.Item))
FROM
dbo.DelimitedSplit8K(#list, '|') dsk -- code for DelimitedSplit8K can be found here... http://www.sqlservercentral.com/articles/Tally+Table/72993/
CROSS APPLY ( VALUES (ISNULL(NULLIF(CHARINDEX(',', dsk.Item, 1), 0), 1)) ) sl (SplitLocation);
CREATE FUNCTION [dbo].[fn_Split_char](#text nvarchar(max), #delimiter varchar(20) = ' ')
RETURNS #Strings TABLE
(
position int IDENTITY PRIMARY KEY,
value nvarchar(max)
)
AS
BEGIN
DECLARE #index int
SET #index = -1
WHILE (LEN(#text) > 0)
BEGIN
SET #index = CHARINDEX(#delimiter , #text)
IF (#index = 0) AND (LEN(#text) > 0)
BEGIN
INSERT INTO #Strings VALUES (#text)
BREAK
END
IF (#index > 1)
BEGIN
INSERT INTO #Strings VALUES (LEFT(#text, #index - 1))
SET #text = RIGHT(#text, (LEN(#text) - #index))
END
ELSE
SET #text = RIGHT(#text, (LEN(#text) - #index))
END
RETURN
END
Select LEFT(value, Charindex(',', value) - 1) ,
RIGHT(value, Charindex(',', Reverse(value)) - 1) ,
* from [fn_Split_char] ('1,101|2,202|3,303', '|')
Use xml path and cross apply to create multiple rows for a single row based on the pipe separator and then use substring w.r.t the commas to derive two desired columns
Create table #temp(list nvarchar(max))
Insert into #temp values('1,101|2,202|3,303')
SELECT
Substring(Tbl.Col.value('./text()[1]','varchar(50)'),1,1)as col1,
Substring(Tbl.Col.value('./text()[1]','varchar(50)'),charindex(',',Tbl.Col.value('./text()[1]','varchar(50)'),1)+1,len(Tbl.Col.value('./text()[1]','varchar(50)')))
FROM
(Select cast('<a>'+ replace((SELECT list As [*] FOR XML PATH ('')), '|', '</a><a>') + '</a>' as xml)as t
from #temp) tl
Cross apply
tl.t.nodes('/a') AS Tbl(Col)
Try using this Table-valued Function, embed this SP to your main SP
ALTER FUNCTION [dbo].[delimiter]
(
#PARAM_IDS AS VARCHAR(MAX)
#PARAM_DELIMITER AS CHAR(1)
)
RETURNS
#NEW_TABLE TABLE
(
NUM INT NOT NULL IDENTITY,
ID INT NOT NULL
)
AS
BEGIN
DECLARE #NEXTSTRING AS NVARCHAR(MAX);
DECLARE #POS AS INT;
DECLARE #STRING AS NVARCHAR(MAX);
DECLARE #DELIMITER AS NVARCHAR(MAX);
SET #STRING = #PARAM_IDS;
SET #DELIMITER = #PARAM_DELIMITER;
SET #STRING = #STRING + #DELIMITER;
SET #POS = CHARINDEX(#DELIMITER,#STRING);
WHILE (#POS <> 0)
BEGIN
SET #NEXTSTRING = SUBSTRING(#STRING,1,#POS - 1);
INSERT #NEW_TABLE (ID) VALUES (#NEXTSTRING);
SET #STRING = SUBSTRING(#STRING,#POS+1,len(#STRING));
SET #POS = CHARINDEX(#DELIMITER,#STRING);
END
RETURN
END
then example of use
SET #DETAILS_COUNT = (SELECT COUNT(*) FROM delimiter(#PARAM_MS_UNIT_ID, #DELIMITER));
my data is :
Id Case ID#
1. QCCR1A105369, QCCR1A104986 ,QCCR1A103717
2. QCIM1A106902,4613410733 QCIM1A106902; 4613344523 QCIM1A105842; 4614004212 QCIM1A106580; 4614060189 QCIM1A106676
3. QCCR1D93616, QCCR1D92488, QCCR1D58461
4. QCCR1B40216 .... ,QCCR1B39080, QCCR1B40216, QCCR1B39745, QCCR1B38463 , QCCR1B38618, QCCR1B38619, QCCR1B38620, QCCR1B38621, QCCR1B38622, QCCR1B38465, QCCR1B38623
5. QCCR2A30221 QCCR2A30223 QCCR2A30222 QCIM2A30416
My output will be Id 1,3,4,5. I want only that rows, which have starting value QC not any numeric value. For ID 2 you can see there are some numeric values, please tell me how can I achieve it.
You could use a table valued function to split your value by a delimiter like this:
CREATE FUNCTION [dbo].[Split]
(
#ItemList NVARCHAR(MAX),
#delimiter CHAR(1)
)
RETURNS #ItemTable TABLE (Item VARCHAR(250))
AS
BEGIN
DECLARE #tempItemList NVARCHAR(MAX)
SET #tempItemList = #ItemList
DECLARE #i INT
DECLARE #Item NVARCHAR(4000)
SET #i = CHARINDEX(#delimiter, #tempItemList)
WHILE (LEN(#tempItemList) > 0)
BEGIN
IF #i = 0
SET #Item = #tempItemList
ELSE
SET #Item = LEFT(#tempItemList, #i - 1)
INSERT INTO #ItemTable(Item) VALUES(#Item)
IF #i = 0
SET #tempItemList = ''
ELSE
SET #tempItemList = RIGHT(#tempItemList, LEN(#tempItemList) - #i)
SET #i = CHARINDEX(#delimiter, #tempItemList)
END
RETURN
END
Then this query returns the expected result:
SELECT t.*
FROM dbo.TableName t
WHERE NOT EXISTS
(
SELECT 1 FROM dbo.Split(t.CaseID, ',') split
WHERE NOT LEFT(LTRIM(split.Item), 2) = 'QC'
OR ISNUMERIC(split.Item) = 1
)
Demo with your sample data. But in general you should avoid multiple values in one column. Instead store it in separate rows in a linked table. That's called database normalization.
I have multi-valued parameter in my Report named #Animal which has ('Cat', 'Dog', 'Mouse').
inside dataset i need to get 'Cat', Dog', 'Mouse' and store it into #AnimalName table variable.
"Hard-Coded" way would be:
DECLARE #AnimalName TABLE (Name nvarchar (10))
INSERT INTO #AnimalName SELECT ('Cat');
INSERT INTO #AnimalName SELECT ('Dog');
INSERT INTO #AnimalName SELECT ('Mouse');
I know that I can use #Animal directly inside my dataset, the reason I'm doing this is because I'm trying to improve my report's performance. Many multi-valued parameters will make the report runs forever.
Does any one know how(the syntax) to get #Animal data values and stored it into a table variables #AnimalName inside dataset?
Thanks heaps!
Pass the comma delimited string into your stored procedure and in your stored proc use a table valued function to convert you multi-valued parameter into a table.
CREATE PROC GetAllAnimals
#AnimalList nvarchar(max)
AS
DECLARE #Animals TABLE (Animal nvarchar(10))
INSERT INTO #Animals SELECT * FROM dbo.fnGetValueListFromMultiSelect(#AnimalList)
and then use the #Animals table to inner join in your query
Functions declared below.
For Integer (or ID) values
CREATE FUNCTION [dbo].[fnGetIdListFromMultiSelect](#String nvarchar(MAX))
RETURNS #Results TABLE ([Id] int)
AS
BEGIN
DECLARE #Delimiter CHAR(1)
DECLARE #INDEX INT
DECLARE #SLICE nvarchar(4000)
IF #String IS NULL RETURN
SET #Delimiter = ','
SET #INDEX = 1
WHILE #INDEX !=0
BEGIN
-- GET THE INDEX OF THE FIRST OCCURENCE OF THE SPLIT CHARACTER
SELECT #INDEX = CHARINDEX(#Delimiter,#STRING)
-- NOW PUSH EVERYTHING TO THE LEFT OF IT INTO THE SLICE VARIABLE
IF #INDEX !=0
BEGIN
SELECT #SLICE = LEFT(#STRING,#INDEX - 1)
-- CHOP THE ITEM REMOVED OFF THE MAIN STRING
SELECT #STRING = RIGHT(#STRING,LEN(#STRING) - #INDEX)
END
ELSE
SELECT #SLICE = #STRING
-- PUT THE ITEM INTO THE RESULTS SET
INSERT INTO #Results([Id]) VALUES(CAST(#SLICE AS INT))
-- BREAK OUT IF WE ARE DONE
IF LEN(#STRING) = 0 BREAK
END
RETURN
END
For string values
CREATE FUNCTION [dbo].[fnGetValueListFromMultiSelect](#String nvarchar(MAX))
RETURNS #Results TABLE ([Item] nvarchar(128) Primary Key)
AS
BEGIN
DECLARE #Delimiter CHAR(1)
DECLARE #INDEX INT
DECLARE #SLICE nvarchar(4000)
SET #Delimiter = ','
SET #INDEX = 1
WHILE #INDEX !=0
BEGIN
-- GET THE INDEX OF THE FIRST OCCURENCE OF THE SPLIT CHARACTER
SELECT #INDEX = CHARINDEX(#Delimiter,#STRING)
-- NOW PUSH EVERYTHING TO THE LEFT OF IT INTO THE SLICE VARIABLE
IF #INDEX !=0
BEGIN
SELECT #SLICE = LEFT(#STRING,#INDEX - 1)
-- CHOP THE ITEM REMOVED OFF THE MAIN STRING
SELECT #STRING = RIGHT(#STRING,LEN(#STRING) - #INDEX)
END
ELSE
SELECT #SLICE = #STRING
-- PUT THE ITEM INTO THE RESULTS SET
INSERT INTO #Results([Item]) VALUES(#SLICE)
-- BREAK OUT IF WE ARE DONE
IF LEN(#STRING) = 0 BREAK
END
RETURN
END
The user selects various words from a drop down list and these values get added into a comma delimited string. When passing the string to a stored procedure I want it to select * from a table where that word exists.
Table
id----word
1-----cat
2-----dog
3-----mouse
4-----dog
string that is passed into the stored procedure is cat, dog so returning columns 1, 2 and 4.
Is there a way of doing this in sql server?
Use IN:
SELECT *
FROM your_table
WHERE word IN ('cat', 'dog')
you first need to make a function SplitCSV :
CREATE FUNCTION [dbo].[SplitCSV] (#CSVString VARCHAR(8000), #Delimiter CHAR(1))
RETURNS #temptable TABLE (items VARCHAR(8000))
AS
BEGIN
DECLARE #pos INT;
DECLARE #slice VARCHAR(8000);
SELECT #pos = 1;
IF LEN(#CSVString) < 1 OR #CSVString IS NULL RETURN;
WHILE #pos!= 0
BEGIN
SET #pos = CHARINDEX(#Delimiter,#CSVString);
IF #pos != 0
SET #slice = LEFT(#CSVString, #pos - 1);
ELSE
SET #slice = #CSVString;
IF( LEN(#slice) > 0)
INSERT INTO #temptable(Items) VALUES (#slice);
SET #CSVString = RIGHT(#CSVString, LEN(#CSVString) - #pos);
IF LEN(#CSVString) = 0 BREAK;
END
RETURN
END
GO
then you can use it like :
SELECT *
FROM myTable
WHERE ID IN (
SELECT items FROM [dbo].[SplitCSV]('1,2,3,4,5', ',')
)
SELECT *
FROM Table
WHERE '%,' + Word + ',%' LIKE ',' + #your_csv_param + ','
Extra commas at the begin and end of parameter and column are to prevent search to match cat with catfish for example.
If you want select all animal except mouse , you can use NOT IN
SELECT * FROM
TABLE
WHERE Word Not IN('Mouse')
So you can avoid type many type of animal
CREATE FUNCTION
ALTER FUNCTION [dbo].[fn_Split](#text varchar(8000), #delimiter varchar(20) = ' ')
RETURNS #Strings TABLE
(
position int IDENTITY PRIMARY KEY,
value varchar(8000)
)
AS
BEGIN
DECLARE #index int
SET #index = -1
WHILE (LEN(#text) > 0)
BEGIN
SET #index = CHARINDEX(#delimiter , #text)
IF (#index = 0) AND (LEN(#text) > 0)
BEGIN
INSERT INTO #Strings VALUES (#text)
BREAK
END
IF (#index > 1)
BEGIN
INSERT INTO #Strings VALUES (LEFT(#text, #index - 1))
SET #text = RIGHT(#text, (LEN(#text) - #index))
END
ELSE
SET #text = RIGHT(#text, (LEN(#text) - #index))
END
RETURN
END
----
select * from yourtable where column in ( select value from fn_Split(#para1,',')
I trying to use list of numbers like this.
SELECT *
FROM users
WHERE id in (list_of_ids)
this is part of sql procedure and the variable 'list_of_ids' is varchar and it contains id's like this: 1,2,3,4,5........
How can i use this list and this query
Try this as well. This could be the better solution as it wont require any additional function to create.
oracle regexp_substr will split comma seperated values into different rows and passes to query.
SELECT *
FROM users
WHERE id in
(SELECT regexp_substr(list_of_ids,'[^,]+', 1, level) FROM dual
connect by regexp_substr(list_of_ids, '[^,]+', 1, level) is not NULL)
You can use a ref cursor to construct the sql query, as in this pl/sql block:
declare
list_of_ids varchar2(100) := '1,3,4'; -- example
c_cursor sys_refcursor;
result number;
begin
open c_cursor for ( 'SELECT id '||
'FROM users '||
'WHERE id in ('||list_of_ids||')'
);
fetch c_cursor into result;
while c_cursor%found
loop
dbms_output.put_line('ID='||to_char(result));
fetch c_cursor into result;
end loop;
close c_cursor;
end;
/
Try this solution in your project.
Add a new user function that returns a table.
Code is below:
CREATE FUNCTION [dbo].[fn_Split](#text varchar(8000), #delimiter
varchar(20) = ' ')
RETURNS #Strings TABLE
(
position int IDENTITY PRIMARY KEY,
value varchar(8000)
)
AS
BEGIN
DECLARE #index int
SET #index = -1
WHILE (LEN(#text) > 0)
BEGIN
SET #index = CHARINDEX(#delimiter , #text)
IF (#index = 0) AND (LEN(#text) > 0)
BEGIN
INSERT INTO #Strings VALUES (#text)
BREAK
END
IF (#index > 1)
BEGIN
INSERT INTO #Strings VALUES (LEFT(#text, #index - 1))
SET #text = RIGHT(#text, (LEN(#text) - #index))
END
ELSE
SET #text = RIGHT(#text, (LEN(#text) - #index))
END
RETURN
END
And then call it from your stored procedure like below.
DECLARE #list_of_ids AS VARCHAR(100)
SET #list_of_ids = '1,2,3,4,5,6,7,8,9,10,'
SELECT *
FROM users
WHERE id in (SELECT value FROM dbo.fn_Split(#list_of_ids,','))