如何在 postgresql 的单个查询中 select 行和计数行?

How select a row and count row in single query in postgresql?

我在 postgresql 中有一个 table 如下:

 id | chat_id |  content  | time |                 read_times                 
----+---------+-----------+------+-------------------------------------------------------------------------
  1 | chat_10 | content_1 |  t1  | [{"username": "user1", "time": 123}, {"username": "user2", "time": 111}]
  2 | chat_10 | content_2 |  t2  | [{"username": "user2", "time": 1235}]
  3 | chat_10 | content_3 |  t3  | []
  4 | chat_11 | content_4 |  t4  | [{"username": "user1", "time": 125}, {"username": "user3", "time": 121}]
  5 | chat_11 | content_5 |  t5  | [{"username": "user1", "time": 126}, {"username": "user3", "time": 127}]

注意t1 < t2 < t3 < t4 < t5

每个用户阅读一条消息后,我们将其注册在read_times列(user2在时间1235阅读了id为2的消息),现在我要获取带有未读计数聊天的用户聊天列表。 user1 结果如下:

 chat_id |  content  |  unread_count
 --------+-----------+--------------
 chat_10 | content_3 |       2
 chat_11 | content_5 |       0

注意: unread_count 是用户未在 caht_id.

中阅读的消息数

是否可以一次查询?

首先,您必须使用 json_array_elements() 函数提取每个 chat_idcontent 的用户名,并使用 FIRST_VALUE() window 函数获取最后一个 content 每个 chat_id.
然后将SUM()window函数与MAX()聚合函数聚合合并,得到列unread_count:

WITH cte AS (
  SELECT t.chat_id, t.content,
         FIRST_VALUE(t.content) OVER (PARTITION BY t.chat_id ORDER BY t.time DESC) last_content,
         (r->>'username') username
  FROM tablename t LEFT JOIN json_array_elements(read_times::json) r ON true
)
SELECT DISTINCT c.chat_id, MAX(c.last_content) "content",
       SUM((MAX((COALESCE(username, '') = 'user1')::int) = 0)::int) OVER (PARTITION BY c.chat_id) unread_count
FROM cte c
GROUP BY c.chat_id, c.content
ORDER BY c.chat_id

参见demo