Select rows that do not contain a word from another table

Solution 1:

contains means Oracle Text; cross join means Cartesian product (usually performance nightmare).

One option which avoids both of these is instr function (which checks existence of the constraint_word in text, but this time using inner join) and the minus set operator.

Something like this, using sample data you posted:

SQL> select * from text_table;

TEXT
---------------------------
word1.apple; word3, example
word1, apple, word2.car
word1 word2 orange word3
mushroomword1 word2 word3
word1 car
qwerty

6 rows selected.

SQL> select * From words_table;

CONSTRAI
--------
example
apple
orange
mushroom
car
qwerty

6 rows selected.

SQL>

As you said, initially query shouldn't return anything because all constraint_words exist in text:

SQL> select c.text
  2  from text_table c
  3  minus
  4  select b.text
  5  from words_table a join text_table b on instr(b.text, a.constraint_word) > 0;

no rows selected

Let's modify one of text rows:

SQL> update text_table set text = 'xxx' where text = 'qwerty';

1 row updated.

What's the result now?

SQL> select c.text
  2  from text_table c
  3  minus
  4  select b.text
  5  from words_table a join text_table b on instr(b.text, a.constraint_word) > 0;

TEXT
---------------------------
xxx

SQL>

Right; text we've just modified.

Solution 2:

Your idea is fine, since you need to test all words for each text. This is what CROSS JOIN does - a combination (cartesian product).

We can even be more restrictive for better performance and use INNER JOIN, or the shorthand JOIN.

See also: CROSS JOIN vs INNER JOIN in SQL

Additionally you need to filter all text records, where there are no matches at all. This means the count of non-matches over all combinations per text is maximum (= number of constraint_words, here 6). This filter can be done using GROUP BY WITH HAVING

-- text without any constaint_word
SELECT t.text, count(*)
FROM text_table t
JOIN words_table w ON CONTAINS(t.text, w.constraint_word, 1) = 0
GROUP BY t.text
HAVING count(*) = (SELECT count(*) FROM words_table)
;

It will output:

text count(*)
mushroomword1 word2 word3 6

Try the demo on on SQL Fiddle

Entire-word vs partial matches

Note that 'mushroom' from constraint words is not matched by CONTAINS because it is contained as word-part not as entire word.

For partial-matches you can use INSTR as answered by Littlefoot.

See also

  • Use string contains function in oracle SQL query
  • How does contains() in PL-SQL work?
  • Oracle context indexes
  • Creating and Maintaining Oracle Text Indexes