How to create a 'Like' + 'In' query in SQL Server (SSMS) - sql-server

I'm looking a way to create a query that includes the functionality of both: 'Like' and 'In' keywords together. So with this I'll be able to make a search of multiple sub-strings in one sentence instead of creating multiple consults:
i.e:
Instead of this:
select * from MyTable where ColumnName like'%substring1%'
select * from MyTable where ColumnName like'%substring2%'
select * from MyTable where ColumnName like '%substring3%'
I want to do something like this:
select * from MyTable where ColumnName in ('%substring1%','%substring2%','%substring3%')
Just to clarify: the wild card (%) is not allowed or it doesn't work together with the "in" keyword so it forces me to look for full strings instead of substrings.
Any idea or approach?

The only approach I can think of is the following:
select * from MyTable where ColumnName like '%substring1%' or ColumnName like '%substring2%' or ColumnName like '%substring3%'
If you have additional conditions, just group it.
select * from MyTable where (ColumnName like '%substring1%' or ColumnName like '%substring2%' or ColumnName like '%substring3%') and AnotherColumn='string'

Pre-populate a temp table with the terms you want to search (or table variable, CTE, or whatever). Then join but use like instead of an equalty operator.
select *
from myTable a
inner join #terms b
on a.myField like '%' + b.term + '%'

Yet another option is pass a delimited string
Example
Declare #Search varchar(max) = 'John,Cappelletti,Some Other Value'
Select Distinct A.*
From MyTable A
Join (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace(#Search,',','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
) B on charindex(RetVal,ColumnName)>0

Why not just use or operator;
SELECT ColumnName
FROM MyTable
WHERE ColumnName LIKE '%substring1%' OR ColumnName LIKE '%substring2%' OR ColumnName LIKE '%substring3%'

Related

Create a User defined function like SQL server 2017 STRING_AGG on earlier versions

I try to create a generic function that can be used like this example of using the new string_agg built-in function on SQL Server 2017
the inside implementation can be something like the follow
with tbl as(
select a.Id, c.Desc
from TableA a
join TableB b on b.aId = a.Id
join TableC c on c.Code = b.bCode
)
select distinct ID
, STUFF(( select ', ' + Desc from tbl t where t.ID = tbl.ID
for xml path(''),TYPE).value('.','VARCHAR(MAX)'),1,2,'') Desc
from tbl
But how to receives the field key, the field to be connected, the separator char, and the scoped select context?
Is it related to Inline or Multi-Statement Table-Valued Functions ?
Well, this is an ugly hack, I have to go and wash my hands now, but it works (in a way :-D)
CREATE FUNCTION dbo.MyStringAgg(#SelectForXmlAuto XML,#Delimiter NVARCHAR(10))
RETURNS NVARCHAR(MAX)
AS
BEGIN
RETURN STUFF((
SELECT #Delimiter + A.nd.value(N'(#*)[1]',N'nvarchar(max)')
FROM #SelectForXmlAuto.nodes(N'/*') AS A(nd)
FOR XML PATH(''),TYPE
).value(N'.',N'nvarchar(max)'),1,LEN(#Delimiter),'');
END
GO
DECLARE #tbl TABLE(GroupId INT,SomeValue NVARCHAR(10));
INSERT INTO #tbl VALUES(1,'A1'),(1,'A2'),(2,'B1'),(3,'C1'),(3,'C2'),(3,'C3');
SELECT GroupId
,dbo.MyStringAgg((SELECT SomeValue
FROM #tbl AS t2
WHERE t2.GroupId=t.GroupId
FOR XML AUTO), N', ')
FROM #tbl AS t
GROUP BY GroupId;
GO
DROP FUNCTION dbo.MyStringAgg;
The result
1 A1, A2
2 B1
3 C1, C2, C3
The parameter is a FOR XML sub-select within paranthesis. This will implicitly pass the sub-selects result as an XML into the function.
To be honest: I would not use this myself...
A query like this
SELECT GroupId
,STUFF((SELECT N', ' + SomeValue
FROM #tbl AS t2
WHERE t2.GroupId=t.GroupId
FOR XML PATH,TYPE).value(N'.','nvarchar(max)'),1,2,'')
FROM #tbl AS t
GROUP BY GroupId;
produces the same result and is almost the same amount of typing - but should be faster then calling a slow UDF...
Ok.. so with the first comment of #MichaƂTurczyn I run into this Microsoft article about CLR User-Defined Aggregate - Invoking Functions
Once I compile the code into SrAggFunc.dll, I was trying to register the aggregate in SQL Server as follows:
CREATE ASSEMBLY [STR_AGG] FROM 'C:\tmp\STR_AGG.dll';
GO
But I got the following error.
Msg 6501, Level 16, State 7, Line 1
CREATE ASSEMBLY failed because it could not open the physical file 'C:\tmp\SrAggFunc.dll': 3(The system cannot find the path specified.).
So I used this excellant part of #SanderRijken code and then change the command to
CREATE ASSEMBLY [STR_AGG]
FROM 0x4D5A90000300000004000000FF......000; --from GetHexString function
GO
and then,
CREATE AGGREGATE [STR_AGG] (#input nvarchar(200)) RETURNS nvarchar(max)
EXTERNAL NAME [STR_AGG].C_STRING_AGG;`
Now it's done.
You can see it under your Database -> Programmability on SSMS
and used like :
SELECT a.Id, [dbo].[STR_AGG](c.Desc) cDesc
FROM TableA a
JOIN TableB b on b.aId = a.Id
JOIN TableC c on c.Code = b.bCode
GROUP BY a.Id
Thanks all =)

Select rows with any member of list of substrings in string

In a Micrososft SQL Server table I have a column with a string.
Example:
'Servernamexyz.server.operationunit.otherstuff.icouldnt.predict.domain.domain2.domain3'
I also have a dynamic list of substrings
Example:
('icouldnt', 'stuff', 'banana')
I don't care for string manipulation. The substrings could also be called:
('%icouldnt%', '%stuff%', '%banana%')
What's the best way to find all rows where the string contains one of the substrings?
Solutions that are not possible:
multiple OR Statements in the WHERE clause, the list is dynamic
external Code to do a "for each", its a multi value parameter from the reportbuilder, so nothing useful here
changing the database, its the database of a tool a costumer is using and we can't change it, even if we would like... so much
I really cant believe how hard such a simple problem can turn out. It would need a "LIKE IN" command to do it in a way that looks ok. Right now I cant think of anything but a messy temp table.
One option is to use CHARINDEX
DECLARE #tab TABLE (Col1 NVARCHAR(200))
INSERT INTO #tab (Col1)
VALUES (N'Servernamexyz.server.operationunit.otherstuff.icouldnt.predict.domain.domain2.domain3' )
;WITH cteX
AS(
SELECT 'icouldnt' Strings
UNION ALL
SELECT 'stuff'
UNION ALL
SELECT 'banana'
)
SELECT
T.*, X.Strings
FROM #tab T
CROSS APPLY (SELECT X.Strings FROM cteX X) X
WHERE CHARINDEX(X.Strings, T.Col1) > 1
Output
EDIT - using an unknown dynamic string variable - #substrings
DECLARE #tab TABLE (Col1 NVARCHAR(200))
INSERT INTO #tab (Col1)
VALUES (N'Servernamexyz.server.operationunit.otherstuff.icouldnt.predict.domain.domain2.domain3' )
DECLARE #substrings NVARCHAR(200) = 'icouldnt,stuff,banana'
SELECT
T.*, X.Strings
FROM #tab T
CROSS APPLY
( --dynamically split the string
SELECT Strings = y.i.value('(./text())[1]', 'nvarchar(4000)')
FROM
(
SELECT x = CONVERT(XML, '<i>'
+ REPLACE(#substrings, ',', '</i><i>')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i)
) X
WHERE CHARINDEX(X.Strings, T.Col1) > 1

SQL Subqueries concatenation issue

I have a simple subquery that works fine
SELECT id, Name, subset
, (select count (1) from anotherTable where qid = someTable.id )
FROM someTable
the value "subset" is a string ie:
"and (PreQ1 like '%A%') and ( PreQ2 like '%A%' or PreQ2 like '%C%')"
And so I'd like to ConCatenate the subquery with the value subset like this:
SELECT id, Name, subset
, exec ( 'select count (1) from anotherTable where qid = someTable.id ' + subset)
FROM someTable
.. but get a "Conversion failed" error
Is this what you want?
SELECT s.id, s.Name, s.subset,
(select cast(count(1) as varchar(255)) + s.subset
from anotherTable a
where a.qid = s.id
)
FROM someTable s;
This does the concatenation within the subquery. You can also put the + s.subset outside the subquery.
Also note that table aliases make the query easier to write and to read.

LIKE operator with a variable number of search conditions, in T-SQL

Is there a short way to look for multiple matches with the LIKE operator, using AND clauses?
I should do it dynamically, with a variable number of terms. Obtaining it statically (with a fixed number of terms) is a no-brainer:
SELECT *
from MyTable
WHERE MyColumn LIKE "%AAA%"
AND MyColumn LIKE "%BBB%"
AND MyColumn LIKE "%CCC%"
Let's assume there is a table variable that contains an unknown number of terms:
DECLARE #Terms table
(
Term nvarchar(500)
)
Is there a way to perform the LIKE statement on MyColumn matching all the items in #Terms?
DECLARE #Terms TABLE
(
WildCards VARCHAR(20)
)
INSERT INTO #Terms
( WildCards )
VALUES
( 'CO' ),
( 'DO' ),
( 'EO' )
DECLARE #FoundAll INT
SELECT #FoundAll = Count(*) FROM #Terms
SELECT mt.MyColumn, COUNT(*), #FoundAll FROM MyTable mt
OUTER APPLY
(
SELECT WildCards FROM #Terms
) d
WHERE mt.MyColumn LIKE ('%' + d.WildCards + '%')
GROUP BY mt.MyColumn
HAVING COUNT(*) = #FoundAll
This will only pull the record which matches ALL of the possible likes.
SQL Fiddle:
select mt.*
from MyTable mt
join #Terms t
on mt.MyColumn like '%' + t.Term + '%'
group by mt.MyColumn
having COUNT(*) = (select COUNT(*) from #Terms)
If you can get away with not using the LIKE then the following would work:
SELECT *
FROM MyTable
WHERE MyColumn IN
(
SELECT Term
FROM Terms
)
Or, how about the following (SQL Fiddle):
SELECT *
FROM MyTable
INNER JOIN Terms
ON MyTable.MyColumn LIKE '%' + Terms.Term + '%'
Try this:
SELECT MT.*
FROM MyTable AS MT INNER JOIN
#Terms AS TR ON CHARINDEX(TR.Term, MT.MyColumn) > 0
GROUP BY MT.MyColumn
HAVING COUNT(*) = (SELECT COUNT(*) FROM Terms)

Is there a way to get the fields names of a table created in a function / stored procedure?

when I need the columns of an existing table I use the query:
SELECT c.[name]
FROM
(SELECT * from syscolumns) c
INNER JOIN
(SELECT [id] from sysobjects where name= 'tableName') o on c.[id]=o.[id]
I need the fields of a table that I create during runTime:
select
a.ID,
b.lName,
b.fName
into #T
from
a
inner join
b on a.id=b.id
.
select * from #T_columns
will result a table with 3 rows:
id
lName
fName
How can I do it?
Thanks
When you create a temp table, it will be in tempdb. You can look it up like this:
SELECT COLUMN_NAME
FROM tempdb.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME LIKE '#T|_%' ESCAPE '|'
If you do a SELECT * FROM INFORMATION_SCHEMA.TABLES in tempdb, you'll see the temp table name you use (#T) actually has a number of underscores appended to it followed by a unique identifier. So you won't find it it you just search where table_name = '#T'.
So that's why you have to use a LIKE as I've shown above. This will match on "#T_" followed by any other other characters.
Try this
SELECT sc.NAME
FROM
tempdb..SYSOBJECTS so JOIN
tempdb..SYSCOLUMNS sc ON sc.id = so.id
WHERE so.NAME LIKE '#T%'

Resources