根据具有同义行的标签 table 选择项目

Selecting items according to tags table with synonymous rows

我有三个 table:文章、标签和 articles_tags(连接点)。

tags 里面我有 3 个(相关的)列: idnamealiasalias 列包含早期 同义 标签的 ID(如果存在)(否则为 NULL)。 没有环或链:所有同义标签都包含相同的别名。 例如:

tags

id    |    name    |    alias
------------------------------------
------------------------------------
8     |   pencil   |    NULL
------------------------------------
------------------------------------
3072  |   pencils  |      8
------------------------------------
------------------------------------
3073  | blue pencil|      8
------------------------------------

(标签 必须 具有此功能,因为它们是用户键入的。)

现在,假设我要搜索包含标签 pencilscats 的所有文章。我希望他们包括 ALL 其他同义标签(pencilblue penciltomcat, 小猫等).

我想出的查询如下:

SELECT * FROM `articles` WHERE id IN
    (SELECT article_id FROM `articles_tags` WHERE id IN
         (SELECT id FROM `tags` WHERE COALESCE(alias, id) IN
            (SELECT id FROM `tags` WHERE name IN
                ("pencils", "cats")
        )
    )
)

我相信它是有效的,但我认为可能有更好的方法然后使用 4 个子查询 INs(性能是关键,因为这将是一个常见的搜索执行).

我非常感谢有关更好解决方案的指导。

谢谢。

奖金问题

如果我需要查找的文章只包含所搜索的标签,该怎么办?

也就是说,选择所有只有标签 "cats" 和 "dogs" 的文章将 return 篇标签只有 ("cats"), ("dogs"), 或 ("cats", "dogs").

编辑: table 结构:

id    |    name    |    alias
------------------------------------
...
------------------------------------
8     |   pencil   |    NULL
------------------------------------
...
------------------------------------
3072  |   pencils  |      8 (pencil)
------------------------------------
------------------------------------
3073  | blue pencil|      8 (pencil)
------------------------------------
------------------------------------
6088  |    cats    |     NULL
------------------------------------
------------------------------------
7098  |    dogs    |     NULL
------------------------------------
------------------------------------
7099  |  kittens   |     6088 (cats)
------------------------------------
------------------------------------
7102  |  chiwawa   |     7098 (dogs)
------------------------------------

文章

id    |        title       |    content
----------------------------------------
...
----------------------------------------
1     |   I love writing   |    ...
----------------------------------------
...
----------------------------------------
42    |Tips for pet owners |    ...
----------------------------------------
----------------------------------------
108   |  Drawing my dog    |    ...
----------------------------------------

articles_tags

id    |      article_id      |    tag_id
------------------------------------
...
------------------------------------
19    |   1(I love writing)  |    3072 (pencils)
------------------------------------
------------------------------------
21    |   1(I love writing)  |    3010 (poetry)
------------------------------------
------------------------------------
22    |   1(I love writing)  |    123  (books)
------------------------------------
------------------------------------
34    | 42(Tips for pet ...) |    6088 (cats)
------------------------------------
------------------------------------
35    | 42(Tips for pet ...) |    7098 (dogs)
------------------------------------
...
------------------------------------
78    | 108(Drawing my dog) |     7098 (dogs)
------------------------------------
------------------------------------
78    | 108(Drawing my dog) |     8    (pencil)
------------------------------------

期望输出:

搜索 chiwawacats 应该会得到 "Tips for pet owners"、"Drawing my dog"。 搜索 pencildogs 应该得到 "I love writing"、"Drawing my dog".

第二部分(奖金): 搜索 chiwawakittens 应该得到 ONLY "Tips for pet owners",而 NOT "Drawing my dog" 因为它还有一个 pencils 标签。

抱歉文字太多了。

您可以通过以下方式获取文章 ID:

select ata.article_id
from articles_tags ata join
     tags t
     on ata.tag_id = t.id
where t.name in ('pencil', 'cats')
group by ata.article_id
having count(distinct t.name) = 2;

对于你的第二个问题,你可以使用条件聚合。这是一种方法:

select ata.article_id
from articles_tags ata join
     tags t
     on ata.tag_id = t.id
where t.name in ('pencil', 'cats')
group by ata.article_id
having sum(t.name = 'pencil') > 0 and sum(t.name = 'cats') > 0;

如果需要,您可以加入 articles 以获取更多列。

这里基本上有两个问题...第一个是关系划分 - 选择适用于父记录的标签,这本身可能有点有趣。这里有两个相关链接:

https://www.simple-talk.com/sql/t-sql-programming/divided-we-stand-the-sql-of-relational-division/(塞尔科)

这个问题的答案提供了大约十几种不同的查询方式(以及使用 PostgreSQL 的性能基准)。内容丰富:

How to filter SQL results in a has-many-through relation

至于 "pencil vs. pencils" 类型标签,我建议通过应用程序输入来处理。创建一个 "filter" 种类,将标签映射到其他固有标签。因此,当有人输入 "pencils" 标签时,它会自动 a) 将其过滤为 "pencil",或 b) 同时输入铅笔和铅笔。这对于像 "blue pencil" 这样的东西特别有用。鉴于上述关系划分问题的相对复杂性,我认为如果 "blue pencil" 同时添加 "pencil" 和 "blue pencil"(也许"blue") 标签导入数据库。

在某种程度上,逻辑应该成为应用程序域的一部分,而不是数据库。我相信这是一个很好的例子。否则,您可能会冒着将数据库引擎强制执行其并非真正设计用于执行的操作的风险,这一点也不好玩。