SQL Query Dynamically Create Multiple LIKE/OR Clause - sql

I am trying to create the answer
SELECT *
FROM table
WHERE column LIKE 'Text%'
OR column LIKE 'Hello%'
OR column LIKE 'That%'
in below link:
Combining "LIKE" and "IN" for SQL Server
The problem is, in my example the values in the answer 'Text', 'Hello' and 'That' are not hard coded, they are populated from an application multi-select control and can be NULL value or a comma-separated string like this :
DECLARE #String_With_Commas nvarchar(255);
SET #String_With_Commas = N'Mercedes,BMW,Audi,Tesla,Land Rover';
I have tried below code, but it didn't work :
DECLARE #SearchString = CONCAT('''',REPLACE(#String_With_Commas, N',', N'%'' OR column LIKE '''));
And use it like :
WHERE column LIKE #SearchString + '%' + ''''

Assuming you are using a fully supported version of SQL Server, a couple ideas:
JOIN to STRING_SPLIT:
SELECT *
FROM dbo.YourTable YT
JOIN STRING_SPLIT(#YourVariable,',') SS ON YT.YourColumn LIKE SS.[value] + '%';
This will, however, return multiple rows if there can be multiple matches.
Use an EXISTS:
SELECT *
FROM dbo.YourTable YT
WHERE EXISTS (SELECT 1
FROM STRING_SPLIT(#YourVariable,',') SS
WHERE YT.YourColumn LIKE SS.[value] + '%');
This won't return the same row twice, if there are multiple matches.
From the comments on this answer, the requirement that the parameter be NULLable was omitted in the question. I would therefore suggest you use the EXISTS solution:
SELECT *
FROM dbo.YourTable YT
WHERE EXISTS (SELECT 1
FROM STRING_SPLIT(#YourVariable,',') SS
WHERE YT.YourColumn LIKE SS.[value] + '%')
OR #YourVariable IS NULL
OPTION (RECOMPILE);

Related

SQL Search for Data in Multiple Columns

Dears,
I have a table as shown below as a sample, and I want to run one query by which i can find all the yellow highlighted ones by using %AAA%.
Instead of running the Where command on each column one by one, I can do one general find option and it will list all the rows.
Thank you in advance!!
You can include all the conditions in one where clause using or:
where col1 like '%aaa%' or
col2 like '%aaa%' or
. . . -- and so on for all the columns
Unpivot the columns and do a WHERE based on that:
select *
from Table
where exists (select 1
from (values (col1), (col2), (col3) ) AS v (allCols) -- etc
where v.allCols like '%aaa%'
);
If you can't be bothered to type them out, try this little query:
select STRING_AGG('(' + c.name + ')', ', ')
from sys.columns c
where c.object_id = OBJECT_ID('Name_Of_Table_Here');
If you are using sql server then you can write dynamic query to do so. Please try below query:
declare #sql as varchar(max);
select #sql = 'select * from [TableName] where '
+ stuff((
select ' or [' + [column_name] + '] like ''%AAA%'''
from information_schema.columns
where table_name = 'TableName'
for xml path('')
)
, 1, 5, ''
);
exec(#sql);
This query will return every row in which at least one column contains AAA.
If you are using PostgreSQL, you can use its JSON functionality:
select t.*
from the_table t
where exists (select *
from jsonb_each(to_jsonb(t)) as x(col,val)
where val like '%AAA%');
If you are using Postgres 12 or later you can use a SQL/JSON path expression:
select t.*
from the_table t
where to_jsonb(t) ## '$.* like_regex "AAA" flag "i"'

SQL Server : add 'Type' to every column in table

I have a table DemoTable in SQL Server. And it has these columns:
Column1, Column2, Column3
I want to query the table
select * from DemoTable
but in query results I want to concatenate Type_ to all the column names available in DemoTable.
So the result of this query should be showing columns
Type_Column1, Type_Column2, Type_Column3
Is there any function or any way to achieve this?
Note: there are N number of columns not only 3 just to rename only these manually.
If the problem is as you say:
After joining all the tables , there are many duplicate column names
then the typical solution is to NOT use *. So instead of this:
SELECT *
FROM A
JOIN B ON ...
JOIN C ON ...
... you should consider using a custom column set, which is the normal and recommended way to do this, as in the following example:
SELECT A.Column1, A.Column2, B.Column3, C.Column4, C.Column5
FROM A
JOIN B ON ...
JOIN C ON ...
Here's one way to automate your task using dynamic SQL:
use MY_DATABASE;
go
--here you specify all your parameters, names should be self-explanatory
declare #sql varchar(1000) = 'select ',
#tableName varchar(100) = 'DemoTable',
#prefix varchar(10) = 'Type_';
select #sql = #sql + name + ' as ' + #prefix + name + ',' from sys.columns
where object_name(object_id) = #tableName;
set #sql = left(#sql, len(#sql) - 1) + ' from ' + #tableName;
exec(#sql);
Some general remarks:
Naming your result set's columns dynamically will demand for dynamic SQL in any case. No way around...
Naming columns to carry extra information is - in most cases - a very bad idea.
the only way I know to deal with the asterisk in a SELECT * FROM ... and still get full control over the columns names and types is XML.
Try this:
SELECT TOP 10 *
FROM sys.objects
FOR XML RAW, ROOT('TableDef'),ELEMENTS, XMLSCHEMA,TYPE
This will return the 10 first rows of sys.objects. The result is an XML, where the rows follow an XML schema definition.
It is possible (but sure not the best in performance) to create a fully inlined query dynamically. The result will be an EAV list carrying everything you need.
WITH PrepareForXml(QueryAsXml) AS
(
SELECT
(
SELECT TOP 10 *
FROM sys.objects
FOR XML RAW, ROOT('TableDef'),ELEMENTS, XMLSCHEMA,TYPE
)
)
,AllRows AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) RowIndex
,rw.query('.') theRowXml
FROM PrepareForXml
CROSS APPLY QueryAsXml.nodes('TableDef/*:row') A(rw)
)
SELECT RowIndex
,B.ColumnName
,B.ColumnValue
,COALESCE(
(SELECT QueryAsXml.value('declare namespace xsd="http://www.w3.org/2001/XMLSchema";
(TableDef
/xsd:schema
/xsd:element
/xsd:complexType
/xsd:sequence
/xsd:element[#name=sql:column("ColumnName")]
/#type )[1]','nvarchar(max)')
FROM PrepareForXml)
,(SELECT QueryAsXml.value('declare namespace xsd="http://www.w3.org/2001/XMLSchema";
(TableDef
/xsd:schema
/xsd:element
/xsd:complexType
/xsd:sequence
/xsd:element[#name=sql:column("ColumnName")]
/xsd:simpleType
/xsd:restriction
/#base)[1]','nvarchar(max)')
FROM PrepareForXml)
) AS ColumnType
FROM AllRows
CROSS APPLY theRowXml.nodes('*:row/*') A(col)
CROSS APPLY (SELECT col.value('local-name(.)','nvarchar(max)') ColumnName
,col.value('(./text())[1]','nvarchar(max)') ColumnValue ) B;
This is the beginning of the result-set:
RowIndex ColumnName ColumnValue ColumnType
1 name sysrscols sqltypes:nvarchar
1 object_id 3 sqltypes:int
1 schema_id 4 sqltypes:int
[...many more...]
I don't know what you need actually, but it might be enough to export the XML as is. It's everything in there...
UPDATE: I did not read carefully enough...
You want to trick out the fact, that a result set's column names must be unique in order to continue with this...
The approach above will not solve this issue. Sorry.
I won't delete this immediately... Might be there are some hints you can get out of this...
You can use the following query to add 'Type' to every column in table:
SELECT Column1 AS Type_Column1, Column2 AS Type_Column2, Column3 AS Type_Column3
FROM DemoTable

MSSQL query search with keyword list

I got one table that contains all information about products. I need to list all articles with a matching keywords (in this case the brand name) in a specific column. Is it possible to initiate some kind of a 'list' with all brand names that I can use for this operation? chaning OR for all brands seems kinda bad.
In the second step I only need to see all articles that does not contain a specific word order before they keywords from the first step.
DECLARE #brand NVARCHAR =
'bmw, toyota, mercedes'
SELECT [Artikelnum]
FROM [dbo].[LAGER]
WHERE [XWebtexke] like '%' + #brand +'%' AND [XWebtexke] NOT LIKE '%suited for%'
GO
Thats what I got so far, but it does not work in the way I need it.
DECLARE #brand NVARCHAR =
'bmw, toyota, mercedes'
select * from (
SELECT [Artikelnum]
FROM [dbo].[LAGER]
WHERE [XWebtexke] NOT LIKE '%suited for%' )t4
WHERE [XWebtexke] like '%' + #brand +'%'
GO
DECLARE #brand NVARCHAR =
'bmw, toyota, mercedes'
select * from (
SELECT [Artikelnum]
FROM [dbo].[LAGER]
WHERE [XWebtexke] NOT LIKE '%suited for%' )t4
WHERE t4.[XWebtexke] like '%' + #brand +'%'
GO
Realized you need all the keywords to match. Here is the solution for that.
You need to split the brand variable, try this:
DECLARE #brand NVARCHAR(200) = 'bmw, toyota, mercedes'
;WITH CTE as
(
SELECT '%'+ t.c.value('.', 'VARCHAR(2000)')+'%' val
FROM (
SELECT x = CAST('<t>' +
REPLACE(#brand, ', ', '</t><t>') + '</t>' AS XML)
) a
CROSS APPLY x.nodes('/t') t(c)
)
SELECT [Artikelnum]
FROM [dbo].[LAGER]
WHERE
not exists(SELECT * FROM CTE WHERE [XWebtexke] not like val)
and [XWebtexke] NOT LIKE '%suited for%'
I am assuming there is always a space after the comma, you can adjust the code with ease if that is not always the case.
In sqlserver 2016 you can use STRING_SPLIT instead of the split used in my answer

using on clause of a left outer join with the LIKE keyword using a dynamic list

I have a column in a table that can have a string CSV, I want to run a query against all those values with a LIKE that is apart of a ON clause to a outer join.
DECLARE #to VARCHAR(max) = (SELECT value FROM dbo.table WHERE id = 'key');
DECLARE #t table ( value varchar(max) )
INSERT INTO #t SELECT item FROM fn_csv_splitstring ( #to , ',' )
After I have gotten all the CSV values, I now want to have each value in my temp table used as an expression on the LIKE keyword similar to the below select
SELECT * FROM
dbo.table e
where e.value LIKE '%' + (SELECT value FROM [#t]) + '%'
The exact statement is used on a left outer join statement, the ON clause links two table row IDs
e.id = t.id and then there is an additional AND expression AND e.value LIKE 'col%' At this point I need to be able to have all rows in my temp table as a bunch of OR LIKE '%' or something that acts as LIKE '%' + (SELECT value FROM [#t]) + '%'
I have tried the IN keyword, but IN seems to only work with exact matches and not the wild card.
Thanks in advance.
Use EXISTS to check whether e.value is like ANY t.value (wildcarded)
SELECT *
FROM dbo.table e
WHERE EXISTS (
SELECT *
FROM #t t
WHERE e.value LIKE '%' + t.value + '%')

Combination of 'LIKE' and 'IN' using t-sql

How can I do this kind of selection:
SELECT *
FROM Street
WHERE StreetName LIKE IN ('% Main Street', 'foo %')
Please don't tell me that I can use OR because these actually comes from a query.
There is no combined LIKE and IN syntax but you can use LIKE to JOIN onto your query as below.
;WITH Query(Result) As
(
SELECT '% Main Street' UNION ALL
SELECT 'foo %'
)
SELECT DISTINCT s.*
FROM Street s
JOIN Query q ON StreetName LIKE q.Result
Or to use your example in the comments
SELECT DISTINCT s.*
FROM Street s
JOIN CarStreets cs ON s.StreetName LIKE cs.name + '%'
WHERE cs.Streets = 'offroad'
You don't have a lot of choices here.
SELECT * FROM Street Where StreetName LIKE '% Main Street' OR StreetName LIKE 'foo %'
If this is part of an existing, more complicated query (which is the impression I'm getting), you could create a table value function that does the checking for you.
SELECT * FROM Street Where StreetName IN (dbo.FindStreetNameFunction('% Main Street|foo %'))
I'd recommend using the simplest solution (the first). If this is nested inside a larger, more complicated query, post it and we'll take a look.
I had a similar conundrum but due to only needing to match the start of a string, I changed my 'like' to SUBSTRING as such:
SELECT *
FROM codes
WHERE SUBSTRING(code, 1, 12) IN ('012316963429', '012315667849')
You can resort to Dynamic SQL and wrapping up all in a stored procedure.
If you get the LIKE IN param in a string as tokens with a certain separator, like
'% Main Street,foo %,Another%Street'
first you need to create a function that receives a list of LIKE "tokens" and returns a table of them.
CREATE FUNCTION [dbo].[SplitList]
(
#list nvarchar(MAX),
#delim nvarchar(5)
)
RETURNS #splitTable table
(
value nvarchar(50)
)
AS BEGIN
While (Charindex(#delim, #list)>0) Begin
Insert Into #splitTable (value)
Select ltrim(rtrim(Substring(#list, 1, Charindex(#delim, #list)-1)))
Set #list = Substring(#list, Charindex(#delim, #list)+len(#delim), len(#list))
End
Insert Into #splitTable (value) Select ltrim(rtrim(#list))
Return
END
Then in the SP you have the following code
declare
#sql nvarchar(MAX),
#subWhere nvarchar(MAX)
#params nvarchar(MAX)
-- prepare the where sub-clause to cover LIKE IN (...)
-- it will actually generate where sub clause StreetName Like option1 or StreetName Like option2 or ...
set #subWhere = STUFF(
(
--(**)
SELECT ' OR StreetName like ''' + value + '''' FROM SplitList('% Main Street,foo %,Another%Street', ',')
FOR XML PATH('')
), 1, 4, '')
-- create the dynamic SQL
set #sql ='select * from [Street]
where
(' + #subWhere + ')
-- and any additional query params here, if needed, like
AND StreetMinHouseNumber = #minHouseNumber
AND StreetNumberOfHouses between (#minNumberOfHouses and #maxNumberOfHouses)'
set #params = ' #minHouseNumber nvarchar(5),
#minNumberOfHouses int,
#minNumberOfHouses int'
EXECUTE sp_executesql #sql, #params,
#minHouseNumber,
#minNumberOfHouses,
#minNumberOfHouses
Of course, if you have your LIKE IN parameters in another table or you gather it through a query, you can replace that in line (**)
I believe I can clarify what he is looking for, but I don't know the answer. I'll use my situation to demonstrate. I have a table with a column called "Query" that holds SQL queries. These queries sometimes contain table names from one of my databases. I need to find all Query rows that contain table names from a particular database. So, I can use the following code to get the table names:
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
I'm trying to use a WHERE IN clause to identify the Query rows that contain the table names I'm interested in:
SELECT *
FROM [DatasourceQuery]
WHERE Query IN LIKE
(
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
)
I believe the OP is trying to do something like that.
This is my way:
First create a table function:
create function [splitDelimeter](#str nvarchar(max), #delimeter nvarchar(10)='*')
returns #r table(val nvarchar(max))
as
begin
declare #x nvarchar(max)=#str
set #x='<m>'+replace(#x, #delimeter, '</m><m>')+'</m>'
declare #xx xml=cast(#x as xml)
insert #r(val)
SELECT Tbl.Col.value('.', 'nvarchar(max)') id
FROM #xx.nodes('/m') Tbl(Col)
return
end
Then split the search text with your preference delimeter. After that you can do your select with left join as below:
declare #s nvarchar(max)='% Main Street*foo %'
select a.* from street a
left join gen.splitDelimeter(#s, '*') b
on a.streetname like b.val
where val is not null
What I did when solving a similar problem was:
SELECT DISTINCT S.*
FROM Street AS S
JOIN (SELECT value FROM String_Split('% Main Street,foo %', N',')) T
ON S.StreetName LIKE T.value;
Which is functionally similar to Martin's answer but a more direct answer to the question.
Note: DISTINCT is used because you might get multiple matches for a single row.