I've to extract some specific data from Oracle DB. I'm using Java 8.
I have to consider 3 columns from a TABLE : flag | date_1 | date_2.
The flag can have values 1,2 or 3 (The flag values updates from 1->2->3 over a period of time or based on certain events).
Say for two dateTime values X and Y, I need extract all the rows from the table where the date value is between X and Y.
If flag value is 1 or 2 then date_1 must be between X & Y irrespective of date_2. But if flag value is 3 then date_2 must be between X & Y irrespective of date_1.
For this I created three different queries with different parameters as flag=1/flag=2/flag=3. But this seems to be very inefficient.
String sql1 = "SELECT new xxxx.xxxx.xxx.Java.class.xxx ("
+ "t.flag, t.date_1, t.date_2)"
+ "FROM Table t "
+ "WHERE t.flag = :flagValue1 "
+ "AND t.date_1 >= :X "
+ "AND t.date_1 < :Y ";
String sql2 = "SELECT new xxxx.xxxx.xxx.Java.class.xxx ("
+ "t.flag, t.date_1, t.date_2)"
+ "FROM Table t "
+ "WHERE t.flag = :flagValue2 "
+ "AND t.date_1 >= :X "
+ "AND t.date_1 < :Y ";
String sql3 = "SELECT new xxxx.xxxx.xxx.Java.class.xxx ("
+ "t.flag, t.date_1, t.date_2)"
+ "FROM Table t "
+ "WHERE t.flag = :flagValue3 "
+ "AND t.date_2 >= :X "
+ "AND t.date_2 < :Y ";
List<E> list1 = this.entityManger.createQuery(sql1, model.class)
.setParameter("1", flag1)
.setParameter("X", firstDateValue)
.setParameter("Y", secondDateValue)
.getResultList();
List<E> list2 = this.entityManger.createQuery(sql2, model.class)
.setParameter("2", flag2)
.setParameter("X", firstDateValue)
.setParameter("Y", secondDateValue)
.getResultList();
List<E> list3 = this.entityManger.createQuery(sql3, model.class)
.setParameter("3", flag3)
.setParameter("X", firstDateValue)
.setParameter("Y", secondDateValue)
.getResultList();
List<E> finalList = new ArrayList<>();
finalList.addAll(list1);
finalList.addAll(list2);
finalList.addAll(list3);
return finalList;
Is there any other way of doing this? What could be better (single)query for this ?
PS: I wonder if I can use Java 8's Lambda Functions for just filtering the values from the List. Any suggestions regarding this ?
Thanks for the help.
If i understand it correctly you actually have 2 input parameters X & Y (aka from/to Date). And you always want to query the values of all flags regarding the entered date. Therefore a query like this would be more efficient:
select new Java.class.name (t.flag, t.date_1, t.date_2) from Table t
where
(t.flag in (1,2) and t.date_1 >= :X and t.date_1 < :Y)
or
(t.flag = 3 and t.date_1 2= :X and t.date_2 < :Y)
This would reduce the whole thing to just one query ... may still be inefficient depending on the dataset.
Related
"MERGE INTO NT_PROPERTY ntProp USING ( " +
"SELECT * FROM NT_PROPERTY ) " +
"VALUES " +
"('minDPTObjectId'," + minDPTObjectId + ", 'Starting DPT Object Id') " +
"('maxDPTObjectId', " + maxDPTObjectId + ", 'Ending DPT Object Id') " +
"vt (NAME, VALUE, NOTE) " +
"ON ( ntProp.NAME = vt.NAME ) " +
"WHEN MATCHED THEN " +
"UPDATE SET VALUE = vt.VALUE "+
"WHEN NOT MATCHED THEN " +
"INSERT (NAME, VALUE, NOTE) VALUES (vt.NAME, vt.VALUE, vt.NOTE)";
Well I'm getting a missing ON keyword error and with no clue what so ever, also is there any other way to make it less clumsy
Help is very much appreciated.
The problem is that your MERGE syntax is incorrect. Your statement takes the form of:
MERGE INTO nt_property ntprop
USING (SELECT * FROM nt_property)
VALUES (...)
vt (...)
ON (ntprop.name = vt.name)
WHEN MATCHED THEN
UPDATE ...
WHEN NOT MATCHED THEN
INSERT ...;
but it should be of the form:
MERGE INTO target_table tgt_alias
USING source_table_or_subquery src_alias
ON (<JOIN conditions>)
WHEN MATCHED THEN
UPDATE ...
WHEN NOT MATCHED THEN
INSERT ...;
Why do you have the VALUES and vt clauses between your using and your on clauses? That's the incorrect syntax. Also, whilst you can use select * from tablename in the using clause, you could just use the tablename directly, since you're selecting all columns and all rows.
MERGE INTO NT_PROPERTY D
USING (SELECT * FROM DUAL ) S
ON (D.NAME = 'minDPTObjectId')
WHEN MATCHED THEN UPDATE SET D.VALUE = '1234'
WHEN NOT MATCHED THEN INSERT (NAME, VALUE, NOTE)
VALUES ('maxDPTObjectId', '1111', 'Ending DPT Object Id') ;
I have a table X which has a column BALANCE. I have two row ids and a value amount. There are two constraint variables - MAX_BALANCE and MIN_BALANCE.
I need to write an update query which updates the column BALANCE. The first row id's BALANCE is added with amount and the amount is subtracted from the second row id's BALANCE. I need to ensure that the BALANCE always stays within the range. That is, MIN_BALANCE <= BALANCE <= MAX_BALANCE.
I am not supposed to update one row and then roll back if the number of rows updated is not equal to 2. The update query should either update two rows (success case) or it should not update any row at all.
I am using Hibernate in Java and here is the query which I have tried. It doesn't work for the success case.
String sql = "UPDATE X x "
+ "SET x.balance = CASE "
+ "WHEN x.id = :rowId1 THEN (x.balance + :amount) "
+ "WHEN x.id = :rowId2 THEN (x.balance - :amount) "
+ "END "
+ "WHERE x.id IN :ids "
+ "AND ((x.id = :rowId1 AND x.balance + :amount <= :MAX_BALANCE) "
+ "OR (x.id = :rowId2 AND x.balance - :amount >= :MIN_BALANCE))";
Query query = entityManager.createQuery(sql);
List<BigInteger> ids = Arrays.asList(new BigInteger(rowId1), new BigInteger(rowId2));
int rows = query.setParameter("amount", amount)
.setParameter("ids", ids)
.setParameter("rowId1", new BigInteger(rowId1))
.setParameter("rowId2", new BigInteger(rowId2))
.setParameter("MAX_BALANCE", new Float(MAX_BALANCE))
.setParameter("MIN_BALANCE", new Float(MIN_BALANCE))
.executeUpdate();
I don't want to check if rows == 1 and throw an exception. The update query should always ensure that rows will take the value either 0 or 2.
Or is there a way to perform this operation based on Criteria Update in Hibernate?
You need to self join your table on itself. The first instance should have the record for rowid1 and the 2nd instance should have the record for rowid2. This way you can check the balances of both records in one go and make the decision whether to update or not.
update x x1 join x x2
set x1.balance=if(x1.balance+:amount <= :MAX_BALANCE,x1.balance+:amount, x1.balance),
x2.balance=if(x2.balance-:amount >= :MIN_BALANCE,x2.balance-:amount, x2.balance)
where x1.id=:rowid1 and x2.id=rowid2
try this ...
String sql = "UPDATE X x "
+ "SET x.balance = CASE "
+ "WHEN x.id = :rowId1 AND x.balance + :amount <= :MAX_BALANCE THEN (x.balance + :amount) "
+ "WHEN x.id = :rowId2 AND x.balance - :amount >= :MIN_BALANCE THEN (x.balance - :amount) "
+ "END "
+ "WHERE x.id = :rowId1 OR x.id = :rowId2";
Edit-
I'll add the use case to clear up the function of this.
The user will select two dates - a start date and an end date - these are then passed on and used to select the tables (each year has its own table). In one use case where the two given dates lie in the same year it's a simple query on that table alone.
However, if the two dates are different years I will need to join all tables (so 2011-2013 will be three tables connected, to search through) and thus, I want a dynamic fix to this. I know building up a query like below is against security - just thought something similar would work. As the system will get new tables each year I also dont want to have to manually add however many new queries for each case (2011-2016, 2014-2018, 2011-2019.. etc)
I have a question about whether it is possible to create a dynamic query as a String like below and then pass that through to service -> repository, and use that as a query?
for (int i = 0; i < yearCondition; i++) {
if (i == 0) {
query += "SELECT md.Device_ID, l.locationRef, " + reportRunForm.getStartDate() + " as 'From Date', "
+ reportRunForm.getEndDate() + " as 'To Date' "
+ "from mData.meterdata" + iDateStart.substring(0, 4)
+ " join MOL2.meters m on device_ID = m.meterUI "
+ "join MOL2.locations l on m.locationID = l.locationID "
+ "join MOL2.meterreg mr on m.meterID = mr.meterID "
+ "where mr.userID = ?1";
}
query += "UNION SELECT md.Device_ID, l.locationRef, " + reportRunForm.getStartDate() + " as 'From Date', "
+ reportRunForm.getEndDate() + " as 'To Date' "
+ "from mData.meterdata" + (Integer.parseInt(iDateStart.substring(0, 4))+i)
+ " join MOL2.meters m on device_ID = m.meterUI "
+ "join MOL2.locations l on m.locationID = l.locationID "
+ "join MOL2.meterreg mr on m.meterID = mr.meterID "
+ "where mr.userID = ?1";
}
I may have the wrong idea with how this works, and I know I could create and persist a query through entitymanager, but wanted to know whether doing it through the repository would be possible?
My thought was I'd build up the query like above, pass it through to service and then to repository, and bind it as value in #Query annotation but this doesn't seem possible. I'm likely approaching this wrong so any advice would be appreciated.
Thanks.
Edit -
Had a goof. Understand doing it at all like that is stupid, an approach to build up something similar is what I'm looking for that is still secure.
Maybe this annotations before your POJO can help
#org.hibernate.annotations.Entity(dynamicInsert = true)
for example two tables district and constituency ...
Dynamic query
query += "select *from constituency c where 1=1";
if(constituencyNumber!=null)
query +=" and c.constituency_number like '"+constituencyNumber+"%'";
query += " group by c.district_id";
OR
select *from constituency c where (c.constituency_number is null or c.constituency_number like '1%') group by c.district_id;
Problem Synopsis:
When attempting to execute a SQL query in Java with a SQLite Database, the SQL statement fails to return from the execute() or executeQuery() method. In other words, the system "hangs" when executing this SQL statement.
Question:
What am I doing wrong to explain why the ResultSet never "returns?"
TroubleShooting
I tried to narrow the problem and the problem seems to be with the Java execute() or executeQuery(). A ResultSet never seems to return. For example, I tried executing exactly the same query directly in SQLite (that is, using a SQLite DB manager). The query (outside Java) executes in about 5ms and returns the valid result set.
NOTE: No exception is thrown. The system merely seems to "hang" and becomes unresponsive until a manual kill. (waiting more than 10 minutes.)
Code:
I heavily edited this code to make the problem simpler to see. (In production, this uses Prepared Statements. But, the error occurs in both methods--straight Statement and prepared Statement versions.)
Basically, the SELECT returns a single DB item so the user can review that item.
Statement st = conn.createStatement() ;
ResultSet rs = st.executeQuery("SELECT DISTINCT d1.id, d1.sourcefullfilepath, " +
"d1.sourcefilepath, d1.sourcefilename, d1.classificationid, d1.classid, " +
"d1.userid FROM MatterDataset, (SELECT MatterDataset.id, " +
"MatterDataset.sourcefullfilepath, MatterDataset.sourcefilepath, " +
"MatterDataset.sourcefilename, MatterDataset.matterid , " +
"DocumentClassification.classificationid, DocumentClassification.classid," +
" DocumentClassification.userid FROM MatterDataset " +
"LEFT JOIN DocumentClassification ON " +
"DocumentClassification.documentid = Matterdataset.id " +
"WHERE ( DocumentClassification.classid = 1 OR " +
"DocumentClassification.classid = 2 ) AND " +
"DocumentClassification.userid < 0 AND " +
"MatterDataset.matterid = \'100\' ) AS d1 " +
"LEFT JOIN PrivilegeLog ON " +
"d1.id = PrivilegeLog.documentparentid AND " +
"d1.matterid = PrivilegeLog.matterid " +
"WHERE PrivilegeLog.privilegelogitemid IS NULL " +
"AND MatterDataset.matterid = \'100\' " +
"ORDER BY d1.id LIMIT 1 ;") ;
Configuration:
Java 6,
JDBC Driver = Xerial sqlite-jdbc-3.7.2,
SQLite 3,
Windows
Update
Minor revision: as I continue to work with this, adding a MIN(d1.id) to the beginning of the SQL statement at least returns a ResultSet (rather than "hanging"). But, this is not really what I wanted as the MIN obviates the LIMIT function.
Statement st = conn.createStatement() ;
ResultSet rs = st.executeQuery("SELECT DISTINCT MIN(d1.id), d1.id,
d1.sourcefullfilepath, " +
"d1.sourcefilepath, d1.sourcefilename, d1.classificationid, d1.classid, " +
"d1.userid FROM MatterDataset, (SELECT MatterDataset.id, " +
"MatterDataset.sourcefullfilepath, MatterDataset.sourcefilepath, " +
"MatterDataset.sourcefilename, MatterDataset.matterid , " +
"DocumentClassification.classificationid, DocumentClassification.classid," +
" DocumentClassification.userid FROM MatterDataset " +
"LEFT JOIN DocumentClassification ON " +
"DocumentClassification.documentid = Matterdataset.id " +
"WHERE ( DocumentClassification.classid = 1 OR " +
"DocumentClassification.classid = 2 ) AND " +
"DocumentClassification.userid < 0 AND " +
"MatterDataset.matterid = \'100\' ) AS d1 " +
"LEFT JOIN PrivilegeLog ON " +
"d1.id = PrivilegeLog.documentparentid AND " +
"d1.matterid = PrivilegeLog.matterid " +
"WHERE PrivilegeLog.privilegelogitemid IS NULL " +
"AND MatterDataset.matterid = \'100\' " +
"ORDER BY d1.id LIMIT 1 ;") ;
What a messy SQL statement (sorry)! I don't know SQLite, but why not simplify to:
SELECT DISTINCT md.id, md.sourcefullfilepath, md.sourcefilepath, md.sourcefilename,
dc.classificationid, dc.classid, dc.userid
FROM MatterDataset md
LEFT JOIN DocumentClassification dc
ON dc.documentid = md.id
AND (dc.classid = 1 OR dc.classid = 2 )
AND dc.userid < 0
LEFT JOIN PrivilegeLog pl
ON md.id = pl.documentparentid
AND md.matterid = pl.matterid
WHERE pl.privilegelogitemid IS NULL
AND md.matterid = \'100\'
ORDER BY md.id LIMIT 1 ;
I was uncertain whether you wanted to LEFT JOIN or INNER JOIN to DocumentClassification (using LEFT JOIN and then put requirements on classid and userid in the WHERE statement is - in my opinion - contradictory). If DocumentClassification has to exist, then change to INNER JOIN and put the references to classid and userid into the WHERE clause, if DocumentClassification may or may not exist in your result set, then keep the query as I suggested above.
I went back and started over. The SQL syntax, while it worked outside Java, simply seemed too complex for the JDBC driver. This cleaned-up revision seems to work:
SELECT DISTINCT
MatterDataset.id, MatterDataset.sourcefullfilepath, MatterDataset.sourcefilepath,
MatterDataset.sourcefilename
FROM MatterDataset , DocumentClassification
ON DocumentClassification.documentid = MatterDataset.id
AND MatterDataset.matterid = DocumentClassification.matterid
LEFT JOIN PrivilegeLog ON MatterDataset.id = PrivilegeLog.documentparentid
AND MatterDataset.matterid = PrivilegeLog.matterid
WHERE PrivilegeLog.privilegelogitemid IS NULL
AND MatterDataset.matterid = '100'
AND (DocumentClassification.classid = 1 OR DocumentClassification.classid = 2)
AND DocumentClassification.userid = -99
ORDER BY MatterDataset.id LIMIT 1;
A nice lesson in: just because you can in SQL doesn't mean you should.
What this statement does is essentially locates items in the MatterDataset Table that are NOT in the PrivilegeLog table. The LEFT JOIN and IS NULL syntax locate the items that are "missing." That is, i want to find items that are in MatterDataset but not yet in PrivilegeLog and return those items.
I'm trying to write a small function that gets an id u (integer), and returns the number of friends u have with distance <=3 (the friends information is stored in the table Likes: Likes(uid1, uid2) means: uid1 likes uid2).
I wrote this simple query:
String likesDistance =
"SELECT uid2 " + //DISTANCE = 1
"FROM Likes " +
"WHERE uid1 = ? " +
"UNION " +
"SELECT L2.uid2 " + //DISTANCE = 2
"FROM Likes L1, Likes L2 " +
"WHERE L1.uid1 = ? and L1.uid2 = L2.uid1 " +
"UNION "+
"SELECT L3.uid2 " + //DISTANCE = 3
"FROM Likes L1, Likes L2 , Likes L3 " +
"WHERE L1.uid1 = ? and L1.uid2 = L2.uid1 and L2.uid2 = L3.uid1 ";
Then, I do the following:
PreparedStatement pstmt2 = con.prepareStatement(likesDistance);
pstmt1.setInt(1, u);
pstmt1.setInt(2, u);
System.out.println("2");
pstmt1.setInt(3, u);
System.out.println("3");
pstmt1.setInt(4, u);
pstmt1.setInt(5, u);
Where u, as mention before, is an integer passed to the function.
All this code is within a 'try' block. When I try to run it, it prints the first printout ("2"), but not the second one ("3"), and I get the following exception message:
The column index is out of range: 3, number of columns: 2.
Why is it like this, and how can I change it to work as I want?
Thanks a lot.
Copy and paste? Guess you wanted to set parameters for statement 2.
Your prepared statement is pstmt2.
You are setting properties on pstmt1.
Try setting the properties on pstmt2 and it should work.
Though you set properties on pstmt2 you get exception as there are only three placeholders in the statement and you are setting for five placeholders.