I'm not sure if this is possible, but I'm wondering if there is a way to create a single criteria query and return multiple counts based on unique restrictions.
Example
Criteria criteria = session.createCriteria(EmployeeProfile.class);
Integer pAccrualBalance = ((Number)criteria
.add(Restrictions.gt("pAccrualBalance", BigDecimal.ZERO))
.setProjection(Projections.rowCount()).uniqueResult()).intValue();
Integer employeeCount = ((Number)criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
logger.info("verify pAccrualBalance import count " + pAccrualBalance);
logger.info("verify employee import count " + employeeCount);
The problem with this code is the pAccrualBalance restriction restricts the employeeCount result set. I'd like to get the unrestricted count for employeeCount without having to do a separate query.
You'll need to join to the table twice... Whilst this can be done with Criteria, it's often easier in HQL.
select count(ep1.id), count(ep2.id)
from EmployeeProfile as ep1
join EmployeeProfile as ep2
where ep2.pAccrualBalance > 0
Related
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.
Can you please share me code snippet to be written via JPA in order to generate the below sql query
SELECT COUNT(*) FROM Customer c
WHERE c.countryId ='Canada' AND
c.lanuguage ='ENG' AND
ROW_NUM <=10;
Because I tried in the below way. But MaxResults is not getting applied it seems as I can able to recieve the count more than 10.
Query query = em.createQuery("SELECT COUNT(c) FROM Customer c where c.countryId ='Canada' and c.lanuguage ='ENG'");
query.setMaxResults(10);
long customerCount = (Long)query.getSingleResult();
Select on count will always return a single value. If you want to have a count lower than 10, add HAVING.
SELECT COUNT(c) AS
FROM Customer c
WHERE c.countryId='Canada' and c.language='END'
HAVING COUNT(c)<=10
This is my sql command:
SELECT GIG.GIG_DESCRIPTION, VENUES.VENUE_NAME, BAND.BAND_NAME,
CASE WHEN GIG.USERID = 0 THEN '--CREATED BY THE BAND--' ELSE USERS.USERNAME END,
GIG.GIG_DATE
from GIG
INNER JOIN VENUES ON GIG.VENUEID = VENUES.VENUEID
INNER JOIN BAND ON GIG.BANDID = BAND.BANDID
INNER JOIN USERS ON GIG.USERID = USERS.USERID
WHERE GIG.USERID != 0
AND GIG.GIGID=" + gigID;
I'm using this query to return some values for a java object. In the Gig table sometimes the userid will equal 0, I'm getting a null pointer exception when I try and return a row with the userid equal to 0. I think I can get rid of the error if that last inner join on the users isn't run if a certain condition isn't true. Can I use a where clause that only effects the last join? How would I do that?
I think that you need link the USERS table using a LEFT JOIN clause in order to not discard the results with userid = 0 and remove the GIG.USERID != 0 on the WHERE clause:
SELECT GIG.GIG_DESCRIPTION, VENUES.VENUE_NAME, BAND.BAND_NAME,
CASE WHEN GIG.USERID = 0 THEN '--CREATED BY THE BAND--' ELSE
USERS.USERNAME END,
GIG.GIG_DATE
from GIG
INNER JOIN VENUES ON GIG.VENUEID = VENUES.VENUEID
INNER JOIN BAND ON GIG.BANDID = BAND.BANDID
LEFT JOIN USERS ON GIG.USERID = USERS.USERID
WHERE GIG.GIGID=" + gigID;
You can have multiple conditions in a join, try something like
INNER JOIN USERS ON GIG.USERID = USERS.USERID AND GIG.USERID != 0
WHERE ...
The where clause is applied to the result of the joins, so it can't be limited to just the last inner join.
You can add your condition to the where clause, of course. But you should keep in mind that it will affect the whole join.
I would suggest you add your condition to your on clause for the particular join.
I am getting ORA-01795 error in my Java code while executing more than 1000 records in IN clause.
I am thinking to break it in the batch of 1000 entries using multiple IN clause separated by OR clause like below:
select * from table_name
where
column_name in (V1,V2,V3,...V1000)
or
column_name in (V1001,V1002,V1003,...V2000)
I have a string id's like -18435,16690,1719,1082,1026,100759... which gets generated dynamically based on user selection. How to write a logic for condition like 1-1000 records ,1001 to 2000 records etc in Java. Can anyone help me here?
There are three potential ways around this limit:
1) As you have already mentioned: split up the statement in batches of 1000
2) Create a derived table using the values and then join them:
with id_list (id) as (
select 'V1' from dual union all
select 'V2' from dual union all
select 'V3' from dual
)
select *
from the_table
where column_name in (select id from id_list);
alternatively you could also join those values - might even be faster:
with id_list (id) as (
select 'V1' from dual union all
select 'V2' from dual union all
select 'V3' from dual
)
select t.*
from the_table t
join id_list l on t.column_name = l.id;
This still generates a really, really huge statement, but doesn't have the limit of 1000 ids. I'm not sure how fast Oracle will parse this though.
3) Insert the values into a (global) temporary table and then use an IN clause (or a JOIN). This is probably going to be the fastest solution.
With so many values I'd avoid both in and or, and the hard-parse penalty of embedded values, in the query if at all possible. You can pass an SQL collection of values and use the table() collection expression as a table you can join your real table to.
This uses a hard-coded array of integers as an example, but you can populate that array from your user input instead. I'm using the built-in collection type definitions, like sys.odcinumberlist, which us a varray of numbers and is limited to 32k values, but you can define your own table type if you prefer or might need to handle more than that.
int[] ids = { -18435,16690,1719,1082,1026,100759 };
ArrayDescriptor aDesc = ArrayDescriptor.createDescriptor("SYS.ODCINUMBERLIST", conn );
oracle.sql.ARRAY ora_ids = new oracle.sql.ARRAY(aDesc, conn, ids);
sql = "select t.* "
+ "from table(?) a "
+ "left join table_name t "
+ "on t.column_name = a.column_value "
+ "order by id";
pStmt = (OraclePreparedStatement) conn.prepareStatement(sql);
pStmt.setArray(1, ora_ids);
rSet = (OracleResultSet) pStmt.executeQuery();
...
Your array can have as many values as you like (well, as many as the collection type you use and your JVM's memory can handle) and isn't subject to the in list's 1000-member limit.
Essentially table(?) ends up looking like a table containing all your values, and this is going to be easier and faster than populating a real or temporary table with all the values and joining to that.
Of course, don't really use t.*, list the columns you need; I'm assuming you used * to simolify the question...
(Here is a more complete example, but for a slightly different scenario.)
I very recently hit this wall myself:
Oracle has an architectural limit of a maximum number of 1000 terms inside an IN()
There are two workarounds:
Refactor the query to become a join
Leave the query as it is, but call it multiple times in a loop, each call using less than 1000 terms
Option 1 depends on the situation. If your list of values comes from a query, you can refactor to a join
Option 2 is also easy, but less performant:
List<String> terms;
for (int i = 0; i <= terms.size() / 1000; i++) {
List<String> next1000 = terms.subList(i * 1000, Math.min((i + 1) * 1000, terms.size());
// build and execute query using next1000 instead of terms
}
In such situations, when I have ids in a List in Java, I use a utility class like this to split the list to partitions and generate the statement from those partitions:
public class ListUtils {
public static <T> List<List<T>> partition(List<T> orig, int size) {
if (orig == null) {
throw new NullPointerException("The list to partition must not be null");
}
if (size < 1) {
throw new IllegalArgumentException("The target partition size must be 1 or greater");
}
int origSize = orig.size();
List<List<T>> result = new ArrayList<>(origSize / size + 1);
for (int i = 0; i < origSize; i += size) {
result.add(orig.subList(i, Math.min(i + size, origSize)));
}
return result;
}
}
Let's say your ids are in a list called ids, you could get sublists of size at most 1000 with:
ListUtils.partition(ids, 1000)
Then you could iterate over the results to construct the final query string.
I'm writing a simple query in my java code using eclipselink v2.3.
This query must simply return a String and two integers, nothing strange I think, or at least I thought,
The query I'm building is the following:
q = entityManager.createQuery(
"SELECT new com.myclass.CalculationQueryResult(transits.device.name,"
+ " SUM(case when transits.direction = 1 then 1 else 0 end) ,"
+ " SUM(case when transits.direction = 0 then 1 else 0 end)) from Transits_Log transits "
+ " where transits.device.name in :devices and transits.dateTime >= :startDate"
+ " and transits.dateTime < :endDate group by transits.device.name" + " order by transits.device.name",
CalculationQueryResult.class);
While it, obviuosly works in SQL Server (our native counterpart), this does not work in JPQL.
The two different (SUM -> CASE) clauses were strangely (at least for me that i'm quite new to JPA) equals to each other. So, I decided to take out the native SQL from the JPQL to investigate deeper and the problem was there. The generated SQL is this one:
SELECT t0.Name,
**SUM(CASE WHEN (t1.Direction = 1) THEN 1 ELSE 0 END)** ,
**SUM(CASE WHEN (t1.Direction = 1) THEN 1 ELSE 0 END)** FROM dbo.ZZZ t0,
YYYY t1
WHERE ((((t1.DeviceName IN ('XXXXX'))
AND (t1.DateTime >= {ts '2012-09-24 17:26:48.031'}))
AND (t1.DateTime < {ts '2012-09-24 18:26:48.031'}))
AND (t0.Name = t1.DeviceName)) GROUP BY t0.Name
ORDER BY t0.Name ASC
As you can see, the SQL generated statement are wrong on the first two lines 'cause the first SUM and the second one should be one the opposite of the other while they're not.
Am I doing something extremely wrong? Does JPQL support multiple nested CASE and SUM? Are there any way to circumnavigate the error(if is the case) without having to write directly native SQL code?
That is very odd. Are you sure your JPQL is correct and compile/deployed?
Can you try the 2.4 release?
If it still occurs, please log a bug.