Can the "IN" operator use LIKE-wildcards (%) in Oracle? - sql

I have searched this question, and found an answer in MySQL but this is one of those incidents where the statement fails to cross over into Oracle.
Can I use wildcards in "IN" MySQL statement?
pretty much sums up my question and what I would like to do, but in Oracle
I would like to find the legal equivalent of
Select * from myTable m
where m.status not in ('Done%', 'Finished except%', 'In Progress%')
Thanks for any help

Select * from myTable m
where m.status not like 'Done%'
and m.status not like 'Finished except%'
and m.status not like 'In Progress%'

It seems that you can use regexp too
WHERE NOT REGEXP_LIKE(field, '^Done|^Finished')
I'm not sure how well this will perform though ... see here

This appears to fit what you are looking for: https://forums.oracle.com/forums/thread.jspa?threadID=2140801
Basically, you will need to use regular expressions as there appears to be nothing built into oracle for this.
I pulled out the example from the thread and converted it for your purposes. I suck at regex's, though, so that might need tweaked :)
SELECT *
FROM myTable m
WHERE NOT regexp_like(m.status,'((Done^|Finished except^|In Progress^)')

Not 100% what you were looking for, but kind of an inside-out way of doing it:
SQL> CREATE TABLE mytable (id NUMBER, status VARCHAR2(50));
Table created.
SQL> INSERT INTO mytable VALUES (1,'Finished except pouring water on witch');
1 row created.
SQL> INSERT INTO mytable VALUES (2,'Finished except clicking ruby-slipper heels');
1 row created.
SQL> INSERT INTO mytable VALUES (3,'You shall (not?) pass');
1 row created.
SQL> INSERT INTO mytable VALUES (4,'Done');
1 row created.
SQL> INSERT INTO mytable VALUES (5,'Done with it.');
1 row created.
SQL> INSERT INTO mytable VALUES (6,'In Progress');
1 row created.
SQL> INSERT INTO mytable VALUES (7,'In progress, OK?');
1 row created.
SQL> INSERT INTO mytable VALUES (8,'In Progress Check Back In Three Days'' Time');
1 row created.
SQL> SELECT *
2 FROM mytable m
3 WHERE +1 NOT IN (INSTR(m.status,'Done')
4 , INSTR(m.status,'Finished except')
5 , INSTR(m.status,'In Progress'));
ID STATUS
---------- --------------------------------------------------
3 You shall (not?) pass
7 In progress, OK?
SQL>

Somewhat convoluted, but:
Select * from myTable m
join (SELECT a.COLUMN_VALUE || b.COLUMN_VALUE status
FROM (TABLE(Sys.Dbms_Debug_Vc2coll('Done', 'Finished except', 'In Progress'))) a
JOIN (Select '%' COLUMN_VALUE from dual) b on 1=1) params
on params.status like m.status;
This was a solution for a very unique problem, but it might help someone. Essentially there is no "in like" statement and there was no way to get an index for the first variable_n characters of the column, so I made this to make a fast dynamic "in like" for use in SSRS.
The list content ('Done', 'Finished except', 'In Progress') can be variable.

The closest legal equivalent to illegal syntax mentioned in question is:
select * from myTable m
where not exists (
select 1
from table(sys.ku$_vcnt('Done', 'Finished except', 'In Progress')) patterns
where m.status like patterns.column_value || '%'
)
Both mine and #Sethionic's answer make possible to list patterns dynamically (just by choosing other source than auxiliar sys.whatever table).
Note, if we had to search pattern inside string (rather than from the beginning) and database contained for example status = 'Done In Progress', then
my solution (modified to like '%' || patterns.column_value || '%') would still generate one row for given record, whileas
the #Sethionic's solution (modified to another auxiliar join before a) would produce multiple rows for each pattern occurence.
Not judging which is better, just be aware of differences and choose which better fits your need.

Related

'In' clause in SQL server with multiple columns

I have a component that retrieves data from database based on the keys provided.
However I want my java application to get all the data for all keys in a single database hit to fasten up things.
I can use 'in' clause when I have only one key.
While working on more than one key I can use below query in oracle
SELECT * FROM <table_name>
where (value_type,CODE1) IN (('I','COMM'),('I','CORE'));
which is similar to writing
SELECT * FROM <table_name>
where value_type = 1 and CODE1 = 'COMM'
and
SELECT * FROM <table_name>
where value_type = 1 and CODE1 = 'CORE'
together
However, this concept of using 'in' clause as above is giving below error in 'SQL server'
ERROR:An expression of non-boolean type specified in a context where a condition is expected, near ','.
Please let know if their is any way to achieve the same in SQL server.
This syntax doesn't exist in SQL Server. Use a combination of And and Or.
SELECT *
FROM <table_name>
WHERE
(value_type = 1 and CODE1 = 'COMM')
OR (value_type = 1 and CODE1 = 'CORE')
(In this case, you could make it shorter, because value_type is compared to the same value in both combinations. I just wanted to show the pattern that works like IN in oracle with multiple fields.)
When using IN with a subquery, you need to rephrase it like this:
Oracle:
SELECT *
FROM foo
WHERE
(value_type, CODE1) IN (
SELECT type, code
FROM bar
WHERE <some conditions>)
SQL Server:
SELECT *
FROM foo
WHERE
EXISTS (
SELECT *
FROM bar
WHERE <some conditions>
AND foo.type_code = bar.type
AND foo.CODE1 = bar.code)
There are other ways to do it, depending on the case, like inner joins and the like.
If you have under 1000 tuples you want to check against and you're using SQL Server 2008+, you can use a table values constructor, and perform a join against it. You can only specify up to 1000 rows in a table values constructor, hence the 1000 tuple limitation. Here's how it would look in your situation:
SELECT <table_name>.* FROM <table_name>
JOIN ( VALUES
('I', 'COMM'),
('I', 'CORE')
) AS MyTable(a, b) ON a = value_type AND b = CODE1;
This is only a good idea if your list of values is going to be unique, otherwise you'll get duplicate values. I'm not sure how the performance of this compares to using many ANDs and ORs, but the SQL query is at least much cleaner to look at, in my opinion.
You can also write this to use EXIST instead of JOIN. That may have different performance characteristics and it will avoid the problem of producing duplicate results if your values aren't unique. It may be worth trying both EXIST and JOIN on your use case to see what's a better fit. Here's how EXIST would look,
SELECT * FROM <table_name>
WHERE EXISTS (
SELECT 1
FROM (
VALUES
('I', 'COMM'),
('I', 'CORE')
) AS MyTable(a, b)
WHERE a = value_type AND b = CODE1
);
In conclusion, I think the best choice is to create a temporary table and query against that. But sometimes that's not possible, e.g. your user lacks the permission to create temporary tables, and then using a table values constructor may be your best choice. Use EXIST or JOIN, depending on which gives you better performance on your database.
Normally you can not do it, but can use the following technique.
SELECT * FROM <table_name>
where (value_type+'/'+CODE1) IN (('I'+'/'+'COMM'),('I'+'/'+'CORE'));
A better solution is to avoid hardcoding your values and put then in a temporary or persistent table:
CREATE TABLE #t (ValueType VARCHAR(16), Code VARCHAR(16))
INSERT INTO #t VALUES ('I','COMM'),('I','CORE')
SELECT DT. *
FROM <table_name> DT
JOIN #t T ON T.ValueType = DT.ValueType AND T.Code = DT.Code
Thus, you avoid storing data in your code (persistent table version) and allow to easily modify the filters (without changing the code).
I think you can try this, combine and and or at the same time.
SELECT
*
FROM
<table_name>
WHERE
value_type = 1
AND (CODE1 = 'COMM' OR CODE1 = 'CORE')
What you can do is 'join' the columns as a string, and pass your values also combined as strings.
where (cast(column1 as text) ||','|| cast(column2 as text)) in (?1)
The other way is to do multiple ands and ors.
I had a similar problem in MS SQL, but a little different. Maybe it will help somebody in futere, in my case i found this solution (not full code, just example):
SELECT Table1.Campaign
,Table1.Coupon
FROM [CRM].[dbo].[Coupons] AS Table1
INNER JOIN [CRM].[dbo].[Coupons] AS Table2 ON Table1.Campaign = Table2.Campaign AND Table1.Coupon = Table2.Coupon
WHERE Table1.Coupon IN ('0000000001', '0000000002') AND Table2.Campaign IN ('XXX000000001', 'XYX000000001')
Of cource on Coupon and Campaign in table i have index for fast search.
Compute it in MS Sql
SELECT * FROM <table_name>
where value_type + '|' + CODE1 IN ('I|COMM', 'I|CORE');

Can a view auto-identify the table name(s) on which it is based ?

Wonder if someone might be able to help me. I'm trying to build a view on a table that allows me to put the table name as an extra column into that view. I can do this through a manual process by mentioning'TableName' AS ColumnA, but I'm trying to see if there is a way that I can get SQL to grab the Table name from its own query to add this as a column.
No. The problem with this, may be that, a view can draw upon multiple tables. You are looking for Me.parent.name or this.parent.name. More precisely, Me.parents(0), Me.parents(1) and so, based on all tables that were joined to create a view. Also, a view can be based on other views. Then, the reference would look like :Me.parents_at_up_level(0).name, if the view at dependency level 3, wants to look at its great-grandfather-table-name. Nevertheless, a good idea and good thinking, this should have been possible.
The short answer is yes (see example below). The long answer is that may depend upon how your view is constructed.
If you're on SQL Server 2008 or greater, you can use sys.dm_sql_referenced_entities to get dependency info for a DB object.
Let's try it with a view:
create table myTable (myColumn int, myOtherColumn char(1));
insert into myTable select 1, 'a';
insert into myTable select 2, 'b';
create view myView as
select t.*, e.referenced_entity_name
from myTable t
join sys.dm_sql_referenced_entities ('dbo.myView', 'OBJECT') e
on e.referenced_minor_id = 0;
select * from myView;
Results:
myColumn myOtherColumn referenced_entity_name
-------- ------------- ----------------------
1 a myTable
2 b myTable
(2 row(s) affected)
Will this simple example fit your usage? Please let us know.
I'm assuming you're working with a set of tables that have at least 1 column in common. In such case, you are probably using a coalesce function to determine the value of that column. In such cases you can use a case block to track the table it came from. Something similar to the below might work for you:
CASE WHEN a.col <> '' THEN 'Table A'
WHEN b.col <> '' THEN 'Table B'
WHEN c.col <> '' THEN 'Table C'
ELSE 'Table D'
END AS colA,
COALESCE(a.col, b.col, c.col, d.col) AS colB
I hope this at least points you in the right direction.
C§

IF-Statement in SQLite: update or insert?

I Can't run this query with SQLite
if 0<(select COUNT(*) from Repetition where (Word='behnam' and Topic='mine'))
begin
update Repetition set Counts=1+ (select Counts from Repetition where (Word='behnam' and Topic='mine'))
end
else
begin
insert Repetition(Word,Topic,Counts)values('behnam','mine',1)
end
It says "Syntax error near IF"
How can I solve the problem
SQLite does not have an IF statement (see the list of supported queries)
Insetad, check out out ERIC B's suggestion on another thread. You're effectively looking at doing an UPSERT (UPdate if the record exists, INSERT if not). Eric B. has a good example of how to do this in SQLite syntax utilizing the "INSERT OR REPLACE" functionality in SQLite. Basically, you'd do something like:
INSERT OR REPLACE INTO Repetition (Word, Topic, Counts)
VALUES ( 'behnam', 'mine',
coalesce((select Counts + 1 from Repetition
where Word = 'behnam', AND Topic = 'mine)
,1)
)
Another approach is to INSERT ... SELECT ... WHERE ... EXISTS [or not] (SELECT ...);
I do this sort of thing all the time, and I use jklemmack's suggestion as well. And I do it for other purposes too, such as doing JOINs in UPDATEs (which SQLite3 does not support).
For example:
CREATE TABLE t(id INTEGER PRIMARY KEY, c1 TEXT NOT NULL UNIQUE, c2 TEXT);
CREATE TABLE r(c1 TEXT NOT NULL UNIQUE, c2 TEXT);
INSERT OR REPLACE INTO t (id, c1, c2)
SELECT t.id, coalesce(r.c1, t.c1), coalesce(r.c2, t.c2)
FROM r LEFT OUTER JOIN t ON r.c1 = t.c1
WHERE r.c2 = #param;
The WHERE there has the condition that you'd have in your IF. The JOIN in the SELECT provides the JOIN that SQLite3 doesn't support in UPDATE. The INSERT OR REPLACE and the use of t.id (which can be NULL if the row doesn't exist in t) together provide the THEN and ELSE bodies.
You can apply this over and over. If you'd have three statements (that cannot somehow be merged into one) in the THEN part of the IF you'd need to have three statements with the IF condition in their WHEREs.
This is called an UPSERT (i.e. UPdate or inSERT). It has its forms in almost every type of database. Look at this question for the SQLite version: SQLite - UPSERT *not* INSERT or REPLACE
One way that I've found is based on SQL WHERE clause true/false statement:
SELECT * FROM SOME_TABLE
WHERE
(
SELECT SINGLE_COLUMN_NAME FROM SOME_OTHER_TABLE
WHERE
SOME_COLUMN = 'some value' and
SOME_OTHER_COLUMN = 'some other value'
)
This actually means execute some QUERIES if some other QUERY returns 'any' result.

IN Clause with NULL or IS NULL

Postgres is the database
Can I use a NULL value for a IN clause? example:
SELECT *
FROM tbl_name
WHERE id_field IN ('value1', 'value2', 'value3', NULL)
I want to limit to these four values.
I have tried the above statement and it doesn't work, well it executes but doesn't add the records with NULL id_fields.
I have also tried to add a OR condition but this just make the query run and run with no end in sight.
SELECT *
FROM tbl_name
WHERE other_condition = bar
AND another_condition = foo
AND id_field IN ('value1', 'value2', 'value3')
OR id_field IS NULL
Any suggestions?
An in statement will be parsed identically to field=val1 or field=val2 or field=val3. Putting a null in there will boil down to field=null which won't work.
(Comment by Marc B)
I would do this for clairity
SELECT *
FROM tbl_name
WHERE
(id_field IN ('value1', 'value2', 'value3') OR id_field IS NULL)
Your query fails due to operator precedence. AND binds before OR!
You need a pair of parentheses, which is not a matter of "clarity", but pure logic necessity.
SELECT *
FROM tbl_name
WHERE other_condition = bar
AND another_condition = foo
AND (id_field IN ('value1', 'value2', 'value3') OR id_field IS NULL);
The added parentheses prevent AND binding before OR. If there were no other WHERE conditions (no AND) you would not need additional parentheses. The accepted answer is misleading in this respect.
SELECT *
FROM tbl_name
WHERE coalesce(id_field,'unik_null_value')
IN ('value1', 'value2', 'value3', 'unik_null_value')
So that you eliminate the null from the check. Given a null value in id_field, the coalesce function would instead of null return 'unik_null_value', and by adding 'unik_null_value to the IN-list, the query would return posts where id_field is value1-3 or null.
The question as answered by Daniel is perfctly fine. I wanted to leave a note regarding NULLS. We should be carefull about using NOT IN operator when a column contains NULL values. You won't get any output if your column contains NULL values and you are using the NOT IN operator. This is how it's explained over here http://www.oraclebin.com/2013/01/beware-of-nulls.html , a very good article which I came across and thought of sharing it.
Note: Since someone claimed that the external link is dead in Sushant Butta's answer I've posted the content here as a separate answer.
Beware of NULLS.
Today I came across a very strange behaviour of query while using IN and NOT IN operators. Actually I wanted to compare two tables and find out whether a value from table b existed in table a or not and find out its behavior if the column containsnull values. So I just created an environment to test this behavior.
We will create table table_a.
SQL> create table table_a ( a number);
Table created.
We will create table table_b.
SQL> create table table_b ( b number);
Table created.
Insert some values into table_a.
SQL> insert into table_a values (1);
1 row created.
SQL> insert into table_a values (2);
1 row created.
SQL> insert into table_a values (3);
1 row created.
Insert some values into table_b.
SQL> insert into table_b values(4);
1 row created.
SQL> insert into table_b values(3);
1 row created.
Now we will execute a query to check the existence of a value in table_a by checking its value from table_b using IN operator.
SQL> select * from table_a where a in (select * from table_b);
A
----------
3
Execute below query to check the non existence.
SQL> select * from table_a where a not in (select * from table_b);
A
----------
1
2
The output came as expected. Now we will insert a null value in the table table_b and see how the above two queries behave.
SQL> insert into table_b values(null);
1 row created.
SQL> select * from table_a where a in (select * from table_b);
A
----------
3
SQL> select * from table_a where a not in (select * from table_b);
no rows selected
The first query behaved as expected but what happened to the second query? Why didn't we get any output, what should have happened? Is there any difference in the query? No.
The change is in the data of table table_b. We have introduced a null value in the table. But how come it's behaving like this? Let's split the two queries into "AND" and "OR" operator.
First Query:
The first query will be handled internally something like this. So a null will not create a problem here as my first two operands will either evaluate to true or false. But my third operand a = null will neither evaluate to true nor false. It will evaluate to null only.
select * from table_a whara a = 3 or a = 4 or a = null;
a = 3 is either true or false
a = 4 is either true or false
a = null is null
Second Query:
The second query will be handled as below. Since we are using an "AND" operator and anything other than true in any of the operand will not give me any output.
select * from table_a whara a <> 3 and a <> 4 and a <> null;
a <> 3 is either true or false
a <> 4 is either true or false
a <> null is null
So how do we handle this? We will pick all the not null values from table table_b while using NOT IN operator.
SQL> select * from table_a where a not in (select * from table_b where b is not null);
A
----------
1
2
So always be careful about NULL values in the column while using NOT IN operator.
Beware of NULL!!
I know that is late to answer but could be useful for someone else
You can use sub-query and convert the null to 0
SELECT *
FROM (SELECT CASE WHEN id_field IS NULL
THEN 0
ELSE id_field
END AS id_field
FROM tbl_name) AS tbl
WHERE tbl.id_field IN ('value1', 'value2', 'value3', 0)
Null refers to an absence of data. Null is formally defined as a value that is unavailable, unassigned, unknown or inapplicable (OCA Oracle Database 12c, SQL Fundamentals I Exam Guide, p87).
So, you may not see records with columns containing null values when said columns are restricted using an "in" or "not in" clauses.

Using LIKE in an Oracle IN clause

I know I can write a query that will return all rows that contain any number of values in a given column, like so:
Select * from tbl where my_col in (val1, val2, val3,... valn)
but if val1, for example, can appear anywhere in my_col, which has datatype varchar(300), I might instead write:
select * from tbl where my_col LIKE '%val1%'
Is there a way of combing these two techniques. I need to search for some 30 possible values that may appear anywhere in the free-form text of the column.
Combining these two statements in the following ways does not seem to work:
select * from tbl where my_col LIKE ('%val1%', '%val2%', 'val3%',....)
select * from tbl where my_col in ('%val1%', '%val2%', 'val3%',....)
What would be useful here would be a LIKE ANY predicate as is available in PostgreSQL
SELECT *
FROM tbl
WHERE my_col LIKE ANY (ARRAY['%val1%', '%val2%', '%val3%', ...])
Unfortunately, that syntax is not available in Oracle. You can expand the quantified comparison predicate using OR, however:
SELECT *
FROM tbl
WHERE my_col LIKE '%val1%' OR my_col LIKE '%val2%' OR my_col LIKE '%val3%', ...
Or alternatively, create a semi join using an EXISTS predicate and an auxiliary array data structure (see this question for details):
SELECT *
FROM tbl t
WHERE EXISTS (
SELECT 1
-- Alternatively, store those values in a temp table:
FROM TABLE (sys.ora_mining_varchar2_nt('%val1%', '%val2%', '%val3%'/*, ...*/))
WHERE t.my_col LIKE column_value
)
For true full-text search, you might want to look at Oracle Text: http://www.oracle.com/technetwork/database/enterprise-edition/index-098492.html
A REGEXP_LIKE will do a case-insensitive regexp search.
select * from Users where Regexp_Like (User_Name, 'karl|anders|leif','i')
This will be executed as a full table scan - just as the LIKE or solution, so the performance will be really bad if the table is not small. If it's not used often at all, it might be ok.
If you need some kind of performance, you will need Oracle Text (or some external indexer).
To get substring indexing with Oracle Text you will need a CONTEXT index. It's a bit involved as it's made for indexing large documents and text using a lot of smarts. If you have particular needs, such as substring searches in numbers and all words (including "the" "an" "a", spaces, etc) , you need to create custom lexers to remove some of the smart stuff...
If you insert a lot of data, Oracle Text will not make things faster, especially if you need the index to be updated within the transactions and not periodically.
No, you cannot do this. The values in the IN clause must be exact matches. You could modify the select thusly:
SELECT *
FROM tbl
WHERE my_col LIKE %val1%
OR my_col LIKE %val2%
OR my_col LIKE %val3%
...
If the val1, val2, val3... are similar enough, you might be able to use regular expressions in the REGEXP_LIKE operator.
Yes, you can use this query (Instead of 'Specialist' and 'Developer', type any strings you want separated by comma and change employees table with your table)
SELECT * FROM employees em
WHERE EXISTS (select 1 from table(sys.dbms_debug_vc2coll('Specialist', 'Developer')) mt
where em.job like ('%' || mt.column_value || '%'));
Why my query is better than the accepted answer: You don't need a CREATE TABLE permission to run it. This can be executed with just SELECT permissions.
In Oracle you can use regexp_like as follows:
select *
from table_name
where regexp_like (name, '^(value-1|value-2|value-3....)');
The caret (^) operator to indicate a beginning-of-line character &
The pipe (|) operator to indicate OR operation.
This one is pretty fast :
select * from listofvalue l
inner join tbl on tbl.mycol like '%' || l.value || '%'
Just to add on #Lukas Eder answer.
An improvement to avoid creating tables and inserting values
(we could use select from dual and unpivot to achieve the same result "on the fly"):
with all_likes as
(select * from
(select '%val1%' like_1, '%val2%' like_2, '%val3%' like_3, '%val4%' as like_4, '%val5%' as like_5 from dual)
unpivot (
united_columns for subquery_column in ("LIKE_1", "LIKE_2", "LIKE_3", "LIKE_4", "LIKE_5"))
)
select * from tbl
where exists (select 1 from all_likes where tbl.my_col like all_likes.united_columns)
I prefer this
WHERE CASE WHEN my_col LIKE '%val1%' THEN 1
WHEN my_col LIKE '%val2%' THEN 1
WHEN my_col LIKE '%val3%' THEN 1
ELSE 0
END = 1
I'm not saying it's optimal but it works and it's easily understood. Most of my queries are adhoc used once so performance is generally not an issue for me.
select * from tbl
where exists (select 1 from all_likes where all_likes.value = substr(tbl.my_col,0, length(tbl.my_col)))
You can put your values in ODCIVARCHAR2LIST and then join it as a regular table.
select tabl1.* FROM tabl1 LEFT JOIN
(select column_value txt from table(sys.ODCIVARCHAR2LIST
('%val1%','%val2%','%val3%')
)) Vals ON tabl1.column LIKE Vals.txt WHERE Vals.txt IS NOT NULL
You don't need a collection type as mentioned in https://stackoverflow.com/a/6074261/802058. Just use an subquery:
SELECT *
FROM tbl t
WHERE EXISTS (
SELECT 1
FROM (
SELECT 'val1%' AS val FROM dual
UNION ALL
SELECT 'val2%' AS val FROM dual
-- ...
-- or simply use an subquery here
)
WHERE t.my_col LIKE val
)