I open a new question, since I cannot find a solution.
I would like to use the system versioning of Hsqldb 2.5.1 in order to be able to replicate in a background task, any change on a table (INSERT, DELETE or UPDATE) occurring over a time interval defined by two timestamp (timestart and timestop).
Assuming this is possible, what would be the three queries to use to find the records changed during the time interval (timestart, timestop) by INSERT, UPDATE and DELETE respectively.
Thank you for your help.
After a lot of research, I found 3 queries that seem to answer my problem.
Any correction is welcome, SQL is not what I know best ...
The replication interval is defined by 2 TIMESTAMP WITHOUT TIME ZONE, because the driver I use is the one provided by the UNO API (OpenOffice / LibreOffice) and the getter/setter (getTimestamp/setTimestamp) does not manage TIMESTAMP WITH TIME ZONE, respectively timestart and timestop.
rowstart: is the column declared as TIMESTAMP GENERATED ALWAYS AS ROW START.
rowend: is the column declared as TIMESTAMP GENERATED ALWAYS AS ROW END.
customerid: is the primary key to the customer table.
To find the records that have been updated:
SELECT current.customerid FROM customer FOR SYSTEM_TIME AS OF timestop + SESSION_TIMEZONE() AS current
INNER JOIN customer FOR SYSTEM_TIME FROM timestart + SESSION_TIMEZONE() TO timestop + SESSION_TIMEZONE() AS previous
ON current.customerid = previous.customerid AND current.rowstart = previous.rowend;
To find the records that have been inserted:
SELECT current.customerid FROM customer FOR SYSTEM_TIME AS OF timestop + SESSION_TIMEZONE() current
LEFT JOIN customer FOR SYSTEM_TIME AS OF timestart + SESSION_TIMEZONE() previous
ON current.customerid = previous.customerid WHERE previous.customerid IS NULL;
To find the records that have been deleted:
SELECT previous.customerid FROM customer FOR SYSTEM_TIME AS OF timestart + SESSION_TIMEZONE() previous
LEFT JOIN customer FOR SYSTEM_TIME AS OF timestop + SESSION_TIMEZONE() current
ON previous.customerid = current.customerid WHERE current.customerid IS NULL;
I do not know if the use of DATABASE_TIMEZONE instead of SESSION_TIMEZONE would be more judicious, free to who wants to confirm ...
I did not have time to test massively, but it works quickly even with a lot of record.
Et voila...
Edit: I just noticed that it is important to use the version hsqldb 2.5.1 because I did not manage to have a correct operation under 2.5.0 ...
Related
I have a simple table, which is queried from my backend every minute.
id (int) | phone_number (string) | start (timedatestamp) | period (string) | occurances (int)
I make an sql query, which runs every minute, and returns the results. It's selects all phone_numbers which start this minute.
SELECT * FROM table
WHERE start >= date_trunc('minute', now()) and
start < date_trunc('minute', now()) + interval '1 minute'
as results
This runs fine, but I need to update the table as well, based on this select results.
There are two parts to this:
For each selected row, I need the occurrences to decrement by 1 and update the database with this
For each selected row, if the periodicity='MONTHLY", I need the start column to change to the date and time exactly a month from now.
Is it possible to do this in one SQL statement? Any help or examples are greatly appreciated :)
Yes and you can do so directly. The only 'twist' is when a column in mentioned in the SET clause Postgres always writes the Rvalue. When you desire to conditionally update a column you set the Rvalue to the existing value when the condition is not meet. See fiddle here.
update atable
set occurances = occurances-1
, start_tm = case when period_txt = 'Monthly'
then now()+interval '1 month'
else start_tm
end
where date_trunc('minute',start_tm) = date_trunc('minute',now());
If I have two tables:
items
Id VARCHAR(26)
CreateAt bigint(20)
Type VARCHAR(26)
expiry
Id VARCHAR(26)
Expiry bigint(20)
The items table contains when the item was created, and what type it is. Then another table, expiry, is a lookup table to say how long certain types should last for. A query is run every day to make sure that items that have expired are removed.
At the moment this query is written in our app, as programming code:
for item in items {
expiry = expiry.get(item.Type)
if (currentDate() - expiry.Expiry > item.CreateAt) {
item.delete()
}
}
This was fine when we only had a few thousand items, but now we have tens of millions it takes a significant amount of time to run. Is there a way to put this into just an SQL statement?
Assuming all date values are actually UNIX timestamps, you could write a query such as:
SELECT * -- DELETE
FROM items
WHERE EXISTS (
SELECT 1
FROM expiry
WHERE expiry.id = items.type
AND items.CreateAt + expiry.Expiry < UNIX_TIMESTAMP()
)
Replace SELECT with DELETE once you're sure that the query selects the correct rows.
If the dates stored are in seconds since the UNIX epoch, you could use this PostgreSQL query:
DELETE FROM items
USING expiry
WHERE items.type = expiry.id
AND items.createat < EXTRACT(epoch FROM current_timestamp) - expiry.expiry;
A standard SQL solution that should work anywhere would be
DELETE FROM items
WHERE items.createat < EXTRACT(epoch FROM current_timestamp)
- (SELECT expiry.expiry FROM expiry
WHERE expiry.id = items.type);
That can be less efficient in PostgreSQL.
Your code is getting slow because you do the join between the tables outside the database.
Second slowing aspect is that you delete the items 1 by 1.
So using the compact delete statements which were provided is the correct solution.
It seems that you are using something like python-sqlalchemy. There the code would be something like:
items.delete().\
where(items.c.type==\
select([expiry.c.id]).\
where(currentDate() - expiry.Expiry > item.c.CreateAt ))
I have a column (ROW_UPDATE_TIME) in a table where it stores the timestamp when an update happens in this table.
I'd like to know how to check rows that the timestamp is today.
This is what I'm using now, but it's not a pretty solution I think:
SELECT
*
FROM
TABLE
WHERE
ROW_UPDATE_TIME BETWEEN (CURRENT TIMESTAMP - 1 DAY) AND (CURRENT TIMESTAMP + 1 DAY);
Is there a better solution, example: ROW_UPDATE_TIME = CURRENT DATE, or something like that?
Found it:
SELECT
*
FROM
TABLE
WHERE
DATE(ROW_UPDATE_TIME) = CURRENT DATE;
The first version you have provided will not return you the results you expect, because you will get in the result timestamps from today or tomorrow, depends on the hour you run it.
Use the query below to get the results from today:
SELECT
*
FROM
table
WHERE
row_update_time
BETWEEN TIMESTAMP(CURRENT_DATE,'00:00:00')
AND TIMESTAMP(CURRENT_DATE,'23:59:59')
Avoid applying a function to a column you compare in the where clause(DATE(row_update_time) = CURRENT_DATE) . That will cause the optimizer to run the function against each row, just to allocate the data you need. It could slow down the query dramatically. Try to run explain against the two versions and you will see what I mean.
I have a table that stores historical data. I get a row inserted in this query every 30 seconds from different type of sources and obviously there is a time stamp associated.
Let's make my parameter as disservice to 1 hour.
Since I charge my services based on time, I need to know, for example, in a specific month, if there is a period within this month in which the there is an interval which is equal or exceeds my 1 hour interval.
A simplified structure of the table would be like:
tid serial primary key,
tunitd id int,
tts timestamp default now(),
tdescr text
I don't want to write a function that loops through all the records comparing them one by one as I suppose it is time and memory consuming.
Is there any way to do this directly from SQL maybe using the interval type in PostgreSQL?
Thanks.
this small SQL query will display all gaps with the duration more than one hour:
select tts, next_tts, next_tts-tts as diff from
(select a.tts, min(b.tts) as next_tts
from test1 a
inner join test1 b ON a.tts < b.tts
GROUP BY a.tts) as c
where next_tts - tts > INTERVAL '1 hour'
order by tts;
SQL Fiddle
in our Application we have two columns at the moment. 1 column with the datetime of an event and 1 column with the datetime how much earlier the alarm should go off. Now we have a new one and thats the exact datetime when the alarm goes off.
I know how I can calculate this for only 1 row, but not for all
select to_date('12/30/1899', 'MM/DD/YYYY') + (select termin_start - alarmvorlauf from termine where id = 1013) from dual
How can I modify the sql so it can update every row in the table 'termine'?
Not sure if I completely understand you, but my guess:
UPDATE termine SET alarm_goes_off = (to_date('12/30/1899', 'MM/DD/YYYY') + (termin_start - alarmvorlauf));
Please add more detail if this is not what you need.