SELECT message."id" AS "id"
, message."transactionId" AS "transactionId"
, "ids"
, message"createdAt" AS "createdAt"
FROM message as message
JOIN (
SELECT
"transactionId",
"createdAt",
array_agg(thisMessage.id) as "ids",
min(thisMessage.id::text)::uuid as "id"
FROM message as thisMessage
WHERE thisMessage."groupId" = 'a5086f93-7e62-47ea-9419-6e3800b2a15e'
GROUP BY "transactionId", "createdAt"
ORDER BY createdAt DESC
OFFSET 0 LIMIT 9
) as groupM
ON message."id" = groupM."id"
WHERE
message."groupId" = 'a5086f93-7e62-47ea-9419-6e3800b2a15e'
ORDER BY message."createdAt" DESC
OFFSET 0 LIMIT 3;
Here is the table
| "id" | "groupId" | "transactionId" | "createdAt" |
|---|---|---|---|
| 1 | a5086f93-7e62-47ea-9419-6e3800b2a15e | null | 2024-05-03 14:45:46.881317 |
| 2 | a5086f93-7e62-47ea-9419-6e3800b2a15e | 20d1e6a7-5cca-48bf-8ac2-ba8fb9f38772 | 2024-05-03 15:45:46.881317 |
| 3 | a5086f93-7e62-47ea-9419-6e3800b2a15e | 20d1e6a7-5cca-48bf-8ac2-ba8fb9f38772 | 2024-05-03 15:45:46.881317 |
| 4 | a5086f93-7e62-47ea-9419-6e3800b2a15e | null | 2024-05-03 17:45:46.881317 |
| 5 | a5086f93-7e62-47ea-9419-6e3800b2a15e | null | 2024-05-03 18:45:46.881317 |
Desired result:
| "id" | "transactionId" | "ids" | "createdAt" |
|---|---|---|---|
| 5 | null | null | 2024-05-03 18:45:46.881317 |
| 4 | null | null | 2024-05-03 17:45:46.881317 |
| 2 | a5086f93-7e62-47ea-9419-6e3800b2a15e | {2, 3} | 2024-05-03 15:45:46.881317 |
The main idea is to group rows with transactionId and count groups as one row, but don't aggregate rows with transactionId IS NULL. So from 2 rows with transactionId and 2 rows with null, LIMIT 3 should return one grouped by transactionId, and 2 with null.
But on large tables, it works very slow. How can I improve it?