Alternative for COALESCE when all its parameters is null - java

I am developing a web application (java-maven project) and there is a problem with the query I use. The UI of my web app has 2 search fields: PTT and ID.
The user needs to fill at least one of the fields to make a search. So both fields are nullable but not at the same time.
Before, I had only one field: PTR and it was showing a result array of size 52. (also getting the same number if
I execute select * from users where ptr='smthing' ). After that I added ID field and updated my query as below:
I execute this query in my webservice:
String query= "SELECT t.ptr, t.id ";
query+= "FROM users t ";
query+= "WHERE t.ptr = COALESCE(?, t.ptr) AND " ;
query+= "t.id = COALESCE(?, t.id) ";
and set the fields with the help of Prepared Statement.
Now if the ptr field is filled, but id field is left blank (this can be null or empty string) on the UI and user makes a search, result array size becomes 30. I compared with database
and it does not fetch the rows where ID is null. So coalesce is not what I need when both of its parameters (?, t.ptr) is null.
How can I fix this problem, any suggestions?

I think the logic you want is:
WHERE (t.ptr = ? OR ? IS NULL) AND
(t.id = ? OR ? IS NULL)
I would recommend using named parameters, so you don't have to pass them in twice.

Check this statement:
String query= "SELECT t.ptr, t.id ";
query+= "FROM users t ";
query+= "WHERE (t.ptr = ? OR 1 = ?)"
query+= " AND " ;
query+= "(t.id = ? OR 1 = ?)";
You see that for each of t.id and t.ptr there is a counterpart parameter. In total there will be 4 parameters.
You say that at least 1 of t.id or t.ptr has a valid value, so there are 2 cases:
[1] t.id and t.ptr both have valid values.
For both the counterpart parameters you pass 0 and the query becomes:
"SELECT t.ptr, t.id FROM users t WHERE (t.ptr = valueptr OR 1 = 0) AND (t.id = valueid OR 1 = 0)"
In the WHERE part:
t.ptr = valueptr OR 1 = 0 is equivalent to t.ptr = valueptr, and
t.id = valueid OR 1 = 0 is equivalent to t.id = valueid,
and the query finally becomes:
"SELECT t.ptr, t.id FROM users t WHERE t.ptr = valueptr AND t.id = valueid"
[2] from t.id or t.ptr only one has a valid value, let's say this is t.ptr.
For the counterpart of t.ptr you pass 0, for t.id you pass -1 (or any other non existing value) and for the counterpart of t.id you pass 1 and the query becomes:
"SELECT t.ptr, t.id FROM users t WHERE (t.ptr = valueptr OR 1 = 0) AND (t.id = -1 OR 1 = 1)"
In the WHERE part:
t.ptr = valueptr OR 1 = 0 is equivalent to t.ptr = valueptr, and
t.id = -1 OR 1 = 1 is equivalent to true because 1 = 1 is always true,
and the query finally becomes:
"SELECT t.ptr, t.id FROM users t WHERE (t.ptr = valueptr OR 1 = 0)"
equivalent to:
"SELECT t.ptr, t.id FROM users t WHERE (t.ptr = valueptr)"
(In the case where only t.id has a valid value then you pass an invalid value for t.ptr and 1 for its counterpart and for the counterpart if t.id you pass 0.)
Maybe it seems complicated but it's working and it can be extended for more than 2 columns.

Oracle has build function for this. But it's hard to understand how to use its.
lnnvl(a = b) = true if (a != b ) or ( a = null ) or (b = null)
in your case
WHERE lnnvl(t.ptr != ? ) AND lnnvl( t.id != ?)
LNNVL

Related

An expression of non-boolean type specified in a context where a condition is expected. SQL not returning values when executed through API

I have query that returns a result when executed in the database. But it does not return anything when I execute it from my application, it just keep on executing.
Below is the code snippet I have:
#Query(value = "select distinct c.ROWID_OBJECT, rc.CMPNY_NMBR from ecm_ors.dbo.c_b_pty rc " +
"left join ecm_ors.dbo.c_b_pty_rel rel on rc.ROWID_OBJECT = rel.PTY_ID_1 " +
"left join ecm_ors.dbo.c_b_pty c on rel.PTY_ID_2 =?1 " +
"where rc.PTY_ROLE = 'L' and c.PTY_ROLE = 'L' and rel.REL_TP = 'is parent of' " +
"and rc.HUB_STATE_IND = 1 and rel.HUB_STATE_IND = 1 and c.HUB_STATE_IND = 1 " +
"AND ?2 IS NULL OR c.CMPNY_NMBR =?2 AND rel.PTY_ID_2 = c.ROWID_OBJECT", nativeQuery = true)
List<Object[]> getCompanyList(String ptyId, String cmpNumber);
The same query shows the result when run it through the database
select distinct c.ROWID_OBJECT, rc.CMPNY_NMBR from ecm_ors.dbo.c_b_pty rc
left join ecm_ors.dbo.c_b_pty_rel rel on rc.ROWID_OBJECT = rel.PTY_ID_1
left join ecm_ors.dbo.c_b_pty c on rel.PTY_ID_2 = 493
where rc.PTY_ROLE = 'L' and c.PTY_ROLE = 'L' and rel.REL_TP = 'is parent of'
and rc.HUB_STATE_IND = 1 and rel.HUB_STATE_IND = 1 and c.HUB_STATE_IND = 1
and 02637 is null or c.CMPNY_NMBR =02637
and rel.PTY_ID_2 = c.ROWID_OBJECT
The problem I'm anticipating in this condition
?2 IS NULL OR c.CMPNY_NMBR =?2
Is it the kind of error:
An expression of non-boolean type specified in a context where a condition is expected
I'm not seeing any errors though, REST call keep rotates.
Not sure how to rectify this, please advice.

Optimizing a query using Stored Procedure

I am optimizing a code written a long time ago by a developer that I never met. I came across a method that requires modification. The first thing that came to mind is to use stored procedure. Maybe there are better ways of achieving this hence this question.
The code goes this way:
public void execute()
{
String query = "select a, b, e from table1";
....
ResultSet rs = stmt.executeQuery(query);
String query2 = null;
List<Integer> list1 = ....
List<Integer> list2 = ....
while(rs.next)
{
query2 = "select count(*) as rowcount from vw_view1 where f='" + rs.getString("a") + "' and d='" + rs.getString("b") + "'";
.....
ResultSet rs2 = stmt2.executeQuery(query2);
list1.add(rs2.getInt(rowcount));
query3 = "select count(*) as rowcount from vw_view1 where c='" + rs.getString("a") + "' and e='" + rs.getString("e") + "'";
.....
ResultSet rs3 = stmt3.executeQuery(query3);
list2.add(rs3.getInt(rowcount));
}
}
Apart from using a stored procedure, is there a better way of avoiding unnecessary trips to the database in this method.
Try this query, check if it gives you the same result. With this you should have in one query directly all the value you wanted
SELECT c,
SUM(CASE WHEN vw1.d = tb1.b THEN 1 ELSE 0 END) as rowcountListOne,
SUM(CASE WHEN vw1.e = tb1.e THEN 1 ELSE 0 END) as rowcountListTwo
from vw_view1 vw1
left join table1 tb1 on vw1.c=tb1.a
GROUP BY c
In the new case you posted you should actually join by c and f:
SELECT c, f,
SUM(CASE WHEN (vw1.d = tb1.b) AND (vw1.f=tb1.a) THEN 1 ELSE 0 END) as rowcountListOne,
SUM(CASE WHEN (vw1.e = tb1.e) AND (vw1.c=tb1.a) THEN 1 ELSE 0 END) as rowcountListTwo
FROM vw_view1 vw1
LEFT JOIN table1 tb1 on (vw1.c=tb1.a OR vw1.f=tb1.a)
GROUP BY c, f
You should check also what Lennart said about COUNT, I think I should add on those case some NULL management, but I can't test it right now.
Maybe is enough if you check vw1.f not null in the case (and vw1.c in the other):
CASE WHEN (vw1.f IS NOT NULL) AND (vw1.d = tb1.b) AND (vw1.f=tb1.a) THEN 1 ELSE 0 END
You could just perform the counts at the same time as your initial select, removing the need to run two further queries for every row in rs:
SELECT t.a, t.b, t.e, t1.RowCount1, t2.RowCount2
FROM table1 AS t
LEFT JOIN
( SELECT c, d, COUNT(*) AS RowCount1
FROM vw_view1
GROUP BY c, d
) AS t2
ON t2.c = t.a
AND t2.d = t.b
LEFT JOIN
( SELECT c, e, COUNT(*) AS RowCount2
FROM vw_view1
GROUP BY c, e
) AS t3
ON t3.c = t.a
AND t3.e = t.e;
The initial select will be slower, but the subsequent loop will be much more efficient.

Hibernate HQL issue expecting IDENT found "*"

I need get all data from relative table so I'm using something like this (i would use it in sql)
private static final String SELECT_OOPR_TO_SEND = "SELECT R.* " +
"FROM offerOrderProjectRel R, offerOrder O, project P " +
"WHERE P.id = R.project_id and O.id = R.offer_order_id " +
"and O.type = 'ORDER' and (P.status = 'PENDING' or P.status ='PROTECTED')" ;
;
#SuppressWarnings("unchecked")
public List<OfferOrderProjectRel> findAllOfferOrderToSendToSalesmans() {
Query q = getSession().createQuery(SELECT_OOPR_TO_SEND);
List<OfferOrderProjectRel> list = q.list();
return list;
}
After lauching this code i'm getting that error :
org.hibernate.hql.internal.ast.QuerySyntaxException: expecting IDENT,
found '**' near line 1, column 10 [SELECT R.* FROM offerOrderProjectRel
R, offerOrder O, project P WHERE P.id = R.project_id and O.id =
R.offer_order_id and O.type = 'ORDER' and (P.status = 'PENDING' or
P.status ='PROTECTED')]
So how can I obtain all data from column R with hibernate?
The method createQuery expects an HQL query string.
HQL is an object-oriented querying language.
HQL interprets SELECT R.* as select the member field * of the object R.
But * is not a member field of R. Is it?..
To select all the member fields of R use:
SELECT R
FROM offerOrderProjectRel R, offerOrder O, project P
WHERE P.id = R.project_id and O.id = R.offer_order_id
and O.type = 'ORDER' and (P.status = 'PENDING' or P.status ='PROTECTED')
you use SQL query, not hql query, so it should be
Query q = getSession().createSQLQuery(SELECT_OOPR_TO_SEND);
For people that received the "expecting IDENT found “*”" error when using org.springframework.data.jpa.repository.Query and found this question I'll add that you can change the nativeQuery flag to true:
#Query(value = "SELECT * FROM table1", nativeQuery = true)
List<Object> myFindAll();

SUM result value repeats even with case statement

I'm using posgresql as a database and java as programming language with hibernate. My problem is this query:
select cast(sum(CASE WHEN p.nropack > 0 THEN p.nropack ELSE 0 END) as integer),
cast(sum(CASE WHEN p.nropack < 0 THEN p.nropack ELSE 0 END) as integer),
cast(p.fechareg as date)
from pronostico p
inner join aeropuerto a on (a.idaeropuerto=p.idaeropuerto)
inner join ciudad c on (a.idciudad=c.idciudad)
inner join pais ps on (ps.idpais=c.idpais)
inner join continente ct on (ct.idcontinente=ps.idcontinente)
where c.idciudad=105
group by cast (p.fechareg as date);
As a result I get:
sum;sum;fechareg
30;-15;"2012-11-15"
But when I use it in my program:
public ArrayList<RepKardex> listarKardex(int ciud){
ciud=105;
ArrayList<RepKardex> listaKardex = new ArrayList<>();
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
String q = "select cast(sum( case when p.nropack > 0 then p.nropack ELSE 0 end ) as integer), "
+ "cast(sum( case when p.nropack < 0 then p.nropack ELSE 0 end ) as integer), "
+ "cast(p.fechareg as date) "
+ "from Pronostico p "
+ "inner join Aeropuerto a on (p.idaeropuerto = a.idaeropuerto) "
+ "inner join Ciudad c on (a.idciudad = c.idciudad) "
+ "inner join Pais ps on (c.idpais = ps.idpais) "
+ "inner join Continente ct on (ct.idcontinente = ps.idcontinente) "
+ "where c.idciudad = :ciud "
+ "group by cast(p.fechareg as date) ";
Query query = session.createSQLQuery(q);
query.setInteger("ciud", ciud);
List lista = query.list();
Iterator iter = lista.iterator();
while (iter.hasNext()) {
Object[] row = (Object[]) iter.next();
if (row!=null){
System.out.println("entrantes : "+(Integer)row[0]);
System.out.println("salientes : "+(Integer)row[1]);
RepKardex rep = new RepKardex((int)row[0],(int)row[1],(Date)row[2]);
listaKardex.add(rep);
}
}
tx.commit();
session.close();
return listaKardex;
}
It prints
entrantes: 30
salida: 30
Can someone help me figure out why it repeats the positive numbers even when I use the case statement inside the query? Thanks in advance.
You can simplify your query with LEAST and GREATEST:
SELECT sum(GREATEST(p.nropack, 0))::int AS entrantes
,sum(LEAST(p.nropack, 0))::int AS salida
,p.fechareg::date AS fechareg
from pronostico p
JOIN aeropuerto a ON a.idaeropuerto = p.idaeropuerto
JOIN ciudad c ON c.idciudad = a.idciudad
JOIN pais ps ON ps.idpais = c.idpais
JOIN continente ct ON ct.idcontinente = ps.idcontinente
where c.idciudad = 105
GROUP BY p.fechareg::date;
Other than that it looks just fine. I see no way the second column could return a positive number. Something else must go wrong here.
Edit:
Comment by #hvd helped to find that the client code seems to get confused by identical column names (both sum columns defaulted to "sum"). Explicit column aliases seem to fix this.

not all named parameters have been set hibernate in createSQLQuery

I am getting the error of not all named parameters have been set. Below is my code.
my SqlQuery which is running fine at mysql prompt, You can refer schema in the question SQL Query
SELECT t.*
FROM (
SELECT #lim := 2,
#cg := ''
) vars,
(select * from Table1 order by product,amount, make) t
WHERE CASE WHEN #cg <> product THEN #r := #lim ELSE 1 END > 0
AND (#r := #r - 1) >= 0
AND (#cg := product) IS NOT NULL
ORDER BY
product,amount, make
my java code
try {
context.dbl.startTransaction();
Session session = context.dbl.getSession();
//String sqlQuery = "from com.infibeam.inventoryservice.dbObjects.PopularBrandDO";
String sqlQuery = "SELECT t.* ";
sqlQuery=sqlQuery + "FROM (";
sqlQuery=sqlQuery + "SELECT #lim := 2,";
sqlQuery=sqlQuery + "#cg := ''";
sqlQuery=sqlQuery + ") vars, ";
sqlQuery=sqlQuery + "(select * from Table1 order by product,amount, make) t";
sqlQuery=sqlQuery + " WHERE CASE WHEN #cg <> product THEN #r := #lim ELSE 1 END > 0";
sqlQuery=sqlQuery + " AND (#r := #r - 1) >= 0 ";
sqlQuery=sqlQuery + " AND (#cg := product) IS NOT NULL ";
sqlQuery=sqlQuery + " ORDER BY product,amount, make";
//Query query = session.createQuery(sqlQuery);
SQLQuery query = session.createSQLQuery(sqlQuery);
listItems = query.list();
}catch(RuntimeException e) {
e.printStackTrace();
}
Below is the exception i am getting
org.hibernate.QueryException: Not all named parameters have been set: [] [SELECT t.* FROM (SELECT #lim := 2,#cg := '') vars, (select * from Table1 order by product,amount, make) t WHERE CASE WHEN #cg <> product THEN #r := #lim ELSE 1 END > 0 AND (#r := #r - 1) >= 0 AND (#cg := product) IS NOT NULL ORDER BY product,amount, make]
at org.hibernate.impl.AbstractQueryImpl.verifyParameters(AbstractQueryImpl.java:291)
at org.hibernate.impl.SQLQueryImpl.verifyParameters(SQLQueryImpl.java:199)
at org.hibernate.impl.SQLQueryImpl.list(SQLQueryImpl.java:143)
at com.infibeam.weaverbird.helper.PopularBrandFacetHelper.bootstrap(PopularBrandFacetHelper.java:48)
Thanks in advance...
The problem is the assignments with :=, which are by the way no standard SQL.
In SQL after a : always a parameter is expected, like in where value = :param and :param has the be set as a parameter then. Now hibernate is scanning the select and find colons where no set parameters follow.
Solution: Redesign your selection using hibernate standards.
You can use two different HQL queries.
First: Select all product: select distinct product from Table1
Second: For each product you do from Table1 where product = :prod, :prod you set as a parameter with the actual product, and with setMaxResults(2) you can limit the number of rows as you need.
Now it is many selects and not a single one, but nevertheless they might be faster than the single query (the single query is complicated and risks an inefficient search strategy in the database). And a big advantage, now it is purely HQL and so your program is portable to different databases.

Categories