How can I limit the number of rows in an Android room database by removing the oldest item in the row and inserting the newest one?
I am guessing its a standard query when adding an item to the database?
EDIT: I want to limit a database table to have a max row count of say 20. If that limit is reached, we remove the oldest item and insert the new one by keeping the current row count to 20.
I think you can insert the data into your table then remove all the rows except last 20 (limit)
To delete you can use the following query
DELETE FROM tableName where id NOT IN (SELECT id from tableName ORDER BY id DESC LIMIT 20)
In this case, id is the primary key which is set to auto increment. You can use date as key as well if you are storing them by date
Here is sample solution:
Query is :
#Query("SELECT * FROM user LIMIT :limit OFFSET :offset")
User[] loadAllUsersByPage(int limit,int offset);
Here, it will give a list of user based on limit and offset.
if loadAllUsersByPage(2,0) it will return first 2 rows from table.
if loadAllUsersByPage(2,1) it will return 2nd and 3rd rows from table.
but if loadAllUsersByPage(-1,10) then it will serve first 10 rows from table.
Assuming:
Your table is
create table example_table (
ts timestamp,
uid number(19),
some_other_field varchar(64)
);
And you don't want to care about running some query manually.
Use database triggers:
create trigger
if not exists -- I don't actually know if you DB will support this line.
-- Might want to remove it if it's not.
example_table_limiter
on example_table
after insert
begin
delete
from example_table
where ts in (
select ts
from example_table
order by ts
limit -1 -- we don't want to limit how many rows we want to delete
offset 25 -- but we want to offset query result so it leaves 25 rows in table
);
end;
"Offset without limit" syntax is inspired by this answer.
To enable your trigger in java:
Simple Android, where you can override SQLiteOpenHelper:
public class DataBaseSchemaHelper extends SQLiteOpenHelper {
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(<trigger string from above>);
}
}
Android Room version:
public class MyDatabase extends RoomDatabase {
#Override
public void init(DatabaseConfiguration _config) {
super.init(_config);
getOpenHelper().getWritableDatabase().execSQL(<trigger string from above>);
}
}
You can limit columns/rows by doing this: this query will return the new data and remove old data when its reach its limit.
Explanation:
First query is select all data order by descending
Second query is remove data from columns/rows id > 20
If you want your table only have 20 row then set the OFFSET to 20, the LIMIT is represent how many rows inserted & deleted at once.
In my example I remove 1 row (the oldest/last row) when the user inputs 1 new data
#Query("SELECT * FROM my_table ORDER BY timeStamp DESC")
fun getAllData(): List<MyEntityClass>
#Query("DELETE FROM my_table WHERE id IN (SELECT id FROM my_table ORDER BY timeStamp DESC LIMIT 1 OFFSET 20)")
fun removeOldData()
Follow this steps :
1> get count of rows of that table
your_count = SELECT count( * ) FROM table_name;
2> if count is >(greater than) 20 than to get oldest record
SELECT *
FROM table_name
ORDER BY entry_Date ASC
LIMIT 1;
3> now delete these selected records
4> insert new datas
NOTE : if you are inserting multiple entries than put this in loop
Related
I am facing an issue in Oracle Query to achieve the following use case,
Consider I have two tables :
Table 1 : product
productId - Integer - primaryKey
productName - Varchar
Table 2 : product_sequence
productId - Integer - primaryKey
sequenceId - Integer - primaryKey
orderId - Integer
orderName - Varchar
product table has 1000 entries and product_sequence table has 10K entries
Requirement :
(paginate) fetch the entries from 0 to 100 / 100 to 200 / etc., in the product table
Distinct count of productId for showing the pagination in UI (check the sample query below)
Filter by 'productName' in 'product' table and 'orderName' in 'product_sequence' table
Query (tried) :
SELECT
p.productId, p.productName, ps.orderId, ps.orderName,
COUNT(distinct p.productId) OVER () AS TOTAL
FROM (
select *
from product
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY
) p
JOIN product_sequence ps on p.productId=ps.productId
WHERE ps.orderId IN ('12','13','14');
NOTE : the above query will work in Oracle, But the issue is
Expected:
Return 100 entries from 'product' table with mapped entries in the 'product_sequence' table
Actual :
It first LIMITS 100 entries in product and then filter the orderId so the number of entries returned is reduced from 100 to lesser number
I agree my query is not correct: It first LIMIT by 100 in 'product' table in subquery and then goes for filter in second table which reduces the count
Could some one help me with the query for this please? Anyhelp is appreciated.
If my question is not clear, Let me know, I can explain with more info.
Try to move the OFFSET and FETCH clauses to the outer query, something like this:
SELECT q.productId, q.productName, q.orderId, q.orderName,
COUNT(distinct p.productId) OVER () AS TOTAL
FROM ( SELECT * FROM product p JOIN product_sequence ps ON p.productId = ps.productId
WHERE ps.orderId IN ('12','13','14') ) q
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY
To get 100 rows per page "after filtering" you'll need to find all the productid values first, then process the main query.
For example:
select
p.productid, p.productname, ps.orderid, ps.orderName,
count(distinct p.productid) over() as total
from product p
join product_sequence ps on p.productid = ps.productid
where ps.orderid in ('12','13','14')
and p.productid in (
select *
from (
select distinct p.productid
from product p
join product_sequence ps on p.productid = ps.productid
where p.productname like '%theremin%' -- filter #1
and ps.orderid in ('12','13','14') -- filter #2
) x
order by productid
offset 300 rows -- get the 4th page
fetch first 100 rows only -- page size set to 100 rows
)
See example at db<>fiddle.
I really can't find a solution for this problem:
Here I have two ResultSets, one which always shows me the number of items stored in my database and one that retrieves all the data from it.
I would like to generate a random number and then generate a random item based on the row number/id in my database. Since I'm fairly new I'm not sure if this is an efficient approach. It doesn't look very clean to retrieve all the data and then iterate over it every time. Especially if I had like 1000 items and the randomly generated number is 999.
PreparedStatement randomSelection = con.prepareStatement("SELECT * FROM items ORDER BY RAND() LIMIT 1"); {
String name = ((ResultSet) randomSelection).getString(2);
System.out.println(name);
}
Tried calling the column itemname with the last line. However I just can't look for a good solution for this problem. Would highly appreciate any help since I'm fairly new to databases.
Thank you
EDIT: This is what I tried now and there is no output somehow
Same for
ResultSet numberOfItemsInDataBase = stmt.executeQuery("SELECT count(*) FROM items;");
// this will return a number between 0 and the number of rows - 1
int id = new Random().nextInt(numberOfItemsInDataBase.getInt(1));
ResultSet itemsInDataBase = stmt.executeQuery("select * from items order by id limit 1 offset " + id);
if (itemsInDataBase.next()) {
String item = itemsInDataBase.getString(2);
System.out.println(item);
}
If you just need a random row of the table then you can do it with plain SQL with the function RAND():
ResultSet itemsInDataBase = stmt.executeQuery("select * from items order by rand() limit 1");
if (itemsInDataBase.next()) {
item = new Item(itemsInDataBase.getString(2));
}
If you want to use the generated random number, then use it in the OFFSET clause of the sql statement:
ResultSet numberOfItemsInDataBase = stmt.executeQuery("SELECT count(*) FROM items;");
// the above query will return exactly 1 row
numberOfItemsInDataBase.next();
// this will return a number between 0 and the number of rows - 1
int id = new Random().nextInt(numberOfItemsInDataBase.getInt(1));
ResultSet itemsInDataBase = stmt.executeQuery("select * from items order by id limit 1 offset " + id);
if (itemsInDataBase.next()) {
item = new Item(itemsInDataBase.getString(2));
}
Use ORDER BY RAND() and limit the result to 1. This circumvents you having to query for the count and then ultimately iterate through the ResultSet until you find the random entry.
try (ResultSet randomSelection = connection
.preparedStatement("SELECT * FROM items ORDER BY RAND() LIMIT 1")) {
if (randomSelection.next()) {
String name = randomSelection.getString(2);
}
}
You can use the limit function to get the item.
The LIMIT clause can be used to constrain the number of rows returned by the SELECT statement. LIMIT takes one or two numeric arguments, which must both be nonnegative integer constants (except when using prepared statements).
With two arguments, the first argument specifies the offset of the first row to return, and the second specifies the maximum number of rows to return. The offset of the initial row is 0 (not 1). So in your case the offset can be the the random generated id minus one and maximum number of rows is 1:
select * from items LIMIT {id-1},1; # Retrieve row (id-1)
I have 2 ResultSets. 1st ResultSet contains the records from table1 from database1 and 2nd ResultSet contains the records from table2 from database2. I need a list of records from resultset1 which are not present in resultSet2. For this I wrote this logic but it is not working and throwing me the following error.
java.sql.SQLException: Invalid operation for read only resultset: deleteRow
if ( table1ResultSet != null )
{
while ( table1ResultSet.next() )
{
final String table1Record = table1ResultSet.getString( 1 );
if ( table2ResultSet != null )
{
while ( table2ResultSet.next() )
{
final String table2Record = table2ResultSet.getString( 1 );
if ( table1Record.toString().equalsIgnoreCase( table2Record.toString() ) )
{
table1ResultSet.deleteRow();
break;
}
}
}
}
}
return table1ResultSet;
That exception says what the problem is - your result set doesn't support delete. In order to have updateable result set there are some requirements:
When you prepare statement did you make it with ResultSet.CONCUR_UPDATABLE?
A query can select from only a single table without any join operations.
The query must select all non-nullable columns and all columns that do not have a default value. A query cannot use "SELECT * ". Cannot select derived columns or aggregates such as the SUM or MAX of a set of columns.
You might want to move the results sets into Java sets before working doing what you are doing though because using deleteRow will actually delete the row from the database (unless that's the expected result)
There is another problem with your code though. Even if delete works your code will fail on the second iteration of result set 1 because you never reset table2ResultSet and for the second iteration there won't be more results in table2resulset.
But on top of all that. Why would you go through all that hussle and get all that rows that you don't need instead of doing it with one single query like:
select * from table 1 where id not in select id from table 2
or
delete from table 1 where id not in select id from table 2
if that's the goal
Your logic:
Assumes the records come in some order (which may or may not be true, depending on your SQL)
Consumes the entire result set 2 for each row of result set 1, which is unlikely your intent
Deletes things, which is also not what you mentioned in the question
Your question can be implemented easily as such:
Set<String> list1 = new HashSet<>();
while (table1ResultSet.next())
list1.add(table1ResultSet.getString(1).toLowerCase());
while (table2ResultSet.next())
list1.remove(table2ResultSet.getString(1).toLowerCase());
System.out.println(list1);
This will print all the values (without duplicates) that are present in the first result set, but not in the second.
My server would retrieve the latest ID from the database, now it is stuck and keeps returning the id 99999, even though the latest id is now 100040
My code is:
String insertTable = "SELECT * FROM dutyofcare ORDER BY Id DESC LIMIT 1";
ps = conn.prepareStatement(insertTable);
rs = ps.executeQuery();
String ResultS = "";
if (rs.next()) {
ResultS += rs.getString("Id");
}
The issue is that the ORDER BY in your query is doing a lexical (character-by-character) sort where 9 always comes after 1, and not numeric sort which handles the digit positions. This is because of the column type of ID. What you need is to ensure ID is a number before the sort is done.
Either change your ID to a numeric column type and run below query:
SELECT MAX(ID) from dutyofcare;
Or if you want to retain your column type (less efficient than above option):
select MAX(cast(ID AS UNSIGNED)) from dutyofcare;
Or if you want to retain your column type AND just fix your existing query (least efficient of all the options)
select * from dutyofcare order by CAST(ID AS UNSIGNED) desc limit 1;
All these methods basically treat the ID as number and choose the biggest value.
try {
Statement s = conn.createStatement();
ResultSet result2 = s.executeQuery("Select Distinct * From Poem p,Recording r Where r.PoemTitle = p.PoemTitle AND r.poemTitle = 'poem1'");
System.out.print("Result (Select with Join): ");
while (result2.next()) {
System.out.println(result2.getString(1)+ " " + result2.getString(2)+ result2.getString(3));
}
} catch(Exception e) {
System.out.print(e.getMessage());
}
I am trying to output the poemtitle and the date it was recorded. When this runs it outputs the poemtitle and then gives the date the poem was created instead of recorded? Is this because of the relationship?
Most likely it's because of the * in the SELECT list.
Specify the columns that you want returned, in the order you want them returned.
We're just guessing at the name of the column that contains "date recorded" and which table it's in:
SELECT p.PoemTitle
, r.dateRecorded
, r.readBy
FROM Poem p
JOIN Recording r
ON r.PoemTitle = p.PoemTitle
WHERE r.poemTitle = 'poem1'
GROUP
BY p.PoemTitle
, r.dateRecorded
, r.readBy
ORDER
BY p.PoemTitle
, r.dateRecorded DESC
, r.readBy
Notes:
Ditch the old-school comma syntax for the join operation and use the JOIN keyword instead, and relocate the join predicates from the WHERE clause to an ON clause.
Avoid using * in the SELECT list. Explicitly list the columns/expressions to be returned. When we read the code, and that SQL statement, we don't know how many columns are being returned, what order the columns are in, or what the datatypes are. (We'd have to go look at the table definitions.)
Explicitly listing the columns/expressions being returned only takes a little bit of work. If code was only ever written, then it would be fine, save the time writing. But code is READ ten times more than it is written. (And the SQL statement with the * makes the SQL statement virtually indecipherable in terms of which column is being referenced by getString(1).
Listing the columns columns can also make it more efficient on the database, to prepare a resultset with a few columns vs a resultset of dozens of columns, and we also transfer a smaller resultset from the database to the client. With a subset of columns, its more likely we can use a covering index for the query.