Access the last entrys in the database with a java program - java

I am currently writing a Java program.
Brief description of the component:
I have an "Entries" table.
This table has the following columns:
Date (which is entered automatically when the user makes a double entry)
Input (This is the double number from the user)
With 5 entries, for example, the program should now access the last 2 entries made by the user and reflect them in the program.
For example, the table looks like this:
Date --------- Entry
21.01.2022 -- 500
01.03.2022 -- 551
04.05.2022 -- 629
30.06.2022 -- 701
15.07.2022 -- 781
Then the program should give me the 701 and the 781.
What is the most sensible way to do this?
It makes no sense to use the following "SQL statement": Select where date 06/30/2022 because it is no longer useful when the user makes a new entry.
Please help!!

select Entry
from your_table
order by date desc -- show most recent entries above in the results
fetch first 2 rows only; -- show first 2 records only
You might be running on an old DB (version less than 12) so "fetch" might not be introduced.
Try this then
select *
from (select Entry
from your_table
order by date desc)
where rownum <= 2;
Answering to your question how to get a penultimate value
select *
from (select Entry, row_number() over(order by date_col desc) rn
from your_table)
where rn = 2;
the "rn = 2" condition will get you not the last date but a date before the last. Setting it to 1 will get you the row with most recent date

You can use the following SQL statement to select the last two rows:
select * from Entries order by date desc limit 2;

Related

SQLite / Java Compare most recent time stamp with one from 24hr ago for each name

I am trying to get data from the most recent time stamp, and the time stamp from 24 hr ago for each name in the table. My current method makes two seperate queries and combines the results. This, however is quite slow, and also prevents me from sorting the data (by comments etc)
The below query gets the data from 24 hr ago (last)
SELECT Price, comment ,name, timestamp
FROM details INNER JOIN car ON details .ID=car.ID
WHERE timestamp >= datetime('now','-1 day') and name = 'BMW'
order by timestamp asc limit 1
I then have another similar query which returns data with the most recent time stamp (first).
I have a method in Java which contains the above queries, and passes in a new car name into the name = " " part. This returns first and last for each car, I then compare price and comment details and return the results.
However this is proving to be very slow process. And it also means that I cant order the results efficiently.
I have also tried with union, however it does provide the desired results
SELECT Price, comment ,name, max(timestamp)
FROM details INNER JOIN name ON details .ID=name .ID
UNION
SELECT Price, comment,name, min(timestamp)
FROM details INNER JOIN name ON details .ID=name .ID
WHERE timestamp >= datetime('now','-1 day')
group by name
order by comment desc
limit 40
What is the correct way to perform this query?
To get one output row for each name, use GROUP BY:
SELECT Price, comment, name, max(timestamp)
FROM details INNER JOIN name USING (ID)
GROUP BY name.ID
UNION ALL
SELECT Price, comment, name, min(timestamp)
FROM details INNER JOIN name USING (ID)
WHERE timestamp >= datetime('now','-1 day')
GROUP BY name.ID
ORDER BY ...;
Assuming that cars.name is unique (i.e. equivalent to cars.id and that you want the results on separate rows, you can do the aggregation in a subquery to get the two timestamps. Then, join in the additional information you want:
SELECT c.name, d.Price, d.comment, d.name, d.timestamp
FROM car c JOIN
details d
ON d.ID = c.ID JOIN
(SELECT dd.ID, MAX(dd.timestamp) as maxts, MIN(dd.timestamp) as mints
FROM detail dd
WHERE dd.timestamp >= datetime('now', '-1 day')
GROUP BY dd.ID
) dd
ON dd.ID = c.ID AND d.timestamp IN (dd.mints, dd.maxts)
ORDER BY timestamp asc;

SQL Query to retrieve list of a field between two dates

I have a table in which there are two columns namely startTime and endTime (both are DateTime dataTypes) there is another column 'Duration' which calculates the difference between these two. I want to retrieve Duration between specific dates like June10-June20. The problem is I've many rows for same date and Few dates between the range dont even exist. I need to plot the graph for the specified range. So, I have to append zero to the output when there is no entry for a particular day and the similar day's value has to be added together and returned as a single value. So, the output rows should be equal to the number of days specified.
Click here for Example
Thanx in advance.
Assuming that the start and end dates in every row would differ only in the time component.
Also assuming that you do not have any other table that has dates listed and you need a dynamic table to list all the dates between the given range.
You could do the following.
select list_of_dates.date, ifnull(sum(your_table.duration),0)
from your_table
right join
(SELECT DATE(ADDDATE('2016-06-20', INTERVAL #i:=#i+1 DAY)) AS date
FROM your_table,
(select #i:=-1) local
HAVING
#i < DATEDIFF('2016-06-25', '2016-06-20') ) list_of_dates
on list_of_dates.date = date(your_table.start_date)
group by list_of_dates.date
If you have a table that stores relevant calendar it gets easier
select calendar_table.date, ifnull(sum(your_table.duration),0)
from your_table
right join
calendar_table
on calendar_table.date = date(your_table.start_date)
where calendar_table.date >= '2016-06-20' and calendar_table.date <= '2016-06-25'
group by calendar_table.date

Preserve order with Select...Where In and lock the lines

I have the following prepared statement in java:
with main_select as
(select request_id,rownum iden
from
(select request_id
from queue_requests
where request_status = 0 and
date_requested <= sysdate and
mod(request_id,?) = ?
order by request_priority desc, oper_id, date_requested)
where rownum < ?)
select *
from queue_requests qr, main_select ms
where qr.request_id in ms.request_id
order by ms.iden for update skip locked;
It doesn't execute:
ORA-02014: cannot select FOR UPDATE from view with DISTINCT, GROUP BY, etc.
I'll try to explain why i need all the select statements:
the first (inner) select obtains the data i need
the second one limits the number of lines to a number (i can't put it in the first select, because oracle firstly limits the results and only after orders them, which is not what i want)
the third (outside with) select preserves the order (i tried using 3 nested selects - so, no with clause - but i can't find a way to preserve the order in this case). Also, it should lock the lines in the queue_requests table, but because i selected data from the with clause, it gives the above error.
So, i want to select data from queue_requests, keep the first x lines, preserve the order of the select and lock the lines.
Is there a way to do it?
The problem seems to be, that you want to set a lock on the result of main_select. I would just guess, that you can do select for update in the select in the with clause like:
with main_select as
(select request_id,rownum iden
from (subselect)
where rownum < ?
for update skip locked)
But as I said lucky guess.

Getting last record from mysql

I am using mysql and facing some problem. I want to retrieve last row that is inserted.
<< Below are details >>
Below is how I created table.
create table maxID (myID varchar(4))
I inserted four values in it as below
insert into maxID values ('A001')
insert into maxID values ('A002')
insert into maxID values ('A004')
insert into maxID values ('A003')
When I execute select myID, last_insert_id() as NewID from maxID, I get output as below
myId NewID
A001 0
A002 0
A004 0
A003 0
When I tried below code,
select myId, last_insert_id() as NewID, #rowid:=#rowid+1 as myrow from maxID, (SELECT #rowid:=0) as init
I get output as below.
myId NewID rowid
A001 0 1
A002 0 2
A004 0 3
A003 0 4
However when I use code select myId, last_insert_id() as NewID, #rowid:=#rowid+1 as myrow from maxID, (SELECT #rowid:=0) as init where #rowid = 4, I get error as Uknown column 'myrow' in where clause
When I use where #rowid=4, I don't get any data in tables.
Link to play with data
Note: Here I am using 4 just to get desired output. Later I can get this from a query (select max(rowid) from maxID)
Please suggest me what need to do if I want to see only last record i.e. A003.
Thanks for your time.
Update:
I already have millions of data in my table so I can't add new column in it as suggested below.
Almost done.
You succeed in getting the insert order.
So:
select myId, #rowid:=#rowid+1 as myrow from maxID, (SELECT #rowid:=0) as init ORDER BY myrow desc LIMIT 1;
In my console I get the following:
mysql> select myId, #rowid:=#rowid+1 as myrow from maxID, (SELECT #rowid:=0) as
init ORDER BY myrow desc LIMIT 1;
+------+-------+
| myId | myrow |
+------+-------+
| A003 | 4 |
+------+-------+
1 row in set (0.00 sec)
Demo
UPDATE
Yak is right. My solution is not deterministic. Maybe it works for small amount of records. I found tons of post abount unreliability of default sorting of a SELECT statement (here for example).
Next steps:
Under which conditions the default SELECT sorting matches the insertion order?
Is it possible to obtain the last inserted record in a table without an incremental id or an insertion timestamp?
I know it's not an answer, but stating the problem limit the problem.
It seems that a SELECT is not guaranteed to return rows in any specific order (without using an ORDER BY clause, of course).
As per the SQL-92 standard (p. 373):
If an < order by clause > is not specified, then the table specified by the < cursor specification > is T and the ordering of rows in T is implementation-dependent.
Okay, MySQL is not fully SQL-92-compliant, but this is a serious hint.
Laurynas Biveinis (apparently affiliated with Percona) also states:
The order of the rows in the absence of ORDER BY clause (...) could be different due to the moon phase and that is OK.
The MySQL manual says about InnoDB:
InnoDB always orders table rows according to [a PRIMARY KEY or NOT NULL UNIQUE index] if one is present.
As far as I am concerned, I assume MySQL could also reorder rows after an OPTIMIZE TABLE or even reuse empty spaces after many deletes and inserts (I have tried to find an example of this, and have failed so far).
Given your table structure, the bottomline is, unfortunately, that so many factors could have altered the order of the rows; I see no solution to reliably determine the order they were inserted. Unless you kept all binary logs since you created the table, of course ;)
Nevertheless, you may still want to add a sequence column to your table. Existing rows would be assigned a possibly inaccurate sequence number, but at least future rows will be correctly sequenced.
ALTER TABLE maxID ADD sequence INT DEFAULT NULL;
ALTER TABLE maxID ADD INDEX(sequence);
ALTER TABLE maxID MODIFY sequence INT AUTO_INCREMENT;
http://sqlfiddle.com/#!2/63a8d/1
From your insert script, A004 is not the last inserted record. It's the third one. If you want to get the last record in alphabetical order (which A004 is), you must use
select myID from maxID order by myID desc limit 1
If you want the last inserted row, why don't you just use add an autoincrement column to your table? That's the point of those kinds of columns. The autoincrement column doesn't have to be the PK (it should be, but doesn't have to if you don't have the choice).
As mentioned in the MySQL help for last_insert_id, you can only use it with **auto-increment columns. This means that you cannot make MySQL find the most recently inserted row for you, unless you know something about the order of the IDs. If they are sorted like your example suggests, then you can use
SELECT *
FROM maxID
WHERE myId = max(myId)
But I suggest adding an auto-increment column to the table and then use = last_insert_id() in your WHERE clause. See also this page for information on how to obtain the last ID.
last_insert_id does not work because it only returns the last value generated by an auto-increment field. However I think that solution may be on the right track. I would suggest adding another column that is a auto-increment integer. You can then insert data and to retrieve the row you just inserted select the last_insert_id(). To retrieve the most recent row (inserted by any process) select the max number for the new column, or sort by it desc.
If you want to use the varchar column you can do an alphabetic sort, but that does not guarantee it will be the last row you inserted row or even the most recently inserted row.
The one other solution I can think of which may do what you need is to create a stored procedure which creates the row, inserts it into the table, and then returns it to you.
Your usage of #rowid is simply counting the order rows are returned to you. There is no guarantee that they are returned to you in the order of oldest to newest. There are a variety of things that can affect the order in which rows are stored.
Please use the following query.
Select * from maxID order by myId desc limit 1,1;

JPA Select latest instance for each item

Let's say I have a Meeting entity. Each meeting has a single attendee and a meeting date. Within my meeting table I may have multiple meetings for each attendee, with different dates for each. I need a JPA query that will select only the latest meeting for all attendees. For instance, if my table looks like this
Meeting ID | Attendee ID | Meeting Date
1 | 1 | 6/1/2011
2 | 2 | 6/1/2011
3 | 1 | 6/6/2011
4 | 3 | 6/6/2011
My result should be
Meeting ID | Attendee ID | Meeting Date
2 | 2 | 6/1/2011
3 | 1 | 6/6/2011
4 | 3 | 6/6/2011
Using JPA 2 against postgres. Meeting has 1-1 to attendee and a simple timestamp date. I suspect I'm going to need to do a group by and max(blah) and maybe a join to myself, but I'm not sure of the best way to approach this.
Update:
After spending the evening playing with this, I still do not have an acceptable JPQL solution to this. Here is what I have so far:
select m from Meeting m
where m.meetingDate in
( select max(meet.meetingDate)
from Meeting meet group by meet.attendee )
I have various other conditions that are not relevant to this question, like filtering by attendee department and whatnot. The only reason this works is because we are tracking meeting date to the second (or finer) and the chance that there will be two meetings at exactly the same time is minimal. We are putting some java stuff around it to keep only hte last meeting for each attendee just in case we do get two at the same time, but that's a pretty crappy solution. It really shouldn't be too difficult to get it all in a query, but I have yet to figure it out.
Update2: Adding sql tag because if I need to use sql to create a view and create a JPA object to map to the view I'm ok with that.
In SQL the solution is very simple - join the table with a subquery, which gives you the most recent meeting for each attendee:
select * from Meeting ALL
join ( select max(meetingDate) as newest, attendee
from Meeting group by attendee ) LATEST
on ALL.meetingDate = LATEST.newest AND ALL.attendee = LATEST.attendee
This works, and works fast!
The problem with JPA is that it (or most implementations) won't allow a subquery for a join. After spending several hours trying what will compile in the first place, and then, how slow it is, I decided that I hate JPA. Solutions like the ones above - like EXISTS (SELECT .. ) or IN ( SELECT .. ) - take ages to execute, orders of magnitude slower than they should.
Having a solution that works meant that I just needed to access that solution from JPA. There are two magic words in SQL that help you do just that:
CREATE VIEW
and the life becomes so much simpler... Just define such entity and use it.
Caution: it's read-only.
Of course, any JPA purists will look down on you when you do that, so if anyone has a pure JPA solution, please let us both know!
I think I've got it with this query.
select m from Meeting m
where m.meetingDate =
(select max(m1.meetingDate)
from Meeting m1
where m1.attendee = m.attendee )
and not exists
(select m2 from Meeting m2
where m2.attendee = m.attendee
and m2.meetingDate > m.meetingDate)
Well in SQL that would be quite simple I think, so I assume that can be mapped to JPA:
SELECT m.AttendeeId, MAX(m.MeetingDate) from Meeting m GROUP BY m.AttendeeId
Edit: If you need the messageId itself as well you can do that with a simple subquery that returns the messageId for a message where the other two values are equal. Just make sure you handle the case where there are several messageIds for the same Attendee and Date (eg pick the first result since they should all be equally good - although I'd doubt that such data even makes sense for meetings)
Plain SQL
As Bulba has said appropriate way is to join a subquery with group by.
JPA, JPQL
The problem is that you can't join a subquery.
Here is a workaround.
Lets see what you get in the subquery with group by. You get a list of pairs (attendee_id, max(meeting_date)).
This pair is like a new unique id for row with max date you want to join on.
Then note that each row in the table forms a pair (attendee_id, meeting_date).
So every row has an id as a pair (attendee_id, meeting_date).
Lets take a row if only it forms an id that belongs to list received in the subquery.
For simplicity lets represent this id-pair as a concatenation of attendee_id and meeting_date: concat(attendee_id, meeting_date).
Then the query in SQL(similarly for JPQL and JPA CriteriaBuilder) would be as follows:
SELECT * FROM meetings
WHERE concat(attendee_id, meeting_date) IN
(SELECT concat(attendee_id, max(meeting_date)) FROM meetings GROUP BY attendee_id)
Note that there is only one subquery per query, not one subquery for each row like in some answers.
Afraid of comparing strings?
We have a special offer for you!
Lets encode that id-pair to number.
It will be a sum of attendee_id and meeting_date but with modifications to ensure uniqueness of code. We can take number representation of date as Unix time.
We will fix the value of max date that our code can capture because final code has max value limit (e.g. bigint(int8)<263). Lets take for convenience max date as 2149-06-07 03:00:00. It equals 5662310400 in seconds and 65536 in days.
I will assume here that we need precision for date in days(so we ignore hours and below).
To construct unique code we can interpret it as a number in a numerical system with base of 65536. The last symbol(number from 0 to 216-1) in or code in such numerical system is number of days. Other symbols will capture attendee_id. In such interpretation code looks like XXXX, where each X is in range [0,216-1] (to be more accurate, first X is in range [0,215-1] because of 1 bit for sign), first three X represents attendee_id and last X represents meeting_date.
So the max value of attendee_id our code can capture is 247-1.
The code can be computed as attendee_id*65536 + "date in days".
In postgresql it will be:
attendee_id*65536 + date_part('epoch', meeting_date)/(60*60*24)
Where date_part returns date in seconds which we convert to days by dividing on constant.
And final query to get the latest meetings for all attendees:
SELECT * FROM meetings
WHERE attendee_id*65536 + date_part('epoch', meeting_date)/(60*60*24)
IN (SELECT attendee_id*65536 + date_part('epoch', max(meeting_date))/(60*60*24) from meetings GROUP BY attendee_id);
Benchmarking
I have created a table with stucture as in the question and populated it with 100000 rows randomly selecting attendee_id from [1, 10000] and random date from range [1970-01-01, 2017-09-16]. I have benchmarked (with EXPLAIN ANALYZE) queries with the following techniques:
Correlated subquery
SELECT * FROM meetings m1 WHERE m1.meeting_date=
(SELECT max(m2.meeting_date) FROM meetings m2 WHERE m2.attendee_id=m1.attendee_id);
Execution time: 873260.878 ms
Join subquery with group by
SELECT * FROM meetings m
JOIN (SELECT attendee_id, max(meeting_date) from meetings GROUP BY attendee_id) attendee_max_date
ON attendee_max_date.attendee_id = m.attendee_id;</code>
Execution time: 103.427 ms
Use pair (attendee_id, date) as a key
Concat attendee_id and meeting_date as strings
SELECT * FROM meetings WHERE concat(attendee_id, meeting_date) IN
(SELECT concat(attendee_id, max(meeting_date)) from meetings GROUP BY attendee_id);
Execution time: 207.720 ms
Encode attendee_id and meeting_date to a single number(code)
SELECT * FROM meetings
WHERE attendee_id*65536 + date_part('epoch',meeting_date)/(60*60*24)
IN (SELECT attendee_id*65536 + date_part('epoch',max(meeting_date))/(60*60*24) from meetings GROUP BY attendee_id);
Execution time: 127.595 ms
Here is a git with table scheme, table data (as csv), code for populating table, and queries.
Try this
SELECT MAX(m.MeetingDate) FROM Meeting m

Categories