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。)我最终得到了不同的解决方案,但您的回答给了我一些启发。谢谢