在MySQL中查询多个多对多关联

时间:2011-10-06 16:45:35

标签: mysql

背景

我的程序在MySQL数据库中存储一系列对象,一组标签以及标签和对象之间的多对多关联。为了让您了解结构:

CREATE TABLE objects (
    object_id INT PRIMARY KEY,
    ...
);
CREATE TABLE tags (
    tag_name VARCHAR(32) NOT NULL
);
CREATE TABLE object_tags (
    object_id INT NOT NULL,
    tag_name VARCHAR(32) NOT NULL,
    PRIMARY KEY (object_id, tag_name)
);

问题

我希望能够查询用给定集合中的所有标记标记的所有对象。举个例子,假设我有一棵活树,一朵死花,一只猩猩和一艘船作为我的物品,我想查询所有标记为生活植物。我希望收到一个只包含 tree 的列表,假设这些标签符合对象的特征。

当前解决方案

目前,鉴于标签列表T1,T2,...,Tn,我正在解决以下问题:

  1. object_id表中选择所有object_tagstag_name为T1。
  2. 将{1}的结果加入object_tags表格,然后选择object_id为T2的所有tag_name列。
  3. 再次将{2}的结果与object_tags表格相关联,并选择object_id为T3的所有tag_name列。
  4. 根据需要重复T4,...,Tn。
  5. 将(4)的结果与objects表一起加入,并选择所需对象的其他列。
  6. 在实践中(使用Java),我从第一个标签的查询字符串开始,然后为第二个标签添加/附加字符串部分,依此类推,然后在最后预先添加/附加生成的字符串部分整个查询。只有这样,字符串才会真正传递到PreparedStatement并在服务器上执行。

    编辑:扩展我的示例,使用此解决方案,我将发出以下查询:

    SELECT object_id FROM object_tags JOIN (
        SELECT object_id FROM object_tags WHERE tag_name='living'
    ) AS _temp USING (object_id) WHERE tag_name='plant';
    

    问题

    这个问题有更好的解决方案吗?虽然标签的数量不太可能很大,但我担心这个解决方案的性能,特别是随着数据库的大小增加。此外,读取和维护代码非常困难,尤其是在引入应用程序的其他问题/约束时。

    我愿意接受任何级别的建议,尽管此时语言(MySQL和Java)不是变量。

1 个答案:

答案 0 :(得分:1)

我不知道此解决方案的性能,但您可以通过在MySql中使用模式匹配来匹配一组管道分隔的标记(或任何分隔符)来简化。这是我以前使用过的标记表类似应用程序的解决方案(@match将是一个由Java代码传入的变量,我已经为示例编写了一个值):

set @match = 'living|plant';
set @numtags =
    length(@match) - length(replace(@match, '|', '')) + 1;

select * from objects o
where @numtags =
(
    select count(*) from object_tags ot
    where concat('|',@match,'|')
        like concat('%|',ot.tag_name,'|%')
    and ot.object_id = o.object_id
)

这是一个有效的演示:http://sqlize.com/0vP6DgQh0j