Java sql - delete half rows in DB table - java

I want to delete a bunch of rows from a DB file that I have in a folder. Connecting and counting the amount of rows in the db file works but when I try to delete a specific amount of rows I get stuck.
Input:
sql = "SELECT COUNT(*) AS id FROM wifi_probe_requests";
...
sql = "DELETE FROM wifi_probe_requests LIMIT " + rowcount/2;
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.executeUpdate();
Output:
54943
[SQLITE_ERROR] SQL error or missing database (near "LIMIT": syntax error)
Not using a limit works fine and I can delete the entire db table but what I want is to delete half the db rows as seen by the rowcount/2 I made.
UPDATE:
So far I have solved the problem by finding the id which is located at the n-rows/2 and then getting the value of it (ex. 264352). Then using that number to indicate what id rows are going to be deleted (ex. id.value < 264352).
sql = "SELECT COUNT(*) AS id FROM wifi_probe_requests";
int rowcount = COUNT(*);
sql = "DELETE FROM wifi_probe_requests WHERE id < (SELECT id FROM wifi_probe_requests ORDER BY id ASC LIMIT "+ rowcount/2 + ",1)";
rowcount = 50000
Delete valueof.id < valueof.id.50000/2
So all values of id below the value of an id at position 25000 will be deleted.

You can't. Some databases don't allow LIMIT in UPDATE or DELETE queries.
It seems that with SQLite it's possible to work around that, by compiling your own version, but if you're not willing to do that, you need to rewrite your query in a different way. For example if you have an autoincrement id in the table, you can calculate the "middle" id and use WHERE id < [middle id] as an alternative to LIMIT.

As stated by #Kayaman this is not possible using SQLITE.
You can bypass this with a query such as;
DELETE FROM wifi_probe_requests WHERE id IN (SELECT id FROM wifi_probe_requests LIMIT 10)
One more thing; I don't think (rowcount/2) will work when you have an uneven amount of rows as it will not result in an integer. I think you will have to round it down/up.

How fancy do you want to make this? A simple solution would be something like:
SELECT COUNT(*) FROM mytable;
"SELECT id FROM mytable order by id LIMIT 1 OFFSET " + round(rowcount/2)
DELETE FROM mytable WHERE id < ?
If you go that route, you should be able to delete the first half of your rows by keyspace. If you just want just about half your rows deleted (and don't really care how many) you could probably find a way to use RANDOM() to do this. Probably like (WARNING TOTALLY UNTESTED):
DELETE FROM mytable WHERE random() < 0.5;

Related

How do you find the index/order integer of a table in an SQL database (Java)?

I know that the following code below gives me the amount of tables in an SQL database (I use SQLite), but how would I get the "order" of a table so I can make a for loop to scan through all of them?
String numtables = "String sql = “SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'database’ ”;
pst = con.prepareStatement(numtables);
ResultSet numt = pst.executeQuery(numtables);
int num = ((Number) rs1.getObject(1)).intValue();
The correct query should be (your talking about tables in a db)
SELECT * FROM my_db.sqlite_master WHERE type='table' order by rootpage;
The root page number for any index or table can be found by querying the rootpage.
They are not necessarily subsequent numbers.
then you can explore the result set with the usual
while (rs.next())
Of course take care of the syntax errors, and use the Preparestatement strategy as often as you can, as other suggest.

How to delete multiple rows from multiple tables using Where clause?

Using an Oracle DB, I need to select all the IDs from a table where a condition exists, then delete the rows from multiple tables where that ID exists. The pseudocode would be something like:
SELECT ID FROM TABLE1 WHERE AGE > ?
DELETE FROM TABLE1 WHERE ID = <all IDs received from SELECT>
DELETE FROM TABLE2 WHERE ID = <all IDs received from SELECT>
DELETE FROM TABLE3 WHERE ID = <all IDs received from SELECT>
What is the best and most efficient way to do this?
I was thinking something like the following, but wanted to know if there was a better way.
PreparedStatement selectStmt = conn.prepareStatment("SELECT ID FROM TABLE1 WHERE AGE > ?");
selectStmt.setInt(1, age);
ResultSet rs = selectStmt.executeQuery():
PreparedStatement delStmt1 = conn.prepareStatment("DELETE FROM TABLE1 WHERE ID = ?");
PreparedStatement delStmt2 = conn.prepareStatment("DELETE FROM TABLE2 WHERE ID = ?");
PreparedStatement delStmt3 = conn.prepareStatment("DELETE FROM TABLE3 WHERE ID = ?");
while(rs.next())
{
String id = rs.getString("ID");
delStmt1.setString(1, id);
delStmt1.addBatch();
delStmt2.setString(1, id);
delStmt2.addBatch();
delStmt3.setString(1, id);
delStmt3.addBatch();
}
delStmt1.executeBatch();
delStmt2.executeBatch();
delStmt3.executeBatch();
Is there a better/more efficient way?
You could do it with one DELETE statement if two of your 3 tables (for example "table2" and "table3") are child tables of the parent table (for example "table1") that have a "ON DELETE CASCADE" option.
This means that the two child tables have a column (example column "id" of "table2" and "table3") that has a foreign key constraint with "ON DELETE CASCADE" option that references the primary key column of the parent table (example column "id" of "table1"). This way only deleting from the parent table would automatically delete associated rows in the child tables.
Check out this in more detail : http://www.techonthenet.com/oracle/foreign_keys/foreign_delete.php
If you delete only few records of a large tables ensure that an index on the
column ID is defined.
To delete the records from the table TABLE2 and 3 the best strategy is to use the CASCADE DELETE as proposed by
#ivanzg - if this is not possible, see below.
To delete from TABLE1 a far superior option that a batch delete on a row basis, use signle delete using the age based predicate:
PreparedStatement stmt = con.prepareStatement("DELETE FROM TABLE1 WHERE age > ?")
stmt.setInt(1,60)
Integer rowCount = stmt.executeUpdate()
If you can't cascade delete, use for the table2 and 3 the same concept as above but with the following statment:
DELETE FROM TABLE2/*or 3*/ WHERE ID in (SELECT ID FROM TABLE1 WHERE age > ?)
General best practice - minimum logic in client, whole logic in the database server. The database should be able to do reasonable execution plan
- see the index note above.
DELETE statement operates a table per statement. However the main implementations support triggers or other mechanisms that perform subordinate modifications. For example Oracle's CREATE TRIGGER.
However developers might end up figuring out what is the database doing behind their backs. (When/Why to use Cascading in SQL Server?)
Alternatively, if you need to use an intermediate result in your delete statements. You might use a temporal table in your batch (as proposed here).
As a side note, I see not transaction control (setAutoCommit(false) ... commit() in your example code. I guess that might be for the sake of simplicity.
Also you are executing 3 different delete batches (one for each table) instead of one. That might negate the benefit of using PreparedStatement.

Need help regarding HIbernate count query

We have a use case to count no. of rows returned by actual query. I am not getting how can I get it using criteria in hibernate.
For example suppose I want to run query
select sum(TOTAL_EARNINGS) from table where column1 = 'value1' group by column2);
Along with this query result I also want to get total rows return by above query(above query may have rownum limit also but for total rows I have to ignore rownum limit).
select count(*) from (select sum(TOTAL_EARNINGS) from table
where column1 = 'value1' group by column2);
Is there any easy way of getting this data. I am fine with running separate query to get count data but I am not getting how can I write this in hibernate using criteria. I can't even see any option in SubQueries which I can use
In general with criteria you can use projections.
You can do so, if you have an entity "Your" mapped by Hibernate:
Edited:
Accordingly to your comment, you need two results, a total count and a group by results.
//the group by results...
List result1 = session.createCriteria("Your.class").setProjection(Projections.projectionList()
.add(Projections.groupProperty("column2"))
.add(Projections.sum("totalErnings"))
).list();
//the total count result
return (Integer) session.createCriteria("Your.class").add(Restrictions.eq("column1", val)).setProjection(Projections.rowCount()).uniqueResult();

Look up a table having 1 million records using Hibernate

My database table(geo ip lookup) is having 7 columns,of which 2 columns constitute < composite-id>.
Now when i lookup for a value using first 2 coloumns it takes me 12-14 seconds to fetch a record..
My DAO code looks like this:
String queryString = "from Igeo igeo where igeo.ip_from <= " + ip
+ "and igeo.ip_to >= " + ip;
Query q = session.createQuery(queryString);
List<Igeo> igeoList = q.list();
if(igeoList.size() > 0){
Igeo igeo = igeoList.get(0);
ISP = igeo.getIsp();
...
...
}
*Igeo = class in java represnting table
**Record is fetched when ip lies between values of composite-id columns eg.
ip_from = 1 ; ip_to = 3 ; ip = 2;
so above row will be returned
This table is only used to read records ,please suggest me a queryString which is more efficient than above
First remove hibernate and run your query in a query browser and see how long it takes to return. If it takes the same amount of time it's not Hibernate. It's the performance of the database. Make sure you add indexes onto the two columns ip_from and ip_to. You can also execute a query plan on your query to see what the database is running under the hood and try and optimize the query plan.
I would suggest NOT using concatenation onto your query as you are. That produces a security hole allowing potential SQL injection from outside parties. It's better to use the following:
Query q = session.createQuery("from Igeo igeo where igeo.from_ip >= ? and igeo.to_ip <= ?");
q.setString( 0, ip );
q.setString( 1, ip );
You could also used named parameters which might shorten it up a bit more.
If the table IGeo does not contain overlapping ranges of ip_from and ip_to, you might try this
String queryString = "FROM Igeo igeo"
+ " WHERE igeo.ip_to >= " + ip
+ " ORDER BY igeo.ip_to";
Then check the first item in the list (to see that ip_from <= ip).
Even if the table could contain overlapping ranges of ip_from, ip_to, I bet the above HQL will run faster.
<Aside> You really should not concatenate a raw string like "ip" into HQL or SQL. It leads to SQL Injection Attack vulnerabilities. Use a query parameter instead</Aside>
Also, verify that your database has an index on the column corresponding to Igeo.ip_to.
Sounds to me from your description, that the database has a primary key of Igeo.ip_from + IGeo.ip_to. If the values of ip_from and ip_to are not overlapping, that does not seem to be normalized. You should need only a single column for the primary key. If you have chosen to use both columns as a primary key, the above query will benefit by adding a single index.
Some databases will perform better if you add an index containing all the columns in the table, starting with ip_to and ip_from. (This enables the database to satisfy the query by accessing only the index). Not sure if MySQL can optimize to this extent, but I know DB2 and Oracle will provide this.

How can I limit the number of rows updated in a JPQL query?

I want to limit this update query to only update 5 rows:
Query updateQuery = em.createQuery("update Entity e SET e.myVar = 1");
updateQuery.setMaxResults(5).executeUpdate();
setMaxResults does not seem to do the job. How can I do this in jpql?
If portability is not a issue, then you can go for database specific native query.
Oracle : em.createNativeQuery("update TableName SET myVar = 1 where id IN (SELECT id FROM TableName WHERE ROWNUM <= 5)").executeUpdate();
MySql : em.createNativeQuery("update TableName SET myVar = 1 where id IN (SELECT id FROM TableName LIMIT 5)").executeUpdate();
ROWNUM & LIMIT are used to limit number of results in a query for Oracle & MySql respectively.
Haven't mentioned which database you are using. Provided sample-code might help you, make alterations accordingly.

Categories