How can I pass a string space delimited to a stored procedure and filter the result?
I'm trying to do this
parameter value
__________________
#query key1 key2 key 3
Then in the stored procedure, I want to first
find all the results with key1.
filter step 1 with key2.
filter step2 with key3.
Another example:
col1 | col2 | col3
------------+-----------------------+-----------------------------------
hello xyz | abc is my last name | and I'm a developer
hello xyz | null | and I'm a developer
If I search for any following it should return for each?
"xyz developer" returns 2 rows
"xyz abc" returns 1 row
"abc developer"returns 1 row
"hello" returns 2 rows
"hello developer" returns 2 rows
"xyz" returns 2 rows
I'm using SQL Server 2016. I tried to use split_string to split query string. But I don't know how to pass this to the stored procedure.
Thanks in advance
Full Text Index is the way to go, but this will return your results.
One caveat (that I can think of). If your search expression/pattern contains a column name, that will generate a false-positive
Declare #YourTable table (col1 varchar(50),col2 varchar(50),col3 varchar(50))
Insert Into #YourTable values
('hello xyz','abc is my last name','and I''m a developer'),
('hello xyz', null ,'and I''m a developer')
Declare #Search varchar(max) = 'xyz abc'
Select A.*
From #YourTable A
Cross Apply (Select FullString=(Select A.* FOR XML Raw)) B
Where FullString like '%'+replace(#Search,' ','%')+'%'
Returns
col1 col2 col3
hello xyz abc is my last name and I'm a developer
EDIT - Multi-Word / Any Order Search
Try this not fully tested. I can't imagine this being very efficient especially with larger tables and numerous key words
Declare #YourTable table (col1 varchar(50),col2 varchar(50),col3 varchar(50))
Insert Into #YourTable values
('hello xyz','abc is my last name','and I''m a developer'),
('hello xyz', null ,'and I''m a developer')
Declare #Search varchar(max) = 'developer xyz'
Select *
From (
Select Distinct A.*
,Hits = sum(sign(charindex(C.Value,B.FullString))) over (partition by B.FullString)
,Req = C.Req
From #YourTable A
Cross Apply (Select FullString=(Select A.* FOR XML Raw)) B
Join (Select *,Req=sum(1) over () From String_Split(#Search,' ') ) C on charindex(C.Value,B.FullString)>0
) A
Where Hits=Req
http://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=c77123a71c810716b36d73a92ac714eb
Related
I want to have an argument in a function that determines which column from a table to use in the function. The table looks like below, but could in the future have more than two different sets/columns of limits.
Is this possible? Could I use the varchar argument like LimitsTable.#MyVarcharArg? (I know that syntax wouldn't work)
Here is something that I was alluding to.
As I mentioned in the comments, this can get expensive
Example
Declare #YourTable Table ([ID] int,[Col1] decimal(10,2),[Col2] decimal(10,2),[Col3] decimal(10,2))
Insert Into #YourTable Values
(1,1.0,null,null)
,(2,0.05,0.10,.15)
,(3,0.05,0.10,.15)
Select A.ID
,B.*
From #YourTable A
Cross Apply [dbo].[tvf-JSON-GetCol](concat('Col',ID),(Select A.* For JSON Path,Without_Array_Wrapper ) ) B
** Not Clear on the Column Selection Criteria, so I simply concat 'Col' and ID
Results
ID Value
1 1.00
2 0.10
3 0.15
The TVF if Desired
CREATE FUNCTION [dbo].[tvf-JSON-GetCol](#col varchar(150),#json varchar(max))
Returns Table
As Return
Select Value = JSON_VALUE(#json,'$.'+#col)
EDIT - Just for Fun, it could be as simple as:
Select A.ID
,Value = JSON_VALUE((Select A.* For JSON Path,Without_Array_Wrapper ),'$.'+concat('Col',ID))
From #YourTable A
2016 requires a literal key name while 2017+ can be dynamic.
My table in DB has a column which stores values in following format.
1234#2345#6780
Four digit numbers are stored using delimiter "#".
Due to a data corruption, there are some records with five digit numbers. There may be one or more than one five digit numbers in a given row.
1234#12345#67895
I'm trying to write a script to get only those corrupted records But cannot find a way to split and check values.
Any help is appreciated.
I'm using SQL server 12.0 version
you can use this function to split values:
CREATE FUNCTION [dbo].[fnSplit]
(#sInputList VARCHAR(8000) -- List of delimited items
, #sDelimiter VARCHAR(8000) = '#' -- delimiter that separates items
)
RETURNS #List TABLE (item VARCHAR(8000))
BEGIN
DECLARE #sItem VARCHAR(8000)
WHILE CHARINDEX(#sDelimiter,#sInputList,0) <> 0
BEGIN
SELECT
#sItem=RTRIM(LTRIM(SUBSTRING(#sInputList,1,CHARINDEX(#sDelimiter,#sInputList,0)-1))),
#sInputList=RTRIM(LTRIM(SUBSTRING(#sInputList,CHARINDEX(#sDelimiter,#sInputList,0)+LEN(#sDelimiter),LEN(#sInputList))))
IF LEN(#sItem) > 0
INSERT INTO #List SELECT #sItem as item
END
IF LEN(#sInputList) > 0
INSERT INTO #List SELECT #sInputList as item -- Put the last item in
RETURN
END
and then ask about the result
You can use XML nodes to split the string before 2016 version.
Create table Xmltest(ID int, numbers nvarchar(max))
insert into Xmltest values (1, '1234#12345#67895')
select ID, N.value('.', 'varchar(255)') as xmlValue
from (
select ID ,
cast(('<w>' + replace(numbers,'#','</w><w>') + '</w>') as xml) as xmlValue
from Xmltest
) as z
cross apply xmlValue.nodes ('//w') as split(N)
Output you get, I added this ID column to identify which row may have more than 4 Characters.
ID xmlValue
1 1234
1 12345
1 67895
To check where you have more than 4 characters you can do:
select ID, N.value('.', 'varchar(255)') as xmlValue
from (
select ID ,
cast(('<w>' + replace(numbers,'#','</w><w>') + '</w>') as xml) as xmlValue
from Xmltest
) as z
cross apply xmlValue.nodes ('//w') as split(N)
where len(N.value('.', 'varchar(255)')) > 4
Output you get:
ID xmlValue
1 12345
1 67895
You can use this. it returns any numbers row which length greater than 4.
SELECT * FROM SampleData
WHERE data LIKE '%[0-9][0-9][0-9][0-9][0-9]%'
this will work patindex is orcale's equivalent of regexp_like():
select * from table_name where not PATINDEX ('^[0-9]{4}(#){1}[0-9]{4}(#){1}[0-9]
{4}$',col_name) !=0;
for SQL Server (starting with 2016)
you can use the built in function of SQL to split a string.
sample:
DECLARE #Text VARCHAR(100) = '1234#12345#67895'
SELECT * FROM STRING_SPLIT(#Text,'#')
result:
value
----
123
4456
78902
you can now easily manipulate the values after
I am string splitting some values that are comma delimited into rows.
However some values have an extra comma on the end.
Example
Userid | Value
1 | A,B,C,D,
2 | F,H
Code
select value
from string_split('A,B,C,D,',',')
Current Output
UserId | Value
1 | A
1 | B
1 | C
1 | D
1 |
Is there any way to make the string split function ignore the final comma if no data follows it?
Desired Output
UserId | Value
1 | A
1 | B
1 | C
1 | D
Using MSSQL
Just add "WHERE" sentence like this:
select value
from string_split('A,B,C,D,',',')
WHERE value <> ''
STRING_SPLIT Function doesn't support for lower version of sql server so first create a function to split the given string and join the function with your select query.Here is below sample for your expected result.
Created User defined Function
CREATE FUNCTION [dbo].[Udf_StringSplit]
(
#Userid INT,
#Value VARCHAR(1000)
)
RETURNS #Result TABLE(
Userid INT,
Value VARCHAR(10)
)
AS BEGIN
DECLARE #Data AS TABLE
(
Userid INT,
Value VARCHAR(100)
)
INSERT INTO #Data(Userid,Value)
SELECT #Userid, #Value
INSERT INTO #Result(Userid,Value)
SELECT Userid,
Split.a.value('.','nvarchar(1000)') AS Value
FROM
(
SELECT Userid,
CAST('<S>'+REPLACE(#Value,',','</S><S>')+'</S>' AS XML) Value
FROM #Data
) AS A
CROSS APPLY Value.nodes('S') AS Split(a)
WHERE Userid=#Userid AND Split.a.value('.','nvarchar(1000)') <>''
RETURN
END
GO
Sample data table
DECLARE #Data AS TABLE(Userid INT , Value VARCHAR(100))
INSERT INTO #Data
SELECT 1,'A,B,C,D,' UNION ALL
SELECT 2,'F,H'
Sql script to get the expected result
SELECT d.Userid,
f.Value
FROM #Data d
CROSS APPLY [dbo].[Udf_StringSplit] (d.Userid,d.Value) AS f
WHERE d.Userid=1
GO
Result
Userid Value
------------
1 A
1 B
1 C
1 D
I have the following T-SQL script:
declare #Name nvarchar
declare data cursor for
select Name from MyDB.dbo.MyTable;
OPEN data;
-- Perform the first fetch.
FETCH NEXT FROM data;
-- Check ##FETCH_STATUS to see if there are any more rows to fetch.
WHILE ##FETCH_STATUS = 0
BEGIN
-- This is executed as long as the previous fetch succeeds.
FETCH NEXT FROM data INTO #Name;
Print 'Name: ' + #Name
END
CLOSE data;
DEALLOCATE data;
GO
I want to make a script that will compare each of the strings in a first column with each of the strings in the second column.
The problem is, I don't know how to loop through each of the rows and take a separate string value.
The code above prints only the first value in the query result.
What am I doing wrong?
To compare all values from one column to all values in another column you don't need a cursor, a simple join will do the work - since you didn't provide sample data and also not desired results, I had to make my own:
Create and populate sample table (Please save us this step in your future questions)
CREATE TABLE MyTable
(
Id int identity(1,1),
Name1 char(3),
Name2 char(3)
)
INSERT INTO MyTable (Name1, Name2) VALUES
('abc','def'),('zyx','abc'),
('ghi','jkl'),('yza','ghi'),
('mno','pqr'),('nml','mno'),('pqr','qpo'),
('stu','vwx'),('wvu','tsr'),('kji','hgf')
The query:
SELECT T1.Id, T1.Name1, T1.Name2, T2.Id, T2.Name1, T2.Name2
FROM MyTable T1
JOIN MyTable T2 ON T1.Name1 = T2.Name2
Result:
Id Name1 Name2 Id Name1 Name2
1 abc def 2 zyx abc
3 ghi jkl 4 yza ghi
5 mno pqr 6 nml mno
7 pqr qpo 5 mno pqr
You probably don't want to use a Cursor.
Are your columns in the same table? If so this is as simple as this;
-- Show All rows with [DIFFERENT] Name and Name2 fields
SELECT
Name,
Name2
FROM [MyDB].[dbo].[MyTable]
WHERE
Name <> Name2
-- Show All rows with [SAME] Name and Name2 fields
SELECT
Name,
Name2
FROM [MyDB].[dbo].[MyTable]
WHERE
Name = Name2
If not you will need to post the table definitions and names of columns to get a more concrete example
I want to split each name for individual columns
create table split_test(value integer,Allnames varchar(40))
insert into split_test values(1,'Vinoth,Kumar,Raja,Manoj,Jamal,Bala');
select * from split_test;
Value Allnames
-------------------
1 Vinoth,Kumar,Raja,Manoj,Jamal,Bala
Expected output
values N1 N2 N3 N4 N5 N6 N7.......N20
1 Vinoth Kumar Raja Manoj Jamal Bala
using this example you can get an idea.
declare #str varchar(max)
set #str = 'Hello world'
declare #separator varchar(max)
set #separator = ' '
declare #Splited table(id int identity(1,1), item varchar(max))
set #str = REPLACE(#str,#separator,'''),(''')
set #str = 'select * from (values('''+#str+''')) as V(A)'
insert into #Splited
exec(#str)
select * from #Splited
Here is an sql statement using recursive CTE to split names into rows, then pivot rows into columns.
SqlFiddle
with names as
(select
value,
1 as name_id,
substring(Allnames,1,charindex(',',Allnames+',', 0)-1) as name,
substring(Allnames,charindex(',',Allnames, 0)+1, 40) as left_names
from split_test
union all
select
value,
name_id +1,
case when charindex(',',left_names, 0)> 0 then
substring(left_names,1,charindex(',',left_names, 0)-1)
else left_names end as name,
case when charindex(',',left_names, 0)> 0 then
substring(left_names,charindex(',',left_names, 0)+1, 40)
else '' end as left_names
from names
where ltrim(left_names)<>'')
select value,
[1],[2],[3],[4],[5],[6],[7],[8],[9]
from (select value,name_id,name from names) as t1
PIVOT (MAX(name) FOR name_id IN ( [1],[2],[3],[4],[5],[6],[7],[8],[9] ) ) AS t2
UPDATE
#KM.'s answer might be a better way to split data into rows without recursive CTE table. It should be more efficient than this one. So I follow that example and simplified the part of null value process logic. Here is the result:
Step 1:
Create a table includes all numbers from 1 to a number grater than max length of Allnames column.
CREATE TABLE Numbers( Number int not null primary key);
with n as
(select 1 as num
union all
select num +1
from n
where num<100)
insert into numbers
select num from n;
Step 2:
Join data of split_test table with numbers table, we can get all the parts start from ,.
Then take the first part between 2 , form every row. If there are null values exists, add them with union.
select value ,
ltrim(rtrim(substring(allnames,number+1,charindex(',',substring(allnames,number,40),2)-2))) as name
from
(select value, ','+allnames+',' as allnames
from split_test) as t1
left join numbers
on number<= len(allnames)
where substring(allnames,number,1)=','
and substring(allnames,number,40)<>','
union
select value, Allnames
from split_test
where Allnames is null
Step 3: Pivot names from rows to columns like my first attempt above, omitted here.
SQLFiddle