Here is the structure of the 2 tables:
This is the "main" table :
CREATE TEMP TABLE final_table (
idx INTEGER,
pids INTEGER[],
stats INTEGER[]
)
INSERT INTO final_table
VALUES
(2733111, '{43255890, 8653548}'::int[], '{4, 5}'::int[]),
(2733112, '{54387564}'::int[] , '{6}'::int[] ),
(2733113, '{}'::int[] , '{}'::int[] )
This is the data from temp table :
CREATE TEMP TABLE aggreg (
idx INTEGER,
pid INTEGER,
count INTEGER
)
VALUES
(2733111, 21854997, 2),
(2733111, 21854923, 10),
(2733112, 12345689, 3),
(2733113, 98765348, 11),
(2733111, 43255890, 4),
(2733112, 54387564, 6);
The goal is to UPDATE the first table (final_table) with the data from the temporary table.
The difficulty lies in the fact that, in a classic update, the same line will be called several times and therefore will only have the last result in update. It is thus necessary to pass by a sub SELECT
in the FROM
of the UPDATE
and it is there that I am stuck.
I can't seem to find the right syntax in the SELECT sub to do this.
Additionally, in the real table some rows may already contain data in the pids
and stats
columns. The purpose of this UPDATE
is to add only the pid
and their corresponding count
.
The last query I tested (and didn't work) is the following:
UPDATE final_table AS ft
SET
stats = ag2.stats || (ag2.stats - ft.stats),
pids = ag2.pids || (ag2.pids - ft.pids)
FROM (
SELECT
ag.idx,
array_agg(ag.pid) AS pids,
array_agg(ag."count") AS stats
FROM
aggreg AS ag
INNER JOIN
final_table AS ft
ON
ag.idx = ft.idx
GROUP BY
ag.idx
) AS ag2
WHERE
ag2.idx = ft.idx
Also I use the intarray
extension to do the array - array
operation.
Based on the example tables, I expect the following result:
idx | pids | stats |
---|---|---|
2733111 | {43255890, 8653548, 21854997, 21854923} | {4, 5, 2, 10} |
2733112 | {54387564, 12345689} | {6, 3} |
2733113 | {98765348} | {11} |
Thanks in advance for your help!
Instead of using a JOIN
operation between the two tables and using it inside the UPDATE statement, you can craft your arrays from the "aggreg" table and use it within the FROM ... WHERE ...
clauses of the UPDATE statement as follows.
In order to deal with existing elements inside the arrays, you should unnest your previous data first, merge it with your aggreg data, and update your "final_table" accordingly.
WITH full_data AS (
SELECT * FROM aggreg
UNION
SELECT ft.idx,
t.pids,
t.stats
FROM final_table ft
CROSS JOIN UNNEST(ft.pids, ft.stats) AS t(pids, stats)
), aggreg_data AS (
SELECT idx,
ARRAY_AGG(pid ORDER BY pid) AS pids,
ARRAY_AGG(count ORDER BY pid) AS counts
FROM full_data
GROUP BY idx
)
UPDATE final_table
SET pids = cte.pids,
stats = cte.counts
FROM aggreg_data cte
WHERE final_table.idx = cte.idx;
Output:
idx | pids | stats |
---|---|---|
2733111 | [21854997,21854923,8653548,43255890] | [2,10,5,4] |
2733112 | [12345689,54387564] | [3,6] |
2733113 | [98765348] | [11] |
Check the demo here.
Note: The ORDER BY
clause inside the ARRAY_AGG
function will ensure that correspondence between arrays is kept. If you need to have the exact original order, the query may become a little more complex and heavy, as that would require to involve a rowid (to be computed with ROW_NUMBER
) and a further subquery.