构造用于检索标记项的SQL查询

时间:2013-12-16 21:26:23

标签: mysql sql

在下面的示例中,我有三个MySQL(InnoDB)表,其中包含有关饮料及其特征的信息。

  1. 第一张表:“饮料”含有多种不同的饮料。

  2. 下表是“标签”,其中包含饮料可以具有的特征。

  3. 饮料可以有多个标签,在“已标记”表中定义。

  4. 表:饮料

    +------------------------+
    |INT id | VARCHAR name   |
    +------------------------+
    |  1    |  coca-cola     |
    +------------------------+
    |  2    |  water         |
    +------------------------+
    |  3    |  mineral-water |
    +------------------------+
    

    表格:标签

    +-------------------------+
    |INT id | VARCHAR tagName |
    +-------------------------+
    |  1    |  clear          |
    +-------------------------+
    |  2    |  carbonated     |
    +-------------------------+
    |  3    |  flavoured      |
    +-------------------------+
    

    表格:已标记

    +------------------------------------+
    |INT id | INT beverageId | INT tagId |
    +------------------------------------+
    |  1    |  1             |  2        | (coca-cola is carbonated)
    +------------------------------------+
    |  2    |  1             |  3        | (coca-cola is flavoured)
    +------------------------------------+
    |  3    |  2             |  1        | (water is clear)
    +------------------------------------+
    |  4    |  3             |  1        | (mineral-water is clear)
    +------------------------------------+
    |  5    |  3             |  2        | (mineral-water is carbonated)
    +------------------------------------+
    
    The fields "beverageId" and "tagId" are foreign keys to the table "beverages".
    

    我想构建一个允许我提供任意数量标签的查询,结果将是具有所有这些标签的所有饮料ID。 查询将包含任意数量的标记。

    • 如果我提供标签ID“2”(碳酸)和标签ID“3”(味道),我会得到饮料ID“1”(可口可乐)。
    • 如果我提供标签ID“1”(清除),我将取回饮料ID“2”(水)和id“3”(矿泉水)。
    • 如果我提供标签“2”(碳酸),我将取回饮料ID“1”(可口可乐)和饮料ID“3”(矿泉水)。

    所以问题是;

    1. 为此目的,桌面设计是否合适?
    2. 如何有效地构建此SQL查询?
    3. 谢谢

1 个答案:

答案 0 :(得分:0)

以下结构为您可以做的事情提供了一些指导。以下是一种方法,其中标记ID作为单独的变量提供:

select beverageid
from tagged t
group by beverageid
having sum(tagid = TAGID1) > 0 and
       sum(tagid = TAGID2) > 0 and
       . . . ;

这样的查询可能有点难以产生,因为having子句中的子句数量不同。

这是另一种假设标签位于逗号分隔列表中的方法:

select beverageid
from tagged t
where find_in_set(tagid, TAGLIST) > 0
group by beverageid
having count(distinct tagid) = 
            (length(TAGLIST) - length(replace(TAGLIST, ',', '')) + 1;

这里唯一的混淆是计算标签的数量。

如果您正在动态构建查询,那么当tagid上有索引时,以下内容应该表现最佳:

select beverageid
from tagged t
where tagid in (TAGLIST)
group by beverageid
having count(distinct tagid) = LENGTH OF TAGLIST;