背景
我的程序在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,我正在解决以下问题:
object_id
表中选择所有object_tags
列tag_name
为T1。object_tags
表格,然后选择object_id
为T2的所有tag_name
列。object_tags
表格相关联,并选择object_id
为T3的所有tag_name
列。objects
表一起加入,并选择所需对象的其他列。在实践中(使用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)不是变量。
答案 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