Tuesday, May 8, 2018

How to execute query when there are no values in where in clause

Leave a Comment

I have a query that fetches results based on different id's, My query looks like this: I have shortened the query, still, there are two more WHERE In conditions.

 SELECT DISTINCT restaurant.*,branch.*   from restaurant,branch,restaurant_cuisines,restaurant_collections   WHERE restaurant.restaurant_id=branch.restaurant_id     AND restaurant_cuisines.cuisine_id IN (2,3)     AND restaurant_collections.collection_id IN (); 

How to execute this query when there are no values in second WHERE in Condition? or How to handle this in application level(Java), I need to write around 28 if else conditions to check empty conditions. How to overcome this?

14 Answers

Answers 1

It all depends on your needs. Normally you should simply skip entire part.

As you use AND for all conditions I assume that by empty list you want to return all rows.(I guess that end users have some sort of multiple checkboxes or something like that).

If column is defined as NOT NULL you could generate your code in JAVA like this:

... AND restaurant_collections.collection_id IN(restaurant_collections.collection_id) -- which is always true and good query optimizer should skip entire part 

When you want specific values you use it as previous:

... AND restaurant_collections.collection_id IN (1,2,3) 

Answers 2

If all you want is for the query to work, you should be able to use:

SELECT DISTINCT restaurant.*,branch.*   from restaurant,branch,restaurant_cuisines,restaurant_collections   WHERE restaurant.restaurant_id=branch.restaurant_id     AND restaurant_cuisines.cuisine_id IN (2,3)     AND restaurant_collections.collection_id IN (NULL); 

But this will always return no results.

If you want something a little more flexible, or if you are expecting all results if collection_id does not have any values passed to it, I recommend changing from implicit to explicit joins.


Right now you're using an implicit join syntax:

SELECT A.*, B.*, C.* FROM A,B,C  WHERE A.j=B.j AND A.j=C.j 

The problem with this is that if C.j is empty, even if you fix your syntax, your query will return nothing.

Explicit joins allow for a more nuanced approach:

SELECT A.*, B.*, C.* FROM A INNER JOIN B ON A.j=B.j AND B.v IN (1,2) LEFT JOIN C ON A.j=C.j AND C.v IN (NULL); 

When you code, instead of passing an empty string, pass a null value (or, if you're not using prepared statements, pass a string containing NULL). Otherwise, pass the values as a list.

If you need the rows to be ignored only when there are no values, you can do something like this instead:

SELECT A.*, B.*, C.* FROM A INNER JOIN B ON A.j=B.j AND B.v IN (1,2) LEFT JOIN C ON A.j=C.j WHERE (COALESCE(<val>) IS NULL OR C.v IN (<val>)); 

When you add the LEFT JOIN and then refer to it in the WHERE clause, you can easily check if the C.v contains the list of values, if the value passed is not null by using the COALESCE function.

  1. Scenario 1, there are values in , return only those values.
  2. Scenario 2, there are no values in and =NULL. Return everything.

In summary, LEFT joins are very flexible and allow you to go back later and optionally enforce a strict INNER JOIN. They should not be used when an INNER JOIN does the job, but most definitely have their uses, such as here.


A note on LEFT vs INNER:

  • If you want the query to execute even if the list is empty, use LEFT JOIN.
  • If you want the query to return results only if there are values in the list, then use INNER JOIN.

Answers 3

This is an app task, not an SQL task.

When I have an API with several optional inputs, I construct the WHERE dynamically. Something like (sorry, this is in PHP, not Java):

$wheres = array();    // declare empty array if (user entered x)  $wheres[] = "x = '$x'; // conditionally append item to array if (user entered y)  $wheres[] = "y = '$y'; if (user entered a non-empty zlist)                      $wheres[] = "z IN (" . implode(',', $zlist), ")";  $where_clause = empty($wheres) ? '' :                 'WHERE ' . implode(' AND ', $wheres);  $SQL = "SELECT ...            $where_clause                ..."; 

Notes:

  • If there is no entry for x, then that part of the WHERE is omitted.
  • If none of the parameters are provided, I even get rid of the WHERE clause entirely.
  • Handling IN is a little messier, but still not bad.
  • Handling a date range, and other variations, are also possible.

Answers 4

I usually solve this kind of conditions by having additional boolean variable. In your example, I'd have an additional boolean param named searchByCollectionIds so that it would like something like:

SELECT DISTINCT restaurant.*,branch.*   from restaurant,branch,restaurant_cuisines,restaurant_collections   WHERE restaurant.restaurant_id=branch.restaurant_id     AND restaurant_cuisines.cuisine_id IN (2,3)     AND (true = :searchByCollectionIds or restaurant_collections.collection_id IN (:collectionIds); 

When :collectionIds is empty, you'd put -1 into it, :searchByCollectionIds set to false and your query would be translated into:

SELECT DISTINCT restaurant.*,branch.*  from restaurant,branch,restaurant_cuisines,restaurant_collections  WHERE restaurant.restaurant_id=branch.restaurant_id    AND restaurant_cuisines.cuisine_id IN (2,3)    AND (true = false or restaurant_collections.collection_id IN (-1); 

Since first condition in the bracket does not meet, it will NOT check second one. So you'll get everything.

Answers 5

The SQL has no really sense if you don't relate "restaurant_cuisines" and "restaurant_collections" with the other items. If these two tables are not related with the other ones then the SQL is a cartesian product of this tables.

SELECT DISTINCT restaurant.*,branch.*   from restaurant,branch,restaurant_cuisines,restaurant_collections   WHERE restaurant.restaurant_id=branch.restaurant_id     AND restaurant_cuisines.cuisine_id IN (2,3)     AND restaurant_collections.collection_id IN (); 

For example y we have the tables A and B with two columns(C1 and C2)

Table values (A & B)   C1  | C2 ------|------  1    |  1  2    |  2 

Making the SQL

SELECT * FROM C1, C2 

selects all the combinations between A & B keeping the "holes" with a null value.

 A.C1 | A.C2 | B.C1 | B.C2 ------|------|------|------   1   |  1   |  1   | 1   2   |  2   |  1   | 1   1   |  1   |  2   | 2   2   |  2   |  2   | 2   ... 

First, relate the tables with the desired fields. Then make the relations using a proper JOIN (LEFT JOIN or OUTER JOIN) if you make this correct the rows with out relation with the second part of the JOIN will be null (like an empty collection) and you could select all rows with null value on it (ej: restaurant_collections.collection_id IS NULL)

Example: ( Requires to fill the missing relations)

SELECT DISTINCT      restaurant.*,     branch.*  FROM    restaurant r        LEFT JOIN branch b ON r.restaurant_id = b.restaurant_id       LEFT JOIN restaurant_cuisines rcu ON  ????       LEFT JOIN restaurant_collections rco ON ???? WHERE    rcu.cuisine_id IN (2,3) AND    rco IS NULL 

Answers 6

Assuming that you have a variable between those brackets you could do this little trick.

 DECLARE @Variable AS VARCHAR(250)    SELECT DISTINCT       restaurant.*,branch.*   FROM      restaurant,      branch,      restaurant_cuisines,      restaurant_collections    WHERE       restaurant.restaurant_id=branch.restaurant_id       AND restaurant_cuisines.cuisine_id IN (2,3)       AND (restaurant_collections.collection_id IN (@VARIABLE) OR @VARIABLE IS NULL); 

Essentially it tests to see if @VARIABLE is NULL and does not include the parameter.

Answers 7

How to handle this in application level(Java), I need to write around 28 if else conditions to check empty conditions.

Change your query to a DYNAMIC QUERY like so

StringBuffer sql = "SELECT DISTINCT restaurant.*,branch.*"+  "from restaurant,branch,restaurant_cuisines,restaurant_collections"+ " WHERE  1=1 "; 

The above will always execute with all the values(basically saying its true), now based on your if/else conditions you just have to append your "AND condition" is not empty like so

How to execute this query when there are no values in second WHERE in Condition?

if(restaurant_cuisines.cuisine_id != null) { sql.append("AND restaurant_cuisines.cuisine_id IN (2,3)"); } 

So the above will only be added if cuisine_id is not null. What this does is makes your query run only based on the conditions required and not add unnecessary additional conditions which might alter your result or face the issues that you are current suffering with.

Hope this helps :)

Answers 8

I'm a SQL Server developer so I would do it like this:

/* @collection_id have to be a string with all the id's separated by comma like '1,2,3,4' */  CREATE PROCEDURE [dbo].[getRestaurant]                                          @collection_id VARCHAR(MAX) AS  DECLARE @query nVARHCAR(MAX)=''  SET @query= ' SELECT DISTINCT restaurant.*,branch.*   FROM restaurant  JOIN branch ON restaurant.restaurant_id=branch.restaurant_id JOIN restaurant_cuisines ON  restaurant.restaurant_id = restaurant_cuisines.restaurant_id JOIN restaurant_collections  WHERE  restaurant_cuisines.cuisine_id IN (2,3)  AND restaurant_collections.collection_id IN ('+@collection_id+')'  EXEC sp_executesql @query 

Then just call this SP from your Java Code

Answers 9

You can try this:

SELECT DISTINCT restaurant.*,branch.*   from restaurant,branch,restaurant_cuisines,restaurant_collections   WHERE restaurant.restaurant_id=branch.restaurant_id     AND restaurant_cuisines.cuisine_id IN (2,3)     AND restaurant_collections.collection_id IN (true); 

Answers 10

The condition IN () will throw syntax exception in MySQL.

You can remove the AND clause if the list is detected as empty. Parse the query and if () is detected, strip out the AND condition.

Another approach is to always have a condition there which will not meet any criteria in the data. For example, AND restaurant_collections.collection_id IN (-1) and then keep appending to the list. For example, AND restaurant_collections.collection_id IN (-1,10,12). This way, there will never be an empty list situation.

Answers 11

First of all next time try to copy the whole code generating that query, by the way you said it seams to me you are getting that from several input fields and merging then into the IN clause

You should create a Set to store these fields before inflate then into the IN

Set<String> collectionIds = new Hashset<>(); collectionIds.add(field1); collectionIds.add(field2); ....  collectionIds.remove(null); 

then you can just check if the id set is empty before create the query and handle it the way you want (simple ignore the query, or build it without this statement)

By the way you should consider using Hibernate or other persistence framework, it is strongly not adivsed to build queries in java or any other language by concating strings. It leads to sql injection

Answers 12

If your database support it, you could use ANY (as in following example with jdbi3)

String sql = "SELECT * FROM table where collection_id = ANY (:ids);" List<Long>ids = ...; org.jdbi.v3.core.Handle db = getHandle(); db.createQuery(sql).bind("ids", ids.toArray(new Long[ids.size()]))

ANY works also with empty array (at least in postgres).

Answers 13

You come with the right direction, write if/else to strip the empty array condition.

But you don't need to write that much (~28 functions), just abstract them to a single function.

StringBuilder baseQuery = new StringBuilder("SELECT DISTINCT restaurant.*,branch.*   from restaurant,branch,restaurant_cuisines,restaurant_collections   WHERE restaurant.restaurant_id=branch.restaurant_id ");  public void addInCondition(String field, Collection<Integer> values) {   if (values != null && !values.isEmpty()) {      // Append your values      baseQuery.append(field + " IN (" + String.join(values, ",") + ") ");   } } 

Hope you get the idea.

Answers 14

App 

[

  1. Block quote

]1

Block quote

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment