0

我有这个查询:

SELECT p.id, r.status, r.title
FROM page AS p
    INNER JOIN page_revision as r ON r.pageId = p.id AND (
        r.id = (SELECT MAX(r2.id) from page_revision as r2 WHERE r2.pageId = r.pageId AND r2.status = 'active')
        OR r.id = (SELECT MAX(r2.id) from page_revision as r2 WHERE r2.pageId = r.pageId)
    )

它返回每个页面和每个页面的最新活动修订,除非没有可用的活动修订,在这种情况下,它只返回最新修订。

有什么办法可以优化它以提高性能或只是一般的可读性?我现在没有任何问题,但我担心的是,当它进入生产环境(可能有很多页面)时,它的性能会很差。

另外,有什么明显的问题我应该注意的吗?子查询的使用总是困扰着我,但据我所知,没有它们就无法做到这一点。

注意:
条件在 JOIN 而不是 WHERE 子句中的原因是在其他查询中(使用相同的逻辑)我从“site”表到“page”表,如果没有页面存在我仍然希望网站返回。

杰克

编辑:我正在使用 MySQL

4

4 回答 4

2

也许有点重构是为了?

如果您在问题latest_revision_id上添加了一个列,那么pages您的问题将会消失,希望您的页面编辑器中只添加几行。

我知道它没有规范化,但它会简化(并大大加快)查询,有时你必须为了性能而去规范化。

于 2009-02-18T12:46:02.163 回答
2

如果“活动”是按字母顺序排列的第一个,您可以将子查询减少到:

SELECT p.id, r.status, r.title
FROM page AS p
    INNER JOIN page_revision as r ON r.pageId = p.id AND 
        r.id = (SELECT r2.id 
                FROM page_revision as r2 
                WHERE r2.pageId = r.pageId 
                ORDER BY r2.status, r2.id DESC
                LIMIT 1)

否则,您可以将 ORDER BY 行替换为

ORDER BY CASE r2.status WHEN 'active' THEN 0 ELSE 1 END, r2.id DESC

这些都来自我对 SQL Server 的假设,您对 MySQL 的了解可能会有所不同。

于 2009-02-18T12:55:30.330 回答
0

MS SQL 2005+Oracle

SELECT p.id, r.status, r.title
FROM (
  SELECT p.*, r,*,
         ROW_NUMBER() OVER (PARTITION BY p.pageId ORDER BY CASE WHEN p.status = 'active' THEN 0 ELSE 1 END, r.id DESC) AS rn
  FROM page AS p, page_revision r
  WHERE r.id = p.pageId
  ) o
WHERE rn = 1

MySQL可能会成为一个问题,因为子查询不能使用INDEX RANGE SCAN外部查询中的表达式不被认为是常量。

您需要创建两个索引和一个返回最后一页修订的函数以使用这些索引:

CREATE INDEX ix_revision_page_status_id ON page_revision (page_id, id, status);

CREATE INDEX ix_revision_page_id (page_id, id);

CREATE FUNCTION `fn_get_last_revision`(input_id INT) RETURNS int(11)
BEGIN
  DECLARE id INT;
  SELECT r_id
  INTO id
  FROM (
    SELECT r.id
    FROM page_revisions
    FORCE INDEX (ix_revision_page_status_id)
    WHERE page_id = input_id
      AND status = 'active'
    ORDER BY id DESC 
    LIMIT 1
    UNION ALL
    SELECT r.id
    FROM page_revisions
    FORCE INDEX (ix_revision_page_id)
    WHERE page_id = input_id
    ORDER BY id DESC 
    LIMIT 1
  ) o
  LIMIT 1;
  RETURN id;
END;

SELECT po.id, r.status, r.title
FROM (
  SELECT p.*, fn_get_last_revision(p.page_id) AS rev_id
  FROM page p
) po, page_revision r
WHERE r.id = po.rev_id;

这将有效地使用索引来获取页面的最新版本。

PS如果您将代码用于状态并0用于活动,您可以摆脱第二个索引并简化查询。

于 2009-02-18T12:07:08.870 回答
0

您的问题是此问题中描述的特殊情况。

使用标准 ANSI SQL 可以获得的最佳效果似乎是:

SELECT p.id, r.status, r.title
FROM page AS p
INNER JOIN page_revision as r ON r.pageId = p.id 
AND r.id = (SELECT MAX(r2.id) from page_revision as r2 WHERE r2.pageId = r.pageId)

其他方法可用,但取决于您使用的数据库。我不确定它是否可以对 MySQL 进行很大改进。

于 2009-02-18T12:10:40.470 回答