通过order_num字段查找下一个连续行

时间:2017-05-13 15:22:14

标签: sql database postgresql greatest-n-per-group

我在Postgres 9.5数据库中有4个表格,用于向具有以下表格结构的客户发送通知:

notices

id    name     
--------------
111   notice1  
112   notice2
113   notice3

notice_documents - 单个通知可以包含多个增加order_num的文档。

id    notice_id   name   order_num    
----------------------------------
211   111         doc1   1
212   111         doc2   2
213   111         doc2   3
214   112         doc3   1
215   113         doc4   1
216   113         doc5   2
217   113         doc6   3
218   113         doc7   4

notice_details - 此表已发送通知文件记录。 is_archived = 0表示它处于活动状态,仅考虑这些记录。

id   customer_id   notice_id   notice_document_id   is_archived
1    3133          111         211                  0
2    3133          111         212                  0
3    3134          112         214                  0
4    3135          113         216                  0

customers - 每个客户都有一个notice_id来发送文件。

id   customer_name notice_id
3133 abc           111
3134 xyz           112
3135 pqr           113

所有列都定义为NOT NULL,并且使用FK约束强制执行参照完整性。

我需要为客户提取连续或下一份文件:

  1. 如果我发送了前两个文档(例如order_num = 12),那么下一个文档将是第三个文档(order_num = 3) - 就像示例中的客户3133一样。
  2. 如果我直接发送第二份文件(如order_num = 2,那么下一份文件也将是第三份 - 就像客户3135一样。
  3. 如果最后一个文件已经发送,则不要发送任何文件。
  4. 如果尚未发送任何文件,则发送第一份文件。
  5. 我尝试使用notice_details获取group by notice_id, customer_id表格中的最后一个插入行并获取已发送的order_num,但这不会涵盖所有情况。
    我还尝试跳过那些已经发送过的行,但这些行也没有涵盖这个场景。

    我怎么能管理它?

1 个答案:

答案 0 :(得分:1)

假设notice_documents.id随着order_num单调增加(这会使列order_num出现冗余噪音),这应该满足所有要求:

SELECT DISTINCT ON (c.id, ndo.notice_id)
       c.id AS customer_id, notice_id, ndo.id AS notice_document_id
FROM   customers             c
JOIN   notice_documents ndo USING (notice_id)
WHERE  NOT EXISTS (
   SELECT 1
   FROM   notice_details 
   WHERE  customer_id = c.id
   AND    notice_id   = c.notice_id
   AND    is_archived  -- "consider those records only"
   AND    notice_document_id >= ndo.id
   )
ORDER  BY c.id, notice_id, ndo.id;

它返回的文档的下一个notice_document_id高于为每个客户和通知组合发送的最高文档ID。 (没有任何文件已经发送过。)

如果您有并发访问权限,则需要做更多工作以避免竞争条件。相关:

关于DISTINCT ON

如何排除已从另一个表引用的行:

更简单的单一customer_id (解决问题更新):

SELECT DISTINCT ON (notice_id)
       notice_id, ndo.id AS notice_document_id
FROM   customers        c
JOIN   notice_documents ndo USING (notice_id)
WHERE  c.customer_id = $customer_id  -- input customer here
WHERE  NOT EXISTS (
   SELECT 1
   FROM   notice_details 
   WHERE  customer_id = c.id
   AND    notice_id   = c.notice_id
   AND    is_archived  -- "consider those records only"
   AND    notice_document_id >= ndo.id
   )
ORDER  BY notice_id, ndo.id;