Union query with multiple selects post java 8 - java

Here is a query that I want to try out in MySQL
SELECT A.x
FROM A
WHERE A.y = 'P'
UNION
SELECT A.x
FROM A
WHERE A.y = 'Q'
The above is a cut-down, much simpler version of the original query that I am trying. In my original query, each SELECT statement involves multiple tables with INNER JOIN
If the possible number of values in 'y' column of table 'A' that I need to query upon is 'n', then my query will involve doing 'n-1' unions on 'n' SELECT statements
I know that JOOQ can do union of multiple SELECT statements. But is there a good way to do this post Java 8 style? maybe using Steam.collect()?
This is what I have but wondering if I could do better
String firstValueToQuery = valuesToQuery.get(0);
Select<Record5<UUID, UUID, String, Integer, String>> selectQuery = getSelectQueryForValue(firstValueToQuery);
valuesToQuery.stream()
.skip(1)
.forEach(valueToQuery -> selectQuery.unionAll(getSelectQueryForValue(valueToQuery)));
selectQuery.fetchStream();
Here is how I implement getSelectQueryForValue
private Select<Record5<UUID, UUID, String, Integer, String>> getSelectQueryForValue(String valueToQuery) {
return jooq.select(
A.P,
A.Q,
A.R,
A.S,
A.T)
.from(A)
.where(A.Y.eq(valueToQuery));
}
PS: I understand that I could rather use the 'IN' clause like below
SELECT A.x
FROM A
WHERE A.y IN ('P','Q',...)
But with my current data distribution in the database, MySQL is using a sub-optimal query plan. Thus using UNION so that the database implicitly prefers a faster query plan by making use of the right index

The idiomatic approach here would be as follows (using JDK 9 API):
try (Stream<Record5<UUID, UUID, String, Integer, String>> stream = valuesToQuery
.stream()
.map(this::getSelectQueryForValue)
.reduce(Select::union)
.stream() // JDK 9 method
.flatMap(Select::fetchStream)) {
...
}
It uses the useful Optional.stream() method, which was added in JDK 9. In JDK 8, you could do this instead:
valuesToQuery
.stream()
.map(this::getSelectQueryForValue)
.reduce(Select::union)
.ifPresent(s -> {
try (Stream<Record5<UUID, UUID, String, Integer, String>> stream =
s.fetchStream()) {
...
}
})
I blogged about this in more detail here.

Related

Coalesce jsonArrayAgg to empty array in jOOQ

What is the equivalent for coalesce in the new jOOQ 3.14 SQL/JSON supporting version (in PostgreSQL)?
select coalesce(json_agg(t.*), '[]'::json)
from (select 'test' as mycol where 1 = 2) t;
The following unfortunately fails with the error "COALESCE types json and uuid[] cannot be matched".
coalesce(jsonArrayAgg(mycol), emptyArray<type>())
JSON.json() or JSONB.jsonb(), e.g.:
coalesce(jsonArrayAgg(mycol), inline(json("[]")))
coalesce(jsonbArrayAgg(mycol), inline(jsonb("[]")))
But you can also use the jsonArray() or jsonbArray() constructors:
coalesce(jsonArrayAgg(mycol), jsonArray())
coalesce(jsonbArrayAgg(mycol), jsonbArray())

Using IN clause for Oracle error exceeding 1000 columns inside JRXML [duplicate]

Is there a workaround for
'ORA-01795: maximum number of expressions in a list is 1000 error'
I have a query and it is selecting fields based on the value of one field. I am using the in clause and there are 10000+ values
example:
select field1, field2, field3
from table1
where name in
(
'value1',
'value2',
...
'value10000+'
);
Every time I execute the query I get the ORA-01795: maximum number of expressions in a list is 1000 error. I am trying to execute the query in TOAD, no difference, the same error. How would I modify the query to get it to work?
Just use multiple in-clauses to get around this:
select field1, field2, field3 from table1
where name in ('value1', 'value2', ..., 'value999')
or name in ('value1000', ..., 'value1999')
or ...;
Some workaround solutions are:
1. Split up IN clause
Split IN clause to multiple IN clauses where literals are less than 1000 and combine them using OR clauses:
Split the original "WHERE" clause from one "IN" condition to several "IN" condition:
Select id from x where id in (1, 2, ..., 1000,…,1500);
To:
Select id from x where id in (1, 2, ..., 999) OR id in (1000,...,1500);
2. Use tuples
The limit of 1000 applies to sets of single items: (x) IN ((1), (2), (3), ...).
There is no limit if the sets contain two or more items: (x, 0) IN ((1,0), (2,0), (3,0), ...):
Select id from x where (x.id, 0) IN ((1, 0), (2, 0), (3, 0),.....(n, 0));
3. Use temporary table
Select id from x where id in (select id from <temporary-table>);
I ran into this issue recently and figured out a cheeky way of doing it without stringing together additional IN clauses
You could make use of Tuples
SELECT field1, field2, field3
FROM table1
WHERE (1, name) IN ((1, value1), (1, value2), (1, value3),.....(1, value5000));
Oracle does allow >1000 Tuples but not simple values. More on this here,
https://community.oracle.com/message/3515498#3515498
and
https://community.oracle.com/thread/958612
This is of course if you don't have the option of using a subquery inside IN to get the values you need from a temp table.
One more way:
CREATE OR REPLACE TYPE TYPE_TABLE_OF_VARCHAR2 AS TABLE OF VARCHAR(100);
-- ...
SELECT field1, field2, field3
FROM table1
WHERE name IN (
SELECT * FROM table (SELECT CAST(? AS TYPE_TABLE_OF_VARCHAR2) FROM dual)
);
I don't consider it's optimal, but it works. The hint /*+ CARDINALITY(...) */ would be very useful because Oracle does not understand cardinality of the array passed and can't estimate optimal execution plan.
As another alternative - batch insert into temporary table and using the last in subquery for IN predicate.
Please use an inner query inside of the in-clause:
select col1, col2, col3... from table1
where id in (select id from table2 where conditions...)
There is another option: with syntax. To use the OPs example, this would look like:
with data as (
select 'value1' name from dual
union all
select 'value2' name from dual
union all
...
select 'value10000+' name from dual)
select field1, field2, field3
from table1 t1
inner join data on t1.name = data.name;
I ran into this problem. In my case I had a list of data in Java where each item had an item_id and a customer_id. I have two tables in the DB with subscriptions to items respective customers. I want to get a list of all subscriptions to the items or to the customer for that item, together with the item id.
I tried three variants:
Multiple selects from Java (using tuples to get around the limit)
With-syntax
Temporary table
Option 1: Multiple Selects from Java
Basically, I first
select item_id, token
from item_subs
where (item_id, 0) in ((:item_id_0, 0)...(:item_id_n, 0))
Then
select cus_id, token
from cus_subs
where (cus_id, 0) in ((:cus_id_0, 0)...(:cus_id_n, 0))
Then I build a Map in Java with the cus_id as the key and a list of items as value, and for each found customer subscription I add (to the list returned from the first select) an entry for all relevant items with that item_id. It's much messier code
Option 2: With-syntax
Get everything at once with an SQL like
with data as (
select :item_id_0 item_id, :cus_id_0 cus_id
union all
...
select :item_id_n item_id, :cus_id_n cus_id )
select I.item_id item_id, I.token token
from item_subs I
inner join data D on I.item_id = D.item_id
union all
select D.item_id item_id, C.token token
from cus_subs C
inner join data D on C.cus_id = D.cus_id
Option 3: Temporary table
Create a global temporary table with three fields: rownr (primary key), item_id and cus_id. Insert all the data there then run a very similar select to option 2, but linking in the temporary table instead of the with data
Performance
This is not a fully-scientific performance analysis.
I'm running against a development database, with slightly over 1000 rows in my data set that I want to find subscriptions for.
I've only tried one data set.
I'm not in the same physical location as my DB server. It's not that far away, but I do notice if I try from home over the VPN then it's all much slower, even though it's the same distance (and it's not my home internet that's the problem).
I was testing the full call, so my API calls another (also running in the same instance in dev) which also connects to to the DB to get the initial data set. But that is the same in all three cases.
YMMV.
That said, the temporary table option was much slower. As in double so slow. I was getting 14-15 seconds for option 1, 15-16 for option 2 and 30 for option 3.
I'll try them again from the same network as the DB server and check if that changes things when I get the chance.
I realize this is an old question and referring to TOAD but if you need to code around this using c# you can split up the list through a for loop. You can essentially do the same with Java using subList();
List<Address> allAddresses = GetAllAddresses();
List<Employee> employees = GetAllEmployees(); // count > 1000
List<Address> addresses = new List<Address>();
for (int i = 0; i < employees.Count; i += 1000)
{
int count = ((employees.Count - i) < 1000) ? (employees.Count - i) - 1 : 1000;
var query = (from address in allAddresses
where employees.GetRange(i, count).Contains(address.EmployeeId)
&& address.State == "UT"
select address).ToList();
addresses.AddRange(query);
}
Hope this helps someone.
there is also another way to resolve this issue. lets say you have two tables Table1 and Table2. and it is required to fetch all entries of Table1 not referred/present in Table2 using Criteria query. So go ahead like this...
List list=new ArrayList();
Criteria cr=session.createCriteria(Table1.class);
cr.add(Restrictions.sqlRestriction("this_.id not in (select t2.t1_id from Table2 t2 )"));
.
.
. . . It will perform all the subquery function directly in SQL without including 1000 or more parameters in SQL converted by Hibernate framework. It worked for me. Note: You may need to change SQL portion as per your requirement.
Operato union
select * from tableA where tableA.Field1 in (1,2,...999)
union
select * from tableA where tableA.Field1 in (1000,1001,...1999)
union
select * from tableA where tableA.Field1 in (2000,2001,...2999)
**Divide a list to lists of n size**
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
public final class PartitionUtil<T> extends AbstractList<List<T>> {
private final List<T> list;
private final int chunkSize;
private PartitionUtil(List<T> list, int chunkSize) {
this.list = new ArrayList<>(list);
this.chunkSize = chunkSize;
}
public static <T> PartitionUtil<T> ofSize(List<T> list, int chunkSize) {
return new PartitionUtil<>(list, chunkSize);
}
#Override
public List<T> get(int index) {
int start = index * chunkSize;
int end = Math.min(start + chunkSize, list.size());
if (start > end) {
throw new IndexOutOfBoundsException("Index " + index + " is out of the list range <0," + (size() - 1) + ">");
}
return new ArrayList<>(list.subList(start, end));
}
#Override
public int size() {
return (int) Math.ceil((double) list.size() / (double) chunkSize);
}
}
Function call :
List<List<String>> containerNumChunks = PartitionUtil.ofSize(list, 999)
more details: https://e.printstacktrace.blog/divide-a-list-to-lists-of-n-size-in-Java-8/
There's also workaround doing disjunction of your array, worked for me as other solutions were hard to implement using some old framework.
select * from tableA where id = 1 or id = 2 or id = 3 ...
But for better perfo, I would use Nikolai Nechai's solution with unions, if possible.
Pass the list and the number of records needs to return in the loop most cases = 999.
List<List<Long>> getSubLists = batchList(inputList, 999);
List<Long> newList = new ArrayList<>();
for (List<Long> subSet : getSubLists) { newList.addALL(daoCall) // add in the required list in loop }
public static <T> List<List<T>> batchList(List<T> inputList, final int maxSize) {
List<List<T>> sublists = new ArrayList<>();
final int size = inputList.size();
for (int i = 0; i < size; i += maxSize) {
sublists.add(new ArrayList<>(inputList.subList(i, Math.min(size, i + maxSize))));
}
return sublists;
}
Use Tuple :
Let's suppose input is :
List<Long> userIdList = Arrays.asList(100L,200L,300L);
StringBuilder tuple = new StringBuilder();
for(Long userId : userIdList) {
tuple.append("(1,").append(userId).append("),");
}
tuple.deleteCharAt(tuple.length()-1);
Output will be :
(1,100),(1,200),(1,300)
And we can pass this to below query like this (And YES, we can pass more than 1000 elements):
SELECT * FROM MyTable WHERE (1, USR_ID) IN ((1,100),(1,200),(1,300));
ORA-01795: maximum number of expressions in a list is 1000.
Issue:
When user selects long list of values (greate or qual to 1000 values/expression) for IN/OR list of where clause, system throws error: “ORA-01795: maximum number of expressions in a list is 1000”
Root Cause:
Oracle IN / OR list has limit of 1000 (actually its 999) number of expression/value list.
Proposed Solution:
You need to split the list of expressions into multiple sets (using OR) and each should be less than 1000 list/expressoin combine using IN / Or list.
Example:
Suppose you have a table ABC with column ZIP of CLOB type and table contains more than 1000 rows.
You need to break them in multiple list, like shown below:
( ZIP IN (1,2,3,.........N999)
OR ZIP IN (1000,1001,......N999)
.....
.....
)
If you are using Hibernate Query Language(HQL) in your repository class and using IN clause, Oracle database is not going to be let you run your query. Because you are giving more then 1000 value. Oracle in clause limit is 1000. So you cannot give more then 1000 value to your IN clause.
In java, you can use this codes. It's going to be very help full. You can call your services like this, and in this example limit is 750.
Don't write more than 1000. Happy coding.
import java.util.List;
public class ListUtils {
public static int getPeriodCount(List<?> list, int limit) {
int size = list.size();
int leap = size % limit;
int periodCount = leap != 0 ? (size / limit) + 1 : (size / limit);
return periodCount;
}
}
public class MainClass {
private static final int LIMIT = 750;
private List<GivenObject> getList(List<Integer> numbers) {
if (CollectionUtils.isEmpty(numbers)) {
return Collections.emptyList();
}
List<Integer> resultList= new ArrayList<>();
int startIndex = 0;
int periodCount = ListUtils.getPeriodCount(numbers, LIMIT);
for (int i = 1; i <= periodCount; i++) {
List<Integer> collectedNumbers = numbers.stream()
.skip(startIndex)
.limit(LIMIT)
.collect(Collectors.toList());
resultList.addAll(collectedNumbers);
startIndex += LIMIT;
}
return resultList;
}
}

Do MERGE query in java neo4j

I have users 1 and 7 and I want to merge a relationship friend between them.The cypher query works fine but when i try to write it in java it is not working
how to do this cypher query using java:
MATCH (a:user), (b:user)
WHERE a.ID="1" AND b.ID="7"
Merge (a)-[:friend]->(b)
return a, b
I tried to this but it is not working
Result result = db.execute( "MERGE (a:user {ID:'7'})-[:friend]->(b:user {ID:'5'}) return a.ID,b.ID") )
also I tried this
Result result = db.execute(MATCH (a:user), (b:user) WHERE a.ID='1' AND b.ID='7' Merge (a)-[: friend]->(b) return a, b)
Both are not working
You have several Java syntax errors.
The first statement has an extra ) at the end. It also needs to end with a semicolon.
the seconds statement needs to wrap the Cypher query in double-quotes (to be a legal Java String). It also needs to end with a semicolon.
Here is complete code snippet that should work in your case:
try (Transaction tx = db.beginTx()) {
// Query, with params
String query = "MATCH (a:user {ID: {aId}}), (b:user {ID: {bId}}) " +
"MERGE (a)-[:friend]->(b) " +
"RETURN a, b";
// Params
Map<String, Object> params = new HashMap<>();
params.put("aId", 1);
params.put("bId", 7);
// Execute in try-with-resource, to ensure that result will be closed
try (Result result = db.execute(query, params)) {
// read result here
}
tx.success();
}
Notable things here:
Transaction is executed in try-with-resource block
Query is using params - {aId} and {bId}
Params are passed as Map with query to database
Query ix executed in try-with-resource block, to ensure that resource will be closed
Probably you should checkout Using Neo4j from Java developer resources, to get more examples and information on that topic.

How do I do "SELECT something IN (...)" with jooq?

I'm trying to do the following with Jooq and can't for the life of me figure out how to do it properly:
select name, id in (
select capability_id
from a.capabilities_users
where user_id = ?)
from a.capabilities;
Basically I want to get all items (capabilities) and know whether each one applies to a particular user. It seems that all the condition type operators (like greater than or in) can only be used in the where and not the select. And I can't think of how else to express this.
Worst case, I can do a select count and then do the boolean logic in Java, but I was hoping to use fetchMap.
Depending on your database and schema meta data, a LEFT JOIN might be a better choice than a predicate in the projection. You should of course verify this in the execution plan.
Solving this with a LEFT JOIN:
-- NVL2 is Oracle syntax.
-- jOOQ will emulate NVL2 using CASE, if it's not available in your database
SELECT c.name, NVL2(cu.capability_id, 1, 0)
FROM a.capabilities c
LEFT OUTER JOIN a.capabilities_users cu
ON (c.id = cu.capability_id
AND cu.user_id = ?)
The above assumes, of course, that there is a unqiue constraint on cu(user_id, capability_id). This would then translate into jOOQ as such:
Capabilities c = CAPABILITIES.as("c");
CapabilitiesUsers cu = CAPABILITIES_USERS.as("cu");
Field<String> key = c.NAME.as("key");
Field<Boolean> value = nvl2(
CAPABILITIES_USER.CAPABILITY_ID, true, false
).as("value");
Map<String, Boolean> map =
DSL.using(configuration)
.select(key, value)
.from(c)
.leftOuterJoin(cu)
.on(c.ID.eq(cu.CAPABILITY_ID))
.and(cu.USER_ID.eq(...))
.fetchMap(key, value);
Solving this with a predicate in the projection:
If you really prefer a predicate in the projection, you might try DSL.field(Condition), which allows for precisely this:
Field<String> key = CAPABILITIES.NAME.as("key");
Field<Boolean> value = field(
CAPABILITIES.ID.in(
select(CAPABILITY_ID)
.from(CAPABILITIES_USERS)
.where(CAPABILITIES_USERS.USER_ID.eq(...))
)
).as("value");
Map<String, Boolean> map =
DSL.using(configuration)
.select(key, value)
.from(CAPABILITIES)
.fetchMap(key, value);
Note that if you're using a standards-compliant database, which doesn't allow for predicates to be treated as columns, DSL.field(Condition) will render an equivalent CASE statement for you.

Caused by: java.sql.SQLException: ORA-01795: maximum number of expressions in a list is 1000?

I am using java/hibernate/Oracle. i have a list with more than 3000 entries. if i pass whole list i get below exception.
Caused by: java.sql.SQLException: ORA-01795: maximum number of expressions in a list is 1000
to solve the issue i am splitting the list into sublists, each sublist will have 1000 entries. for every thousand entries i am firing a query. it is working fine.
Please clarify me, is there any better solution?
Thanks!
It's an Oracle limitation, which is why it's got an Oracle error code... although you could argue that it's a limitation of Hibernate that it doesn't transparently work around it :)
You should probably put the list into a temporary table and join on that, assuming Oracle doesn't have anything like SQL Server's table-valued parameters. (Or you could break your query up into multiple queries, potentailly - it depends on what you're doing.)
The Exception-Text lets me believe that this is coming from the Oracle-Database - look at the error-number...
You cant have a list with more than 1000 elements in a single "where" condition if you are working with Oracle DB. So you can chop down your "where" condition in multiple "where" conditions and join them with "or" clause.
If you are using hibernate Criteria, you can use below Java method to do this.
Just replace your code where ever you used
criteria.add(Restrictions.in(propertyName, mainList));
with
addCriteriaIn(propertyName, mainList, criteria);
which the method is :
private void addCriteriaIn (String propertyName, List<?> list,Criteria criteria)
{
Disjunction or = Restrictions.disjunction();
if(list.size()>1000)
{
while(list.size()>1000)
{
List<?> subList = list.subList(0, 1000);
or.add(Restrictions.in(propertyName, subList));
list.subList(0, 1000).clear();
}
}
or.add(Restrictions.in(propertyName, list));
criteria.add(or);
}
there is also another way to resolve this issue. lets say you have two tables Table1 and Table2. and it is required to fetch all entries of Table1 not referred/present in Table2 using Criteria query.
So go ahead like this...
List list=new ArrayList();
Criteria cr=session.createCriteria(Table1.class);
cr.add(Restrictions.sqlRestriction("this_.id not in (select t2.t1_id from Table2 t2 )"));
.
.
List list=new ArrayList();
Criteria cr=session.createCriteria(Table1.class);
cr.add(Restrictions.sqlRestriction("this_.id not in (select t2.t1_id from Table2 t2 )"));
list=cr.list();
.
.
.
It will perform all the subquery finction directly in SQL without including 1000 or more parameters in SQL converted by Hibernate framework. It worked for me.
Note: You may need to change SQL portion as per your requirement.
i have used below and it works perfectly for me
if (myArgList.size() > 1000) {
while(myArgList.size()>0)
{
tempList = myArgList;
def listSize= tempList.size()
if(listSize>1000){
def subList = tempList.subList(0, 1000);
def tcriteria = tableA.createCriteria()
result.addAll(tcriteria.listDistinct {
'in'('id', subList.collect{it.id})
fetchMode('tableBObj', FetchMode.JOIN)
})
myArgList.subList(0, 1000).clear();
}else{
def subList = myArgList.subList(0, listSize);
def ncriteria = tableA.createCriteria()
result.addAll(ncriteria.listDistinct {
'in'('id', subList.collect{it.id})
fetchMode('tableBObj', FetchMode.JOIN)
})
myArgList.clear();
}
}
} else {
def criteria = tableA.createCriteria()
result = criteria.listDistinct {
'in'('id', myArgList.collect{it.id})
fetchMode('tableBObj', FetchMode.JOIN)
}
}

Categories