【问题标题】:Select rows that do not contain a word from another table从另一个表中选择不包含单词的行
【发布时间】:2022-01-17 18:04:05
【问题描述】:

我有一个表格,每行一个单词,还有一个表格,其中一行有一些文本。我需要 从第二个表中只选择那些不包含第一个表中的单词的行。

例如:

带有约束词的表格

constraint_word
example
apple
orange
mushroom
car
qwerty

带文字的表格

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

在这种情况下不应选择任何内容,因为第二个表中的每一行都包含第一个表中的单词。

我只有一个想法,使用CROSS JOIN 来实现这一目标

SELECT DISTINCT text FROM text_table CROSS JOIN words_table
           WHERE CONTAINS(text, constraint_word ) = 0

有没有办法不使用CROSS JOIN

【问题讨论】:

  • 您使用的是哪个 dbms?
  • @jarlh 我使用 Oracle

标签: sql oracle


【解决方案1】:

contains 表示 Oracle 文本;交叉连接意味着笛卡尔积(通常是性能噩梦)。

避免这两种情况的一个选项是instr 函数(它检查text 中是否存在constraint_word,但这次使用内部连接)和minus 集合运算符。

类似这样,使用您发布的示例数据:

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>

正如你所说,最初查询不应该返回任何内容,因为所有constraint_words 都存在于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

让我们修改text 行之一:

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

1 row updated.

现在结果如何?

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>

对;我们刚刚修改的文本。

【讨论】:

  • 虽然INSTR 的性能更好,但匹配不同:CONTAINS 将遵守某些语法约束,例如单词边界,而 INSTR 也匹配 mushroomword1(其中 'mushroom' 不是整个单词,而是部分匹配)。
  • @hc_dev,样本数据(参见第 4 行,“mushroomword1 ...”)表明部分匹配是可以的。
【解决方案2】:

您的想法很好,因为您需要测试每个文本的所有单词。 这就是 CROSS JOIN 所做的 - 组合(笛卡尔积)。

我们甚至可以限制更多以获得更好的性能,并使用 INNER JOIN 或简写 JOIN

另见:CROSS JOIN vs INNER JOIN in SQL

此外,您需要过滤所有没有匹配项的 text 记录。这意味着每个text 的所有组合的不匹配数是最大值(= 约束字数,此处为 6)。 这个过滤器可以使用GROUP BYHAVING来完成

-- 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)
;

它会输出:

text count(*)
mushroomword1 word2 word3 6

SQL Fiddle上试用演示

整个单词与部分匹配

请注意,约束词中的 'mushroom' 与 CONTAINS 不匹配,因为它包含为 word-part 而不是整个词。

对于部分匹配,您可以将INSTR 用作answered by Littlefoot

另见

【讨论】:

    【解决方案3】:

    我相信这行得通(我认为CROSS JOIN 路由的问题在于它包含任何不包含至少一个单词的文本——不仅仅是不包含的文本' t 包含任何):

    SELECT DISTINCT text FROM text_table WHERE (SELECT COUNT(*) FROM words_table WHERE CONTAINS(text, constraint_word)) = 0;
    

    【讨论】:

      猜你喜欢
      • 2014-03-06
      • 2015-09-23
      • 1970-01-01
      • 2017-07-12
      • 1970-01-01
      • 2016-06-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多