You seem to be misinterpreting a part of my advice to your previous question:
so I can not Truncate and rename the temp_tables since the system
should be on all the time.
There was no renaming involved. After TRUNCATE
you run an INSERT
. The only blocking operation is the TRUNCATE
. I quote the manual:
TRUNCATE
acquires an ACCESS EXCLUSIVE
lock on each table it
operates on, which blocks all other concurrent operations on the
table. (...) If concurrent access to a table is required, then the
DELETE
command should be used instead.
INSERT
may still work, if it is not time-critical and allowed to wait until the lock is released. TRUNCATE
is usually very fast, if you run it in a separate transaction, it should only block for a couple of milliseconds. Note: separate transaction, not separate session! Your temporary table lives and dies with the session.
The drawback of a separate transaction: if you lose your session before you are able to re-insert from the temporary table, you loose data. To be sure, you could use a plain table instead. (Which would offer the alternative path to drop the old and rename the new table.)
Either way, your updated question makes clear that you want to run this repeatedly, accumulating old rows in the same table. In this case, TRUNCATE
is not a good option anyway. You can always just use a plain DELETE
. Considerably slower with big tables, but concurrent INSERT
is not blocked at all. The autovacuuming daemon will have to do some more work, too.
It's mostly a trade-off between speed and security.
SQL
The DELETE
command for the slow and sure method could look like this:
DELETE FROM TABLE tbl t
USING (
SELECT created_at FROM tbl WHERE created_at < now() - (interval '3 month')
) d
LEFT JOIN (
SELECT min(created_at) AS created_at
FROM tbl
GROUP BY date_trunc('minute', created_at)
WHERE created_at < now() - (interval '3 month')
) x USING (created_at)
WHERE x.created_at IS NULL
AND d.created_at = t.created_at;
You need to do a left outer join
on the generate_series
with an ON
clause that matches the date of each report.
Without a schema or sample data it's a bit fiddly to cook up an example modification of your actual query.
Here's a simplified example to show you how it works:
CREATE TABLE sparse_dates(
bakedgood text primary key,
firstbaked date not null
);
INSERT INTO sparse_dates (bakedgood, firstbaked) VALUES
('tart', '2012-02-01'),
('baguette', '2012-02-01'),
('cookie', '2012-03-01'),
('macaron', '2012-08-01');
To get the number of baked goods first baked on a given month:
SELECT monthtimestamp, count(bakedgood)
FROM generate_series(
(SELECT min(firstbaked) FROM sparse_dates),
(SELECT max(firstbaked) FROM sparse_dates),
INTERVAL '1' MONTH)
AS monthtimestamp
LEFT OUTER JOIN sparse_dates ON (monthtimestamp = firstbaked)
GROUP BY monthtimestamp;
with output like:
monthtimestamp | count
------------------------+-------
2012-02-01 00:00:00+08 | 2
2012-03-01 00:00:00+08 | 1
2012-04-01 00:00:00+08 | 0
2012-05-01 00:00:00+08 | 0
2012-06-01 00:00:00+08 | 0
2012-07-01 00:00:00+08 | 0
2012-08-01 00:00:00+08 | 1
(7 rows)
Best Answer
date_trunc
truncates the date to the specified part. You needextract