【问题标题】:MySQL Query to Match Unrelated TermsMySQL 查询以匹配不相关的术语
【发布时间】:2012-05-25 02:35:49
【问题描述】:

我正在尝试构建一个让我发疯的查询。我不知道从哪里开始解决它,但是在搜索了一下之后,我开始使用子查询。现在,我不确定这是否会解决我的问题,或者如果可以,如何创建一个可以满足我需求的解决方案。

这是我当前表的一个非常简单的视图(称为 tbl_1):

---------------------------------
|  row |  name  |  other_names  |
|-------------------------------|
|   1  |   A    |    B, C       |
|   2  |   B    |    C          |
|   3  |   A    |    C          |
|   4  |   D    |    E          |
|   5  |   C    |    A, B       |
---------------------------------

我正在使用的某些商品有多个名称(品牌名称、其他国家/地区的名称、代号等),但最终所有这些不同的名称都指的是同一个商品。我最初是按照以下方式运行搜索查询:

SELECT * FROM tbl_1
WHERE name LIKE '%A%'
OR other_names LIKE '%A%';

这将返回第 1 行和第 3 行。但是,我很快意识到我的查询也应该返回第 2 行,因为 A = B = C。我将如何去做这样的事情?我对花哨查询之外的其他建议持开放态度,例如构建另一个表,以某种方式将所有名称组合到一行中,但我认为这样的事情容易出错或效率低下。

此外,我正在运行 MySQL 5.5.23,使用 InnoDB 以及用 PHP 和 Python 编写的其他代码。

谢谢!

2012 年 5 月 26 日更新:
我回到了使用子查询的最初想法,但是当我认为我到达某个地方时,我遇到了一个记录在案的 MySQL 问题,其中查询是从外部评估的,我的子查询将针对每一行进行评估,并且不会在现实的时间内完成。这是我试图做的:

SELECT * FROM tbl_1
WHERE name = ANY
    (SELECT name FROM tbl_1 WHERE other_names LIKE '%A%' or name LIKE '%A%')
OR other_names = ANY 
    (SELECT name FROM tbl_1 WHERE other_names LIKE '%A%' or name LIKE '%A%')

使用示例表返回我想要的内容,但前面提到的 MySQL 问题/错误导致子查询被视为依赖查询而不是独立查询。结果,我无法在我的真实表(约 250,000 行)上测试查询,因为它最终超时。

我已经读到该问题的主要解决方法是使用联接而不是子查询,但我不确定如何将其应用于我正在尝试做的事情。我想得越多,使用 PHP/Python 独立运行子查询并使用结果数组来制作我想要的主查询可能会更好。但是,我仍然认为可能会遗漏一些结果,因为列中的术语不如我的示例好(有些术语是多个单词,有些有括号,其他名称不一定是逗号-分开等)。

或者,我正在考虑构建一个单独的表来构建必要的链接,例如:

| 1 | A | B, C|
| 2 | B | C, A|
| 3 | C | A, B|

但考虑到我正在使用的数据及其存在的非标准化格式,我认为说起来容易做起来难。

此时我强烈考虑的方法是使用易于构建的链接(即名称:其他名称的 1:1 比例)构建一个单独的表,因此我不必处理格式问题存在于 other_names 列中。我还可以消除/限制 LIKE 的使用,并要求用户至少知道一个确切的名称,以简化结果并可能提高整体性能。

总之,我讨厌处理我无法控制的输入数据。

【问题讨论】:

  • 只是想了解表结构,如果 B 实际上只是 A 的另一个名称,而不是为什么它有一个单独的行(第 2 行)?
  • @coder 行中的数据由不一定知道某事物的所有名称的用户填充。更具体地说,这些名称是指药品。一个人可能知道美国的 Incivek = telaprevir 并输入它(Incivek in name,telaprevir in other_names),但它在欧洲也称为 Incivo。因此,一个人可能正在搜索“Incivek”,但它也应该为 Incivo 提取结果。

标签: mysql select subquery


【解决方案1】:

偶然发现了这个问题,所以我不知道我的建议是否相关,但这看起来像“union-find”之类的好用法。

SELECT 将非常简单和快速。 但是插入和更新相对复杂,您可能需要一个代码内循环(更新行> 0)......和几个数据库调用

表格示例:

---------------------------
|  row |  name  |  group  |
|-------------------------|
|   1  |   A    |    1    |
|   2  |   B    |    1    |
|   4  |   C    |    1    |
|   5  |   D    |    2    |
|   6  |   X    |    1    |
|   7  |   Z    |    2    |
---------------------------

选择: SELECT name FROM tbl WHERE group = (SELECT group FROM tbl WHERE name LIKE '%A%')


插入关系 K = T: (psedu codeish..)

SELECT group as gk WHERE name = K; SELECT group as gt WHERE name = T;

if (gk empty result) 和 (gt empty result) 都插入新组

---------------------------
|  row |  name  |  group  |
|-------------------------|
|   1  |   A    |    1    |
|   2  |   B    |    1    |
|   4  |   C    |    1    |
|   5  |   D    |    2    |
|   6  |   X    |    1    |
|   7  |   Z    |    2    |
|   8  |   K    |    3    |
|   9  |   T    |    3    |
---------------------------

if (gk empty result) and (gt NOT empty result) insert t with group = gx.group

---------------------------
|  row |  name  |  group  |
|-------------------------|
|   1  |   A    |    1    |
|   2  |   B    |    1    |
|   4  |   C    |    1    |
|   5  |   D    |    2    |
|   6  |   X    |    1    |
|   7  |   Z    |    2    |
|   8  |   K    |    2    |
|   9  |   T    |    2    |
---------------------------

(其他情况相同)

当两者都不为空时,将一组更新为另一组

更新tbl1 SET group = gt WHERE group = gk

【讨论】:

    【解决方案2】:

    我想不出一个支持无限深度名称身份的查询。但是,如果您可以使用有限数量的“递归”,您可以考虑使用与此类似的查询,从您提供的查询开始,检索所有具有名称标识的行:

    SELECT a.* FROM tbl_1 a
    WHERE a.name='A'
    OR a.other_names LIKE '%A%'
    UNION
    SELECT b.* FROM tbl_1 a
    JOIN tbl_1 b ON a.other_names LIKE '%' || b.name || '%' OR b.other_names LIKE '%' || a.name || '%'
    WHERE a.name='A'
    OR a.other_names LIKE '%A%';
    

    此查询将返回第 2 行,但在您的示例中它不会返回任何以“B”为“other_name”的其他行。所以你必须联合另一个查询:

    SELECT a.* FROM tbl_1 a
    WHERE a.name='A'
    OR a.other_names LIKE '%A%'
    UNION
    SELECT b.* FROM tbl_1 a
    JOIN tbl_1 b ON a.other_names LIKE '%' || b.name || '%' OR b.other_names LIKE '%' || a.name || '%'
    WHERE a.name='A'
    OR a.other_names LIKE '%A%';
    UNION
    SELECT c.* FROM tbl_1 a
    JOIN tbl_1 b ON (a.other_names LIKE '%' || b.name || '%' OR b.other_names LIKE '%' || a.name || '%')
    JOIN tbl_1 c ON (b.other_names LIKE '%' || c.name || '%' OR c.other_names LIKE '%' || b.name || '%')
    WHERE a.name='A'
    OR a.other_names LIKE '%A%';
    

    如您所见,查询会随着深度的增加而迅速增长和加速,而且它也不是我所说的美丽。但它可能适合您的需求。我在使用 MySQL 函数方面不是很有经验,但我想你可以创建一个更优雅的解决方案,也可以使用这些函数无限深度地工作。您也可以考虑使用 Python 以编程方式解决问题。

    【讨论】:

    • 谢谢!由于实际问题(我的表有超过 200,000 行),这并不能真正解决我的问题,但是你提到的递归让我很快意识到这实际上是多么困难。在这一点上,我想我将尝试使用 python 来处理输入数据并创建一个包含所有正确链接的单独表。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-11
    • 1970-01-01
    • 2014-06-02
    • 2011-02-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多