SQL:选择给定用户以前看不见的记录

时间:2013-02-22 13:05:19

标签: sql postgresql activerecord

我有一个带有类似

模式的postgres数据库
CREATE TABLE authors (
    id integer NOT NULL
);

CREATE TABLE posts (
    id integer NOT NULL,
    author_id integer,
    text text
);

CREATE TABLE comments (
    id integer NOT NULL,
    post_id integer,
    ordinal integer DEFAULT 0,
    author_id integer
);

鉴于具体author_id,我希望能够选择一批20个帖子:

  1. 不包含该作者发表评论的帖子。
  2. 不是该作者的帖子。
  3. 是否包含该帖子的10条最新评论。
  4. 我认为#1点正在扼杀我的查询时间。到目前为止,我使用内部查询来解决这一点,比如

    SELECT * from posts
    WHERE posts.id NOT IN (
       SELECT posts.id FROM posts JOIN comments ON posts.id = comments.post_id)
    

    随着我的数据库的增长,这个查询变得更糟。我不擅长SQL;有一个更好的方法吗?我正在使用ActiveRecord,如果这有助于/伤害。

3 个答案:

答案 0 :(得分:0)

用这个替换你的查询

SELECT * from posts p, comments c
WHERE posts.id <> c.posts.id;

希望有所帮助

答案 1 :(得分:0)

我的经验是“不在”中查询时间比任何事情都要多。替代品“不存在”或者,如果postgresql支持这种语法。

where somefield in 
(select somefield
 from etc
 except
 select somefield
 from etc)

有时使用减号这个词而不是除外。

答案 2 :(得分:0)

调试显示的查询

您提出的查询效率不必要。首先,您可以通过在子查询中省略冗余JOIN来简化:

SELECT *
FROM   posts
WHERE  posts.id NOT IN (SELECT post_id FROM comments)

这可以改写为LEFT JOIN / IS NULLNOT EXISTS半反连接,我期望其表现最佳:

SELECT *
FROM   posts p
WHERE  NOT EXISTS (SELECT 1 FROM comments c WHERE c.post_id = p.id)

完整查询

你的观点3.不清楚:

  

是否包括该帖子的10条最新评论。

忽略那个,查询可能是:

SELECT *
FROM   posts p
WHERE  p.author_id <> $author_id -- "not a post by that author"
AND    NOT EXISTS (
   SELECT 1
   FROM   comments c
   WHERE  c.author_id = $author_id
   AND    c.post_id = p.id) -- exclude "post that has a comment by that author"
--  ORDER  BY ??undefined??, maybe id DESC
LIMIT  20

posts.author_id应定义为NOT NULL,否则您必须使用:

p.author_id IS DISTINCT FROM $author_id 

对于大量行,索引是性能的关键。我希望posts.id成为主键,因此它会自动编入索引。如果您还没有这个多列索引,请添加:

CREATE INDEX comments_pa_idx ON comments (post_id, author_id);