使用ORDER BY'x'和JOIN,但保留没有'x'值的行

时间:2011-06-14 12:36:48

标签: mysql optimization

这是一个相对复杂问题的简化版本,我和我的同事们无法理解这些问题。

考虑两个表,table_atable_b。在我们的CMS table_a中保存了数据库中存储的所有数据的元数据,table_b有一些更具体的信息,因此为简单起见,我需要titledate列。

目前我们的查询如下:

SELECT * 
FROM `table_a` LEFT OUTER JOIN `table_b` ON (table_a.id = table_b.id)
WHERE table_a.col = 'value'
ORDER BY table_b.date ASC
LIMIT 0,20

table_a有大量行时,这会严重降低。如果更改JOIN RIGHT OUTER JOIN(触发MySQL使用table_b.date上的INDEX集),查询会无限快,但它不会产生相同的结果(因为如果table_b.date没有值,它被忽略了。)

这在我们的CMS中成为一个问题,因为如果用户对日期列进行排序,那么任何没有设置日期的行都会从界面中消失,从而产生令人困惑的UI体验并且难以为日期添加日期缺少它们的行。

是否有解决方案:

  1. 使用table_b。date的INDEX这样做 查询将更好地扩展
  2. 以某种方式保留这些行 table_b没有date的{​​{1}} 设置,以便用户可以输入 数据

2 个答案:

答案 0 :(得分:0)

我将参加第二次ArtoAle的评论。由于order by适用于table_b中缺失行的外部联接中的空值,因此无论如何这些行都将无序。

模拟的外连接是丑陋的部分,所以让我们先看一下。 Mysql没有except,所以你需要用exists来编写查询。

SELECT table_a.col1, table_a.col2, table_a.col3, ... NULL as table_b_col1, NULL as ...
FROM
    table_a
WHERE
    NOT EXISTS (SELECT 1 FROM table_a INNER JOIN table_b ON table_a.id = table_b.id);

应将原始查询UNION ALL作为内部联接。{p>需要UNION_ALL才能保留原始订单。

无论你做什么,这种查询都可能会变慢,因为没有一个索引可以轻易支持“外键不存在”的查询类型。这基本上归结为table_a.id中的索引扫描,其中table_b.id中的相应行具有查找(或者可能是并行扫描)。

答案 1 :(得分:0)

所以我们最终实现了一个不同的解决方案,虽然结果不如使用INDEX,但它仍然提供了大约25%的快速提升。

我们删除了JOIN,而是使用了ORDER BY子查询:

SELECT * 
FROM `table_a`
WHERE table_a.col = 'value'
ORDER BY (
   SELECT date 
   FROM table_b 
   WHERE id = table_a.id
) ASC
LIMIT 0,20