内联查询是个坏主意吗?

时间:2008-11-21 11:16:18

标签: sql postgresql

我有一个包含不同站点上的生成器运行时的表,我想为每个站点选择最新的条目。每台发电机每周运行一次或两次。

我有一个查询会做到这一点,但我想知道它是否是最好的选择。我不禁想到使用WHERE x IN(SELECT ...)是懒惰的,而不是制定查询的最佳方式 - 任何查询。

表格如下:

CREATE TABLE generator_logs (
    id integer NOT NULL,
    site_id character varying(4) NOT NULL,
    start timestamp without time zone NOT NULL,
    "end" timestamp without time zone NOT NULL,
    duration integer NOT NULL
);

查询:

SELECT id, site_id, start, "end", duration 
FROM generator_logs
WHERE start IN (SELECT MAX(start) AS start 
                FROM generator_logs 
                GROUP BY site_id) 
ORDER BY start DESC

没有大量数据,所以我并不担心优化查询。但是,我必须在具有数百万行的表格上做类似的事情(据我所关注的是大表!)并且优化更为重要。

那么有更好的查询,内联查询通常是个坏主意吗?

5 个答案:

答案 0 :(得分:4)

您的查询是否应该相关?即:

SELECT id, site_id, start, "end", duration 
FROM generator_logs g1
WHERE start = (SELECT MAX(g2.start) AS start 
               FROM generator_logs  g2
               WHERE g2.site_id = g1.site_id) 
ORDER BY start DESC

否则,您可能会选择非最新日志,其起始值恰好与其他网站的最新开始日期相匹配。

或者:

SELECT id, site_id, start, "end", duration 
FROM generator_logs g1
WHERE (site_id, start) IN (SELECT site_id, MAX(g2.start) AS start 
                           FROM generator_logs  g2
                           GROUP BY site_id)
ORDER BY start DESC

答案 1 :(得分:1)

我会使用连接,因为它们的表现比“IN”子句要好得多:

select gl.id, gl.site_id, gl.start, gl."end", gl.duration 
from 
    generator_logs gl
    inner join (
        select max(start) as start, site_id
        from generator_logs 
        group by site_id
    ) gl2
        on gl.site_id = gl2.site_id
        and gl.start = gl2.start

另外,Tony pointed out您在原始查询中缺少相关性

答案 2 :(得分:0)

在MYSQL中它可能会有问题,因为Last i Checked它无法有效地优化子查询(即:通过查询重写)

许多DBMS都有基因查询规划器,无论您的输入查询结构如何,它都会做同样的事情。

MYSQL在某些情况下会针对这种情况创建临时表,有时则不会,并且根据具体情况,索引,条件,子查询仍然可以相当快。

有些人抱怨子查询难以阅读,但如果将它们分解为局部变量,它们就完全可以了。

$maxids = 'SELECT MAX(start) AS start FROM generator_logs GROUP BY site_id';
$q ="     
    SELECT id, site_id, start, \"end\", duration 
       FROM generator_logs
       WHERE start IN ($maxids) 
       ORDER BY start DESC
";

答案 3 :(得分:0)

这个问题 - 不仅找到MAX,而且找到相应行的其余部分 - 是一个常见问题。幸运的是,Postgres使用DISTINCT ON

为一个查询提供了一种很好的方法
SELECT DISTINCT ON (site_id)
  id, site_id, start, "end", duration
FROM generator_logs
ORDER BY site_id, start DESC;

DISTINCT ON (site_id)表示"每site_id"返回一条记录。 order by子句确定哪条记录。但请注意,这与您的原始查询略有不同 - 如果同一站点的两条记录具有相同的start,则查询将返回两条记录,而这只返回一条记录。

答案 4 :(得分:0)

查找每组每组 的记录的方法是选择那些在同一组中没有记录的记录具有更高的值:

SELECT id, site_id, "start", "end", duration 
FROM generator_logs g1
WHERE NOT EXISTS (
    SELECT 1
    FROM generator_logs g2
    WHERE g2.site_id = g1.site_id
    AND g2."start" > g1."start"
    );