Mysql 优化连接查询以从A获取数据,条件为B,按B排序
我用以下表格建立了项目间相似性矩阵:Mysql 优化连接查询以从A获取数据,条件为B,按B排序,mysql,join,Mysql,Join,我用以下表格建立了项目间相似性矩阵: items (id, ...) (Primary key `id`) similarities (item1_id, item2_id, similarity) (Index on `item1_id` and `item2_id`) 相似性表包含具有相似性索引的ID对,即: item1_id item2_id similarity 1 2 0.3143 2 3 0.734 为了高效存储
items (id, ...) (Primary key `id`)
similarities (item1_id, item2_id, similarity) (Index on `item1_id` and `item2_id`)
相似性
表包含具有相似性索引的ID对,即:
item1_id item2_id similarity
1 2 0.3143
2 3 0.734
为了高效存储,省略了“反向对”,即只有一对(1,2),没有冗余对(2,1)。这意味着项目的外键可以是item1\u id
或item2\u id
现在我想找到与一堆其他项目相似的项目,按相似性降序排序。我正在使用此查询:
SELECT `Item`.*
FROM `items` AS `Item`
LEFT JOIN `similarities` AS `Similarity`
ON (`Item`.`id` = `Similarity`.`item1_id`
AND `Similarity`.`item2_id` IN (1, 2, 3, ...))
OR (`Item`.`id` = `Similarity`.`item2_id`
AND `Similarity`.`item1_id` IN (1, 2, ,3, ...))
WHERE `Similarity`.`item1_id` IN (1, 2, 3, ...)
OR `Similarity`.`item2_id` IN (1, 2, 3, ...)
GROUP BY `Item`.`id`
ORDER BY `Similarity`.`similarity` desc
SELECT `Item`.*
FROM `items` AS `Item`
JOIN (
SELECT `item1_id` AS `id`, `similarity`
FROM `similarities`
WHERE `similarities`.`item2_id` IN (1, 2, 3, ...)
UNION
SELECT `item2_id` AS `id`, `similarity`
FROM `similarities`
WHERE `similarities`.`item1_id` IN (1, 2, 3, ...)
) AS `SimilarityUnion` ON `SimilarityUnion`.`id` = `Item`.`id`
GROUP BY `SimilarityUnion`.`id`
ORDER BY `SimilarityUnion`.`similarity` DESC
不过速度非常慢,大约100000个项目和大约30000个相似性对需要4-5秒。看来加入成本非常高。以下是查询EXPLAIN
ed:
select_type table type possible_keys key key_len ref rows Extra
SIMPLE Similarity index_merge item1_id,item2_id item1_id,item2_id 110,110 NULL 31 Using sort_union(item1_id,...
SIMPLE Item ALL PRIMARY NULL NULL NULL 136600 Using where; Using join buffer
我能做些什么来加快速度?最糟糕的情况是,我会在两个单独的查询中执行此操作,但如果可能的话,我更喜欢一个联接查询。我实际上没有尝试此操作,但它可能为您指明了正确的方向。其思想是对(唯一)id的
联合
、来自相似性
的相似性对生成一个临时结果,然后将项目与之连接
SELECT Item.*, s.other_item_id, s.similarity
FROM items AS Item
JOIN
(
SELECT item1_id AS id, item2_id AS other_item_id, similarity FROM similarities
UNION
SELECT item2_id AS id, item1_id AS other_item_id, similarity FROM similarities
) AS s ON s.id = items.id
WHERE items.id IN (1, 2, 3, ...)
ORDER BY s.similarity DESC;
在原始查询中,您不需要在
JOIN
条件和WHERE
子句中限制ID的相似性
。我想知道两次连接到items表是否比两次查询的性能更好。
请原谅这句话的psuedo代码ish SELECT部分-我认为您实际上需要为每个字段值提供一个案例
SELECT
CASE WHEN `Item2`.`id` IS NULL THEN
`Item1`.`id`
ELSE `Item2`.`id`
END,
SELECT
CASE WHEN `Item2`.`id` IS NULL THEN
`Item1`.`name`
ELSE `Item2`.`name`
END,
SELECT
CASE WHEN `Item2`.`id` IS NULL THEN
`Item1`.`description`
ELSE `Item2`.`description`
END,
[and so on]
FROM `items` AS `Item1`
LEFT OUTER JOIN `similarities` AS `Similarity`
ON (`Item1`.`id` = `Similarity`.`item1_id`
RIGHT OUTER JOIN `items` AS `Item2`
ON (`Item2`.`id` = `Similarity`.`item2_id`
WHERE `Similarity`.`item1_id` IN (1, 2, 3, ...)
OR `Similarity`.`item2_id` IN (1, 2, 3, ...)
ORDER BY `Similarity`.`similarity` desc
多亏了这些灵感,我最终提出了以下问题:
SELECT `Item`.*
FROM `items` AS `Item`
LEFT JOIN `similarities` AS `Similarity`
ON (`Item`.`id` = `Similarity`.`item1_id`
AND `Similarity`.`item2_id` IN (1, 2, 3, ...))
OR (`Item`.`id` = `Similarity`.`item2_id`
AND `Similarity`.`item1_id` IN (1, 2, ,3, ...))
WHERE `Similarity`.`item1_id` IN (1, 2, 3, ...)
OR `Similarity`.`item2_id` IN (1, 2, 3, ...)
GROUP BY `Item`.`id`
ORDER BY `Similarity`.`similarity` desc
SELECT `Item`.*
FROM `items` AS `Item`
JOIN (
SELECT `item1_id` AS `id`, `similarity`
FROM `similarities`
WHERE `similarities`.`item2_id` IN (1, 2, 3, ...)
UNION
SELECT `item2_id` AS `id`, `similarity`
FROM `similarities`
WHERE `similarities`.`item1_id` IN (1, 2, 3, ...)
) AS `SimilarityUnion` ON `SimilarityUnion`.`id` = `Item`.`id`
GROUP BY `SimilarityUnion`.`id`
ORDER BY `SimilarityUnion`.`similarity` DESC
我真的很想回答这个问题,作为对你帮助的回报。既然点击了,我想我做不到。祝你好运“一组其他项目”是否由(1,2,3,…)表示?谢谢。@Lester是的,这些是我想要获取相似项目的一组项目的ID。所以基本上我的两个独立查询策略合并成了一个查询?:)我试试看。我必须看看如何通过我的DAL来实现这一点。如果你能给出一个期望结果的例子,这会有所帮助。是否要查看给定项目与之类似的其他项目ID?我只想要项目的记录,就像普通的
SELECT*FROM items
。相似性只是条件,我不需要获取它。事实上,我可能会使用LIMIT
来只获取一些“顶级相似”项目。您可以将查询描述为“查找与这些项最相似的10项(1、2、3……)”。好的。。。因此,您将有一个WHERE条件,该条件将结果限制为特定范围内的相似性或其他内容?注释时间重叠…:)这将是一个LIMIT
ed查询,因此需要排序。此外,矩阵中省略了0的相似性,因此只要存在相似性并且按降序排序,就可以了。啊,芬奇,谢谢!)但是,SELECT
语句中的CASE
语句出现语法错误。只需使用selectitem1.*,Item2.*
进行测试,就可以得到非常快的结果。我还得继续玩这个。如果您能改进这一点,我会很高兴。当Item2.id为NULL时,使用大小写测试Item1.id ELSE Item2.id END CASE
会在大小写附近出现语法错误…
(END CASE
语句)顺便说一句。谢谢,我刚刚编辑了一点。每个字段名都有单独的大小写(这只是为了使您的新查询包含与旧查询相同的字段,因为您已经提到了现有的DAL。)我最终得到了不同的解决方案,但您的回答给了我一些启发。谢谢