Select with IN and Like - sql

I have a very interesting problem. I have an SSRS report with a multiple select drop down.
The drop down allows to select more than one value, or all values.
All values is not the problem.
The problem is 1 or the combination of more than 1 option
When I select in the drop down 'AAA' it should return 3 values: 'AAA','AAA 1','AAA 2'
Right now is only returning 1 value.
QUESTION:
How can make the IN statement work like a LIKE?
The Drop down select
SELECT '(All)' AS team, '(All)' AS Descr
UNION ALL
SELECT 'AAA' , 'AAA'
UNION ALL
SELECT 'BBB' , 'BBB'
Table Mytable
ColumnA Varchar(5)
Values for ColumnA
'AAA'
'AAA 1'
'AAA 2'
'BBB'
'BBB 1'
'BBB 2'
SELECT * FROM Mytable
WHERE ColumnA IN (SELECT * FROM SplitListString(#Team, ',')))
Split function
CREATE FUNCTION [dbo].[SplitListString]
(#InputString NVARCHAR(max), #SplitChar CHAR(1))
RETURNS #ValuesList TABLE
(
param NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #ListValue NVARCHAR(max)
DECLARE #TmpString NVARCHAR(max)
DECLARE #PosSeparator INT
DECLARE #EndValues BIT
SET #TmpString = LTRIM(RTRIM(#InputString));
SET #EndValues = 0
WHILE (#EndValues = 0) BEGIN
SET #PosSeparator = CHARINDEX(#SplitChar, #TmpString)
IF (#PosSeparator) > 1 BEGIN
SELECT #ListValue = LTRIM(RTRIM(SUBSTRING(#TmpString, 1, #PosSeparator -1 )))
END
ELSE BEGIN
SELECT #ListValue = LTRIM(RTRIM(#TmpString))
SET #EndValues = 1
END
IF LEN(#ListValue) > 0 BEGIN
INSERT INTO #ValuesList
SELECT #ListValue
END
SET #TmpString = LTRIM(RTRIM(SUBSTRING(#TmpString, #PosSeparator + 1, LEN(#TmpString) - #PosSeparator)))
END
RETURN
END

You can't. But, you can make the like work like the like:
select *
from mytable t join
SplitListString(#Team, ',') s
on t.ColumnA like '%'+s.param+'%'
That is, move the split list to an explicit join. Replace with the actual column name returned by the function, and use the like function.
Or, if you prefer:
select *
from mytable t cross join
SplitListString(#Team, ',') s
where t.ColumnA like '%'+s.param+'%'
The two versions are equivalent and should produce the same execution plan.

Better approach would be to have a TeamsTable (teamID, teamName, ...) and teamMembersTable (teamMemberID, teamID, teamMemberDetails, ...).
Then you an build your dropdown list as
SELECT ... FROM TeamsTable ...;
and
SELECT ... FROM teamMembersTable WHERE teamID IN (valueFromYourDropDown);
Or you can just store your teamID or teamName (or both) in your (equivalent of) teamMembersTable

You're not going to get IN to work the same as LIKE without a lot of work. You could do something like this though (and it would be nice to see some of your actual data though so we could give better solutions):
SELECT *
FROM table
WHERE LEFT(field,3) IN #Parameter
If you'd like better performance, create a code field on your table and update it like this:
UPDATE table
SET codeField = LEFT(field,3)
Then just add an index on that field and run this query to get your results:
SELECT *
FROM table
WHERE codeField IN #Parameter

Related

Finding row until condition is met

This is my table:
PackingNr
SerienNr
PN185971
PN185972
PN185972
PN185974
PN185974
PN185978
PN185978
R005478
PN185968
R000547
PN185725
R004987
As an input I get PackingNr and I need to select SerienNr which is like Rxxxxx not PNxxxxx.
So for example, if I have input PN175971, I need to get SerienNr = R005478.
How can I do this inside of select query? I tried CASE but this won't work as I don't know how many times I have to go again.
My select query is selecting also other columns from different tables.
SELECT
... ,
CASE
WHEN PSPD.SerienNr LIKE '%PN%'
THEN
(SELECT SerienNr FROM PSAPacking_Det
WHERE PSAPacking_Det.PackingNr = PSPD.SerienNr)
ELSE PSPD.SerienNr
END AS SerienNr
...
FROM
PSAPacking PSPD
JOIN
...
WHERE
PSPD.PackingNr = 'PN185971'
You need a recursive CTE. Something like this:
WITH cte AS (
SELECT t.PackingNr, t.SerienNr
FROM YourTable t
WHERE t..PackingNr = 'YourValueHere'
UNION ALL
SELECT t.PackingNr, t.SerienNr
FROM YourTable t
JOIN cte ON cte.SerienNr = t.PackingNr
)
SELECT TOP (1)
*
FROM cte
WHERE cte.SerienNr LIKE 'R%';
try this
declare #var2 varchar(max) = 'PN185971' --PUT YOUR INPUT HERE
declare #var1 varchar(max);
while(#var2 not like 'R%')
begin
set #var1 = #var2
set #var2 = (select max(SerienNr) from PSAPacking where PackingNr = #var1)
end
select #var2
if it doesn't find the value starting with R it returns null

How to match an array of substrings to db [duplicate]

In SQL I (sadly) often have to use "LIKE" conditions due to databases that violate nearly every rule of normalization. I can't change that right now. But that's irrelevant to the question.
Further, I often use conditions like WHERE something in (1,1,2,3,5,8,13,21) for better readability and flexibility of my SQL statements.
Is there any possible way to combine these two things without writing complicated sub-selects?
I want something as easy as WHERE something LIKE ('bla%', '%foo%', 'batz%') instead of this:
WHERE something LIKE 'bla%'
OR something LIKE '%foo%'
OR something LIKE 'batz%'
I'm working with SQl Server and Oracle here but I'm interested if this is possible in any RDBMS at all.
There is no combination of LIKE & IN in SQL, much less in TSQL (SQL Server) or PLSQL (Oracle). Part of the reason for that is because Full Text Search (FTS) is the recommended alternative.
Both Oracle and SQL Server FTS implementations support the CONTAINS keyword, but the syntax is still slightly different:
Oracle:
WHERE CONTAINS(t.something, 'bla OR foo OR batz', 1) > 0
SQL Server:
WHERE CONTAINS(t.something, '"bla*" OR "foo*" OR "batz*"')
The column you are querying must be full-text indexed.
Reference:
Building Full-Text Search Applications with Oracle Text
Understanding SQL Server Full-Text
If you want to make your statement easily readable, then you can use REGEXP_LIKE (available from Oracle version 10 onwards).
An example table:
SQL> create table mytable (something)
2 as
3 select 'blabla' from dual union all
4 select 'notbla' from dual union all
5 select 'ofooof' from dual union all
6 select 'ofofof' from dual union all
7 select 'batzzz' from dual
8 /
Table created.
The original syntax:
SQL> select something
2 from mytable
3 where something like 'bla%'
4 or something like '%foo%'
5 or something like 'batz%'
6 /
SOMETH
------
blabla
ofooof
batzzz
3 rows selected.
And a simple looking query with REGEXP_LIKE
SQL> select something
2 from mytable
3 where regexp_like (something,'^bla|foo|^batz')
4 /
SOMETH
------
blabla
ofooof
batzzz
3 rows selected.
BUT ...
I would not recommend it myself due to the not-so-good performance. I'd stick with the several LIKE predicates. So the examples were just for fun.
you're stuck with the
WHERE something LIKE 'bla%'
OR something LIKE '%foo%'
OR something LIKE 'batz%'
unless you populate a temp table (include the wild cards in with the data) and join like this:
FROM YourTable y
INNER JOIN YourTempTable t On y.something LIKE t.something
try it out (using SQL Server syntax):
declare #x table (x varchar(10))
declare #y table (y varchar(10))
insert #x values ('abcdefg')
insert #x values ('abc')
insert #x values ('mnop')
insert #y values ('%abc%')
insert #y values ('%b%')
select distinct *
FROM #x x
WHERE x.x LIKE '%abc%'
or x.x LIKE '%b%'
select distinct x.*
FROM #x x
INNER JOIN #y y On x.x LIKE y.y
OUTPUT:
x
----------
abcdefg
abc
(2 row(s) affected)
x
----------
abc
abcdefg
(2 row(s) affected)
With PostgreSQL there is the ANY or ALL form:
WHERE col LIKE ANY( subselect )
or
WHERE col LIKE ALL( subselect )
where the subselect returns exactly one column of data.
Another solution, should work on any RDBMS:
WHERE EXISTS (SELECT 1
FROM (SELECT 'bla%' pattern FROM dual UNION ALL
SELECT '%foo%' FROM dual UNION ALL
SELECT 'batz%' FROM dual)
WHERE something LIKE pattern)
The inner select can be replaced by another source of patterns like a table (or a view) in this way:
WHERE EXISTS (SELECT 1
FROM table_of_patterns t
WHERE something LIKE t.pattern)
table_of_patterns should contain at least a column pattern, and can be populated like this:
INSERT INTO table_of_patterns(pattern) VALUES ('bla%');
INSERT INTO table_of_patterns(pattern) VALUES ('%foo%');
INSERT INTO table_of_patterns(pattern) VALUES ('batz%');
I would suggest using a TableValue user function if you'd like to encapsulate the Inner Join or temp table techniques shown above. This would allow it to read a bit more clearly.
After using the split function defined at: http://www.logiclabz.com/sql-server/split-function-in-sql-server-to-break-comma-separated-strings-into-table.aspx
we can write the following based on a table I created called "Fish" (int id, varchar(50) Name)
SELECT Fish.* from Fish
JOIN dbo.Split('%ass,%e%',',') as Splits
on Name like Splits.items //items is the name of the output column from the split function.
Outputs
1 Bass
2 Pike
7 Angler
8 Walleye
I'm working with SQl Server and Oracle here but I'm interested if this is possible in any RDBMS at all.
Teradata supports LIKE ALL/ANY syntax:
ALL every string in the list.
ANY any string in the list.
┌──────────────────────────────┬────────────────────────────────────┐
│ THIS expression … │ IS equivalent to this expression … │
├──────────────────────────────┼────────────────────────────────────┤
│ x LIKE ALL ('A%','%B','%C%') │ x LIKE 'A%' │
│ │ AND x LIKE '%B' │
│ │ AND x LIKE '%C%' │
│ │ │
│ x LIKE ANY ('A%','%B','%C%') │ x LIKE 'A%' │
│ │ OR x LIKE '%B' │
│ │ OR x LIKE '%C%' │
└──────────────────────────────┴────────────────────────────────────┘
EDIT:
jOOQ version 3.12.0 supports that syntax:
Add synthetic [NOT] LIKE ANY and [NOT] LIKE ALL operators
A lot of times, SQL users would like to be able to combine LIKE and IN predicates, as in:
SELECT *
FROM customer
WHERE last_name [ NOT ] LIKE ANY ('A%', 'E%') [ ESCAPE '!' ]
The workaround is to manually expand the predicate to the equivalent
SELECT *
FROM customer
WHERE last_name LIKE 'A%'
OR last_name LIKE 'E%'
jOOQ could support such a synthetic predicate out of the box.
PostgreSQL LIKE/ILIKE ANY (ARRAY[]):
SELECT *
FROM t
WHERE c LIKE ANY (ARRAY['A%', '%B']);
SELECT *
FROM t
WHERE c LIKE ANY ('{"Do%", "%at"}');
db<>fiddle demo
Snowflake also supports LIKE ANY/LIKE ALL matching:
LIKE ANY/ALL
Allows case-sensitive matching of strings based on comparison with one or more patterns.
<subject> LIKE ANY (<pattern1> [, <pattern2> ... ] ) [ ESCAPE <escape_char> ]
Example:
SELECT *
FROM like_example
WHERE subject LIKE ANY ('%Jo%oe%','T%e')
-- WHERE subject LIKE ALL ('%Jo%oe%','J%e')
Use an inner join instead:
SELECT ...
FROM SomeTable
JOIN
(SELECT 'bla%' AS Pattern
UNION ALL SELECT '%foo%'
UNION ALL SELECT 'batz%'
UNION ALL SELECT 'abc'
) AS Patterns
ON SomeTable.SomeColumn LIKE Patterns.Pattern
One approach would be to store the conditions in a temp table (or table variable in SQL Server) and join to that like this:
SELECT t.SomeField
FROM YourTable t
JOIN #TempTableWithConditions c ON t.something LIKE c.ConditionValue
I have a simple solution, that works in postgresql at least, using like any followed by the list of regex. Here is an example, looking at identifying some antibiotics in a list:
select *
from database.table
where lower(drug_name) like any ('{%cillin%,%cyclin%,%xacin%,%mycine%,%cephal%}')
u can even try this
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
Query
select * from my_table inner join (select value from fn_split('ABC,MOP',','))
as split_table on my_table.column_name like '%'+split_table.value+'%';
Starting with 2016, SQL Server includes a STRING_SPLIT function. I'm using SQL Server v17.4 and I got this to work for me:
DECLARE #dashboard nvarchar(50)
SET #dashboard = 'P1%,P7%'
SELECT * from Project p
JOIN STRING_SPLIT(#dashboard, ',') AS sp ON p.ProjectNumber LIKE sp.value
May be you think the combination like this:
SELECT *
FROM table t INNER JOIN
(
SELECT * FROM (VALUES('bla'),('foo'),('batz')) AS list(col)
) l ON t.column LIKE '%'+l.Col+'%'
If you have defined full text index for your target table then you may use this alternative:
SELECT *
FROM table t
WHERE CONTAINS(t.column, '"bla*" OR "foo*" OR "batz*"')
I was also wondering for something like that. I just tested using a combination of SUBSTRING and IN and it is an effective solution for this kind of problem. Try the below query :
Select * from TB_YOUR T1 Where SUBSTRING(T1.Something, 1,3) IN ('bla', 'foo', 'batz')
In Oracle you can use a collection in the following way:
WHERE EXISTS (SELECT 1
FROM TABLE(ku$_vcnt('bla%', '%foo%', 'batz%'))
WHERE something LIKE column_value)
Here I have used a predefined collection type ku$_vcnt, but you can declare your own one like this:
CREATE TYPE my_collection AS TABLE OF VARCHAR2(4000);
I may have a solution for this, although it will only work in SQL Server 2008 as far as I know. I discovered that you can use the row-constructor described in https://stackoverflow.com/a/7285095/894974 to join a 'fictional' table using a like clause.
It sounds more complex then it is, look:
SELECT [name]
,[userID]
,[name]
,[town]
,[email]
FROM usr
join (values ('hotmail'),('gmail'),('live')) as myTable(myColumn) on email like '%'+myTable.myColumn+'%'
This will result in all users with an e-mail adres like the ones provided in the list.
Hope it's of use to anyone. The problem had been bothering me a while.
For Sql Server you can resort to Dynamic SQL.
Most of the time in such situations you have the parameter of IN clause based on some data from database.
The example below is a little "forced", but this can match various real cases found in legacy databases.
Suppose you have table Persons where person names are stored in a single field PersonName as FirstName + ' ' + LastName.
You need to select all persons from a list of first names, stored in field NameToSelect in table NamesToSelect, plus some additional criteria (like filtered on gender, birth date, etc)
You can do it as follows
-- #gender is nchar(1), #birthDate is date
declare
#sql nvarchar(MAX),
#subWhere nvarchar(MAX)
#params nvarchar(MAX)
-- prepare the where sub-clause to cover LIKE IN (...)
-- it will actually generate where clause PersonName Like 'param1%' or PersonName Like 'param2%' or ...
set #subWhere = STUFF(
(
SELECT ' OR PersonName like ''' + [NameToSelect] + '%'''
FROM [NamesToSelect] t FOR XML PATH('')
), 1, 4, '')
-- create the dynamic SQL
set #sql ='select
PersonName
,Gender
,BirstDate -- and other field here
from [Persons]
where
Gender = #gender
AND BirthDate = #birthDate
AND (' + #subWhere + ')'
set #params = ' #gender nchar(1),
#birthDate Date'
EXECUTE sp_executesql #sql, #params,
#gender,
#birthDate
If you are using MySQL the closest you can get is full-text search:
Full-Text Search, MySQL Documentation
This works for comma separated values
DECLARE #ARC_CHECKNUM VARCHAR(MAX)
SET #ARC_CHECKNUM = 'ABC,135,MED,ASFSDFSF,AXX'
SELECT ' AND (a.arc_checknum LIKE ''%' + REPLACE(#arc_checknum,',','%'' OR a.arc_checknum LIKE ''%') + '%'')''
Evaluates to:
AND (a.arc_checknum LIKE '%ABC%' OR a.arc_checknum LIKE '%135%' OR a.arc_checknum LIKE '%MED%' OR a.arc_checknum LIKE '%ASFSDFSF%' OR a.arc_checknum LIKE '%AXX%')
If you want it to use indexes, you must omit the first '%' character.
In Oracle RBDMS you can achieve this behavior using REGEXP_LIKE function.
The following code will test if the string three is present in the list expression one|two|three|four|five (in which the pipe "|" symbol means OR logic operation).
SELECT 'Success !!!' result
FROM dual
WHERE REGEXP_LIKE('three', 'one|two|three|four|five');
RESULT
---------------------------------
Success !!!
1 row selected.
Preceding expression is equivalent to:
three=one OR three=two OR three=three OR three=four OR three=five
So it will succeed.
On the other hand, the following test will fail.
SELECT 'Success !!!' result
FROM dual
WHERE REGEXP_LIKE('ten', 'one|two|three|four|five');
no rows selected
There are several functions related to regular expressions (REGEXP_*) available in Oracle since 10g version. If you are an Oracle developer and interested this topic this should be a good beginning Using Regular Expressions with Oracle Database.
No answer like this:
SELECT * FROM table WHERE something LIKE ('bla% %foo% batz%')
In oracle no problem.
In Teradata you can use LIKE ANY ('%ABC%','%PQR%','%XYZ%'). Below is an example which has produced the same results for me
--===========
-- CHECK ONE
--===========
SELECT *
FROM Random_Table A
WHERE (Lower(A.TRAN_1_DSC) LIKE ('%american%express%centurion%bank%')
OR Lower(A.TRAN_1_DSC) LIKE ('%bofi%federal%bank%')
OR Lower(A.TRAN_1_DSC) LIKE ('%american%express%bank%fsb%'))
;
--===========
-- CHECK TWO
--===========
SELECT *
FROM Random_Table A
WHERE Lower(A.TRAN_1_DSC) LIKE ANY
('%american%express%centurion%bank%',
'%bofi%federal%bank%',
'%american%express%bank%fsb%')
Sorry for dredging up an old post, but it has a lot of views. I faced a similar problem this week and came up with this pattern:
declare #example table ( sampletext varchar( 50 ) );
insert #example values
( 'The quick brown fox jumped over the lazy dog.' ),
( 'Ask not what your country can do for you.' ),
( 'Cupcakes are the new hotness.' );
declare #filter table ( searchtext varchar( 50 ) );
insert #filter values
( 'lazy' ),
( 'hotness' ),
( 'cupcakes' );
-- Expect to get rows 1 and 3, but no duplication from Cupcakes and Hotness
select *
from #example e
where exists ( select * from #filter f where e.sampletext like '%' + searchtext + '%' )
Exists() works a little better than join, IMO, because it just tests each record in the set, but doesn't cause duplication if there are multiple matches.
This is possible in Postgres using like or ilike and any or all with array. This is an example that worked for me using Postgres 9:
select id, name from tb_organisation where name ilike any (array['%wembley%', '%south%']);
And this prints out:
id | name
-----+------------------------
433 | South Tampa Center
613 | South Pole
365 | Bromley South
796 | Wembley Special Events
202 | Southall
111 | Wembley Inner Space
In T-SQL, this option works but it is not very fast:
CREATE FUNCTION FN_LIKE_IN (#PROC NVARCHAR(MAX), #ITENS NVARCHAR(MAX)) RETURNS NVARCHAR(MAX) AS BEGIN
--Search an item with LIKE inside a list delimited by "," Vathaire 11/06/2019
DECLARE #ITEM NVARCHAR(MAX)
WHILE CHARINDEX(',', #ITENS) > 0 BEGIN
SET #ITEM = LEFT(#ITENS, CHARINDEX(',', #ITENS) - 1)
--IF #ITEM LIKE #PROC
IF #PROC LIKE #ITEM
RETURN #PROC --#ITEM --1
ELSE
SET #ITENS = STUFF(#ITENS, 1, LEN(#ITEM) + 1, '')
END
IF #PROC LIKE #ITENS RETURN #PROC --#ITEM --1
RETURN NULL --0
END
Query:
SELECT * FROM SYS.PROCEDURES
WHERE DBO.FN_LIKE_IN(NAME, 'PRC%,SP%') IS NOT NULL
you can do this dynamically for a large number of elements, at the expense of performance, but it works.
DECLARE #val nvarchar(256),
#list nvarchar(max) = 'one,two,three,ten,five';
CREATE table #table (FIRST_NAME nvarchar(512), LAST_NAME nvarchar(512));
CREATE table #student (FIRST_NAME nvarchar(512), LAST_NAME nvarchar(512), EMAIL
nvarchar(512));
INSERT INTO #student (FIRST_NAME, LAST_NAME, EMAIL)
SELECT 'TEST', ' redOne' ,'test.redOne#toto.com' UNION ALL
SELECT 'student', ' student' ,'student#toto.com' UNION ALL
SELECT 'student', ' two' ,'student.two#toto.com' UNION ALL
SELECT 'hello', ' ONE TWO THREE' ,'student.two#toto.com'
DECLARE check_cursor CURSOR FOR select value from STRING_SPLIT(#list,',')
OPEN check_cursor
FETCH NEXT FROM check_cursor INTO #val
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #val
IF EXISTS (select * from #student where REPLACE(FIRST_NAME, ' ','')
like '%' + #val + '%' OR REPLACE(LAST_NAME, ' ','') like '%' + #val + '%')
BEGIN
INSERT INTO #table (FIRST_NAME, LAST_NAME )
SELECT TOP 1 FIRST_NAME, LAST_NAME VALUE from #student where
REPLACE(FIRST_NAME, ' ','') like '%' + #val + '%' OR REPLACE(LAST_NAME, ' ','')
like '%' + #val + '%'
END;
FETCH NEXT FROM check_cursor INTO #val
END
CLOSE check_cursor;
DEALLOCATE check_cursor;
SELECT * FROM #table;
DROP TABLE #table;
DROP TABLE #student;
use cursor in SQL SERVER and execute for every value:
table sample:
create table Gastos_ConciliacionExcluida(IdRegistro int identity(1,1), MascaraTexto nvarchar(50), Activa bit default 1, Primary key (IDRegistro))
insert into Gastos_ConciliacionExcluida(MascaraTexto) Values ('%Reembolso%')
alter procedure SP_Gastos_ConciliacionExcluidaProcesar
as
declare cur cursor for select MascaraTexto From Gastos_ConciliacionExcluida where Activa=1
declare #Txt nvarchar(50)
open cur
fetch next from cur into #Txt
while ##Fetch_Status = 0
begin
update Gastos_BancoRegistro set PresumibleNoConciliable = 1
where Concepto like #txt
fetch next from cur into #Txt
end
close cur
deallocate cur
do this
WHERE something + '%' in ('bla', 'foo', 'batz')
OR '%' + something + '%' in ('tra', 'la', 'la')
or
WHERE something + '%' in (select col from table where ....)

Conditionally modify query based on parameter

I have this query (something like a case statement which I can use and fix it)
select *
from mytable
where 1=1
and (isNull(ID, 0) = 0 OR UtilityID IN (9,40))
I also want to add another statement
select *
from mytable
where 1=1
and UtilityID NOT IN (9,40)
Everything is happening in a procedure, so want to use a variable like declare #something so if that is passed as 1, use the first statement and the if 0 is passed, use the latter one.
While I appreciate the genius in Dale's answer I find this more readable:
IF #something = 0
BEGIN
select *
from mytable
where ID IS NULL OR ID = 0 OR UtilityID IN (9,40);
END
IF #something = 1
BEGIN
select *
from mytable
where UtilityID NOT IN (9,40);
END
It's procedure code, so use IF to direct the control flow. Also expanded and simplified your where clauses
I think I understand your logic, ignoring the 1=1 (which does nothing) you want to only allow id = 0 when #something = 1. This should do it:
declare #something bit = 0;
declare #mytable table (ID int, UtilityID int);
insert into #mytable (ID, UtilityID)
select 0, 1 union all
select 1, 2 union all
select 2, 9 union all
select 3, 40;
select *
from #mytable
where (
(#something = 1 and (isnull(ID, 0) = 0 or UtilityID in (9,40)))
or (#something = 0 and (UtilityID not in (9,40)))
);
A more performant approach for a larger query could be:
select *
from #mytable
where (#something = 1 and (isnull(ID, 0) = 0 or UtilityID in (9,40)))
union all
select *
from #mytable
where (#something = 0 and (UtilityID not in (9,40)));
PS: Hopefully your ID cannot ever by null - it should have a constraint on it.

Writing CASE Statement in SQL

I have a requirement to display two columns in a report,whose values will be determined using the same input expression as following:
SELECT
CASE WHEN id>10
THEN 'AAAA'
ELSE
'BBBB'
END as 'FirstColumn',
CASE WHEN id>10
THEN
'BBBB'
ELSE
'DDDD' END as 'SecondColumn'
Can I construct this expression without repeating input expression twice as they are same?
You will need to repeat the CASE statment for each field as while the condition might be the same teh results are differnt. The only other alternative is to use a UNion all statement whenre the first select takes the records WHERE id<=10 and the other one does the WHERE ID>10. This can bea viable alternative but it is a littel hareder to maintain, so if the performance is good enough, Iwoudl stick to repeating teh CASE condition.
If some sophisticated is supposed instead of id>10 then, to make it a bit shorter and a bit more readable:
select
IIF(p.b=1, 'AAA', 'BBB') [Col1],
IIF(p.b=1, 'BBB', 'DDD') [Col2]
from
TableName t
outer apply (select cast(case when t.id>10 then 1 else NULL end as bit) as b) p
However, this is only available in SqlServer 2012. In earlier versions, parhaps, you will have to write your own IIF-like scalar function:
create function dbo.IIF (#b bit, #ifValue varchar(50), #elseValue varchar(50))
returns varchar(50)
as begin
return (select case when #b = 1 then #ifValue else #elseValue end)
end
GO
select
dbo.IIF(p.b, 'AAA', 'BBB') [Col1],
dbo.IIF(p.b, 'BBB', 'DDD') [Col2]
from
TableName t
outer apply (select cast(case when t.id>10 then 1 else NULL end as bit) as b) p
If it's worth the trouble, you could created a User-defined function (UDF) that would do this transforamation logic:
CREATE FUNCTION dbo.fColTransform(#id Int)
RETURNS Varchar(20)
AS BEGIN
DECLARE #ret Varchar(20)
SET #ret =
CASE
WHEN #id IS NULL THEN 'Unknown'
WHEN #id > 10 THEN 'BBB'
ELSE 'DDD'
END
RETURN #ret
END
Then, in your SELECT, you could structure it like this:
SELECT dbo.fColTransform(id1) AS FirstColumn,
dbo.fColTransform(id2) AS SecondColumn
FROM MyTable
You can store it in a variable and use Execute to call it:
Declare #foo as nvarchar(max)
set #foo='MyTable'
execute ('Select * from ' + #foo)
The downside is that your entire query would be all red (since it's now a string).
Try with this query, maybe will be useful (in the case if you do not use a function):
WITH temp AS (
SELECT
CASE WHEN x.ID > 10
THEN 'AAAA'
ELSE 'BBBB'
END as Col
FROM (
SELECT 1 AS ID
UNION
SELECT 11 AS ID
UNION
SELECT 13 AS ID
UNION
SELECT 9 AS ID
UNION
SELECT 7 AS ID
) x
)
SELECT temp.Col AS FirstColumn, temp.Col AS SecondColumn
FROM temp
Basically:
With name As (
/*yourquery with one CASE-COLUMN*/
)
SELECT name.COL AS COLUMN1, name.COL AS COLUMN2 FROM name
You can try this here

How can I introduce multiple conditions in LIKE operator?

I want to write an SQL statement like below:
select * from tbl where col like ('ABC%','XYZ%','PQR%');
I know it can be done using OR. But I want to know is there any better solution.
This is a good use of a temporary table.
CREATE TEMPORARY TABLE patterns (
pattern VARCHAR(20)
);
INSERT INTO patterns VALUES ('ABC%'), ('XYZ%'), ('PQR%');
SELECT t.* FROM tbl t JOIN patterns p ON (t.col LIKE p.pattern);
In the example patterns, there's no way col could match more than one pattern, so you can be sure you'll see each row of tbl at most once in the result. But if your patterns are such that col could match more than one, you should use the DISTINCT query modifier.
SELECT DISTINCT t.* FROM tbl t JOIN patterns p ON (t.col LIKE p.pattern);
Oracle 10g has functions that allow the use of POSIX-compliant regular expressions in SQL:
REGEXP_LIKE
REGEXP_REPLACE
REGEXP_INSTR
REGEXP_SUBSTR
See the Oracle Database SQL Reference for syntax details on this functions.
Take a look at Regular expressions in Perl with examples.
Code :
select * from tbl where regexp_like(col, '^(ABC|XYZ|PQR)');
Here is an alternative way:
select * from tbl where col like 'ABC%'
union
select * from tbl where col like 'XYZ%'
union
select * from tbl where col like 'PQR%';
Here is the test code to verify:
create table tbl (col varchar(255));
insert into tbl (col) values ('ABCDEFG'), ('HIJKLMNO'), ('PQRSTUVW'), ('XYZ');
select * from tbl where col like 'ABC%'
union
select * from tbl where col like 'XYZ%'
union
select * from tbl where col like 'PQR%';
+----------+
| col |
+----------+
| ABCDEFG |
| XYZ |
| PQRSTUVW |
+----------+
3 rows in set (0.00 sec)
select * from tbl where col like 'ABC%'
or col like 'XYZ%'
or col like 'PQR%';
This works in toad and powerbuilder. Don't know about the rest
This might help:
select * from tbl where col like '[ABC-XYZ-PQR]%'
I've used this in SQL Server 2005 and it worked.
I also had the same requirement where I didn't have choice to pass like operator multiple times by either doing an OR or writing union query.
This worked for me in Oracle 11g:
REGEXP_LIKE (column, 'ABC.*|XYZ.*|PQR.*');
Even u can try this
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
Query
select * from my_table inner join (select value from fn_split('ABC,MOP',','))
as split_table on my_table.column_name like '%'+split_table.value+'%';
If your parameter value is not fixed or your value can be null based on business you can try the following approach.
DECLARE #DrugClassstring VARCHAR(MAX);
SET #DrugClassstring = 'C3,C2'; -- You can pass null also
---------------------------------------------
IF #DrugClassstring IS NULL
SET #DrugClassstring = 'C3,C2,C4,C5,RX,OT'; -- If null you can set your all conditional case that will return for all
SELECT dn.drugclass_FK , dn.cdrugname
FROM drugname AS dn
INNER JOIN dbo.SplitString(#DrugClassstring, ',') class ON dn.drugclass_FK = class.[Name] -- SplitString is a a function
SplitString function
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
ALTER FUNCTION [dbo].[SplitString](#stringToSplit VARCHAR(MAX),
#delimeter CHAR(1) = ',')
RETURNS #returnList TABLE([Name] [NVARCHAR](500))
AS
BEGIN
--It's use in report sql, before any change concern to everyone
DECLARE #name NVARCHAR(255);
DECLARE #pos INT;
WHILE CHARINDEX(#delimeter, #stringToSplit) > 0
BEGIN
SELECT #pos = CHARINDEX(#delimeter, #stringToSplit);
SELECT #name = SUBSTRING(#stringToSplit, 1, #pos-1);
INSERT INTO #returnList
SELECT #name;
SELECT #stringToSplit = SUBSTRING(#stringToSplit, #pos+1, LEN(#stringToSplit)-#pos);
END;
INSERT INTO #returnList
SELECT #stringToSplit;
RETURN;
END;
I had to add all to Asaph's answer to make it work.
select * from tbl where col like 'ABC%'
union all
select * from tbl where col like 'XYZ%'
union all
select * from tbl where col like 'PQR%';
SELECT *
From tbl
WHERE col
LIKE '[0-9,a-z]%';
simply use this condition of like in sql and you will get your desired answer