【问题标题】:Relational database query question关系数据库查询题
【发布时间】:2011-05-18 20:39:45
【问题描述】:

我有一个关于我想针对 PostgreSQL 表执行的特定查询的问题。尽管我欢迎对我使用的表格方案提出批评,但我会更加欣赏对我实际问题的回答!

我正在使用 uuid-ossp postgresql-contrib 模块并具有以下表结构:

       Column        |            Type             | Modifiers | Storage  | Description
---------------------+-----------------------------+-----------+----------+-------------
 revision_id         | uuid                        | not null  | plain    |
 document_id         | uuid                        | not null  | plain    |
 user_id             | uuid                        | not null  | plain    |
 datetime_edited     | timestamp without time zone | not null  | plain    |
 contents            | text                        | not null  | extended |
Indexes:
    "document_pkey" PRIMARY KEY, btree (revision_id)

这个想法是:

  • 一个文档可能有一个或多个修订版。不删除修订。为了更新文档,插入了一个新行,其中包含新的 revision_id 但相同的 document_id
  • revision_id 在所有文档的所有修订版中都是唯一的。
  • contents 是代表文档的数据块,user_id 标识谁更新了文档。

我正在努力想出一个查询,该查询返回特定用户创建的所有文档的所有最新修订。我知道我可以做到,例如:

select * from document where user_id = '6a2aabc417b34ef99b14b10eaa8e9313';

但这会返回所有文档。如何向下钻取并要求按document_idLIMIT 1 进行分组,并根据datetime_edited 返回最新的revision_id

编辑:由于一个文档可以有一个或多个修订版,我在说“所有文档由用户创建”时一直含糊其辞。 创建我的意思是用户对文档贡献了一个或多个修订,即用户编辑文档的地方至少有一个修订。

这样的事情甚至可以在一个查询中实现,还是我需要多次访问数据库才能实现这一点?

编辑:revision_id 不是单调递增的。这是一个随机的 UUID。因此,max(revision_id) != max(datetime_edited)。

【问题讨论】:

  • 更好的方法可能是将旧版本放在一个单独的表中,并使用 (doc_id, revision) 复合 PK 并在 document 表中只保留当前版本。那么你的查询就相当直接了,代价是更复杂的更新。
  • 这绝对是一个选择。如果很少查询文档的旧修订版本并且每个文档有很多修订版本,那么从长远来看,它不仅会使查询更简单而且更快。缺点是数据没有完全标准化,但对我来说,简单总是赢。

标签: sql database postgresql set


【解决方案1】:
Select ...
From document As D
    Join    (
            Select D1.document_id, Max( datetime_edited ) As datetime_edited
            From document As D1
            Group By D1.document_id
            ) As LastRevision
        On LastRevision.document_id = D.document_id
            And LastRevision.datetime_edited = D.datetime_edited
Where Exists    (
                Select 1
                From document As D2
                Where D2.document_id = D.document_id
                    And D2.user_id = '6a2aabc417b34ef99b14b10eaa8e9313'
                )

另一种形式:

Select ...
From document As D
    Join    (
            Select D1.document_id, Max( datetime_edited ) As datetime_edited
            From document As D1
            Group By D1.document_id
            ) As LastRevision
        On LastRevision.document_id = D.document_id
            And LastRevision.datetime_edited = D.datetime_edited
    Join    (
            Select D2.document_id
            From document As D2
            Where D2.user_id = '6a2aabc417b34ef99b14b10eaa8e9313'
            Group By D2.document_id
            ) As UserDocs
        On UserDocs.document_id = D.document_id

【讨论】:

  • 你是怎么想出来的?我可以阅读什么来编写这样的查询?这让我大吃一惊,但我只是将第一个查询复制粘贴到我的psql 提示符中,它可以正常工作......我不配。
  • @Asymptote - 我将问题分解为组成部分。第一部分是确定“最新版本”。下一个部分是过滤用户对其进行修订的文档,这可以通过 Exists 子句、Join 或 In 子句(我没有显示)来完成。然后把它们放在一起。
  • 啊,我明白了...我正在复制粘贴每个 JOIN 中的各个语句,我正在赶上。谢谢!
  • @Asymptote - 我看不到第二个查询如何返回所有文档行,除非相关用户对每个文档都有修订。如果在第二个查询中,您注释掉第一个连接,您应该只获取给定用户有修订的文档。如果您评论第二个加入,您将获得每个文档的最新修订版(包括给定用户没有修订版的那些)。
  • @Asymptote - 顺便说一句,我假设您不会遇到这样的情况:同一个 document_id 具有相同的 datetime_edited 值的两个修订版。这种情况需要我们找到一种方法来确定哪个是最新的。因此,对 document_id、datetime_edited 的唯一约束是一个好主意。
【解决方案2】:

获取每个文档的最高修订 id,然后选择那些文档:

select *
from document
where revision_id in (
  select max(revision_id)
  from document
  where user_id = '6a2aabc417b34ef99b14b10eaa8e9313'
  group by document_id
)

更新:

由于版本 id 不是递增的,因此您必须选择 document_id 和它是最新的 datetime_edited,然后加入到文档表中:

select d.*
from document d
inner join (
  select document_id, max(datetime_edited) as datetime_edited
  from document
  where user_id = '6a2aabc417b34ef99b14b10eaa8e9313'
  group by document_id
) x on x.document_id = d.document_id and x.datetime_edited = d.datetime_edited

【讨论】:

  • revision_id 不是单调递增的。这是一个随机的 UUID。因此,max(revision_id) != max(datetime_edited)
  • @Asymptote:我明白了。然后,您必须获取每个文档的最新日期,并加入文档表。我在上面添加了更新。
  • 我认为这里的第二个版本(JOIN to subquery)可能是这里答案的最佳表现。
猜你喜欢
  • 2016-06-10
  • 1970-01-01
  • 1970-01-01
  • 2022-11-17
  • 1970-01-01
  • 2016-05-25
  • 2021-03-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多