【问题标题】:PostgreSQL select master records and display details columns for specific recordPostgreSQL 选择主记录并显示特定记录的详细信息列
【发布时间】:2014-05-24 19:18:09
【问题描述】:

首先我必须为糟糕的主题文本道歉,但我没有更好的主意。也许正因为如此,我在搜索网络时没有找到解决方案。

我有 2 个表:master 和 details,当然有外键可以掌握。我想从 master 中获取所有行和所有字段,以及从详细信息中获取 master 中每一行的特定记录的字段(比如说某些列的顺序)。

我试过这样:

SELECT master.id, master.title, temp2.master_id, temp2.datetime, temp2.title_details
FROM master
LEFT JOIN (SELECT master_id, datetime, title AS title_details FROM details ORDER BY datetime DESC) temp2 ON temp2.master_id=master.id

//and this:
SELECT master.id, master.title, 
(SELECT master_id, datetime, title AS title_details FROM details WHERE master.id=details.master_id ORDER BY datetime DESC) 
FROM master
//but of course: subquery must return only one column

但这不起作用。

举例说明我想做什么:

Master:
id  title
1   test
2   blab
3   something

Details:
id  master_id   datetime    title
1   1           2004-...    t: 1.1
2   1           2005-...    t: 2.1
3   1           2006-...    t: 3.1
4   2           2004-...    t: 4.2
5   2           2005-...    t: 5.2
6   3           2006-...    t: 6.3  

Expected output:
id  title       datetime    title_details
1   test        2006-...        t: 3.1
2   blab        2005-...        t: 5.2
3   something   2006-...        t: 6.3

因为我很难解释我需要什么,所以这是我不想做的 PHP 代码(从头开始):

$q = Database::$DB->prepare("SELECT * FROM master");
$q2 = Database::$DB->prepare("SELECT * FROM details WHERE master_id=? ORDER BY datetime DESC LIMIT 1");
$rows = $q->execute();
foreach ($rows as $row)
{
    $q2->execute($row->id);
    $row->AdditionalFields = $q2->fetch();
} 

换句话说,我不想遍历所有主行并详细选择特定 ONE 记录(最后 - 按日期时间排序)的数据。

我尝试了所有不同的 UNION、JOINS 和 SUBQUERIES,但都没有成功。

已编辑(对不同答案发表评论):

实际的查询是:

SELECT DISTINCT ON (todo_topics.id) todo_topics.id, todo_topics.user_id, users.username AS author, todo_topics.title, todo_topics.datetime_created, todo_topics.version, todo_topics.todo_status_id, todo_statuses.icon_image, 
    todo_topics.version_status_changed, todo_posts.text, u.username AS last_poster, todo_posts.user_id as last_poster_id
FROM todo_topics
LEFT JOIN todo_statuses ON todo_statuses.id = todo_topics.todo_status_id
LEFT JOIN users ON users.id = todo_topics.user_id
LEFT JOIN todo_posts ON todo_topics.id=todo_posts.todo_topic_id
LEFT JOIN users u ON u.id = todo_posts.user_id
ORDER BY todo_topics.id, todo_posts.datetime_created  DESC

“总运行时间:0.863 毫秒”

    SELECT
      todo_topics.id, todo_topics.user_id, users.username AS author, todo_topics.title, todo_topics.datetime_created, todo_topics.version, todo_topics.todo_status_id, todo_statuses.icon_image, 
        todo_topics.version_status_changed, todo_posts.text, u.username AS last_poster, todo_posts.user_id as last_poster_id
    FROM
      todo_topics
      LEFT JOIN todo_statuses ON todo_statuses.id = todo_topics.todo_status_id
    LEFT JOIN users ON users.id = todo_topics.user_id

    INNER JOIN
    (
      SELECT
        *,
        ROW_NUMBER() OVER (PARTITION BY todo_topic_id ORDER BY datetime_created DESC) AS ordinal
      FROM
        todo_posts
    )
      AS todo_posts
        ON todo_posts.todo_topic_id = todo_topics.id
    LEFT JOIN users u ON u.id = todo_posts.user_id
    WHERE
      todo_posts.ordinal = 1

“总运行时间:1.281 毫秒”

 SELECT
  todo_topics.id, todo_topics.user_id, users.username AS author, todo_topics.title, todo_topics.datetime_created, todo_topics.version, todo_topics.todo_status_id, todo_statuses.icon_image, 
    todo_topics.version_status_changed, todo_posts.text, u.username AS last_poster, todo_posts.user_id as last_poster_id
FROM
  todo_topics
LEFT JOIN todo_statuses ON todo_statuses.id = todo_topics.todo_status_id
LEFT JOIN users ON users.id = todo_topics.user_id
INNER JOIN
(
  SELECT
    todo_topic_id,
    MAX(datetime_created)  AS max_datetime
  FROM
    todo_posts
  GROUP BY
    todo_topic_id
)
  AS details_lookup
    ON  details_lookup.todo_topic_id = todo_topics.id
INNER JOIN
  todo_posts
    ON  todo_posts.todo_topic_id = details_lookup.todo_topic_id
    AND todo_posts.datetime_created  = details_lookup.max_datetime
LEFT JOIN users u ON u.id = todo_posts.user_id

“总运行时间:1.143 毫秒”

如果有人想知道这个时间对特定硬件意味着什么:

该数据库是实验性的(每个表中有几条记录 -

【问题讨论】:

  • 要进行更有意义的测试,您需要填充表格。不要忘记索引。当表格增长时,这些变得更加更加重要。请按照我的答案中的链接了解有关拟合指数的更多信息。

标签: sql postgresql subquery greatest-n-per-group master-detail


【解决方案1】:
SELECT
  *
FROM
  master
INNER JOIN
(
  SELECT
    *,
    ROW_NUMBER() OVER (PARTITION BY master_id ORDER BY datetime DESC) AS ordinal
  FROM
    details
)
  AS details
    ON details.master_id = master.id
WHERE
  details.ordinal = 1

或者……

SELECT
  *
FROM
  master
INNER JOIN
(
  SELECT
    master_id,
    MAX(datetime)  AS max_datetime
  FROM
    details
  GROUP BY
    master_id
)
  AS details_lookup
    ON  details_lookup.master_id = master.id
INNER JOIN
  details
    ON  details.master_id = details_lookup.master_id
    AND details.datetime  = details_lookup.max_datetime

【讨论】:

    【解决方案2】:

    DISTINCT ON 更简单:

    SELECT DISTINCT ON (m.id)
           m.*, d.datetime, d.title AS title_details
    FROM   master m
    LEFT   JOIN details d ON d.master_id = m.id
    ORDER  BY m.id, d.datetime DESC;
    

    假设master.id 是主键,details.datetimeNOT NULL
    详解:
    Select first row in each GROUP BY group?

    如果datetime 可以为NULL,请小心。在这种情况下,您可能想要NULLS LAST

    【讨论】:

    • 我想你的意思是:SELECT DISTINCT ON (m.id) m.*, d.datetime, d.title AS title_details FROM master m LEFT JOIN details d ON d.master_id = m.id ORDER BY m.id, d.datetime DESC 为什么(如果)你的解决方案比@MatBailie 更好
    • @Makla:正确。应用。排序顺序取决于您要选择的行。我现在在您的 PHP 代码中看到:您想按 datetime 排序。该示例适用于任何一种方式。为什么更好?越来越短。只需使用 EXPLAIN ANALYZE 进行测试,您自己就可以看到,您不必相信我的话。
    • 是的,已确认。
    猜你喜欢
    • 1970-01-01
    • 2014-05-25
    • 1970-01-01
    • 2021-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-30
    • 1970-01-01
    相关资源
    最近更新 更多