Empty IN clause parameter list in MySQL

早过忘川 提交于 2019-11-27 20:58:40

If I have an application where I'm building the IN list dynamically, and it might end up empty, what I sometimes do is initialize the list with an impossible value and add to that. E.g. if it's a list of usernames, I'll start with an empty string, since that's not a possible username. If it's an auto_increment ID, I'll use -1 because the actual values are always positive.

If this isn't feasible because there are no impossible values, you have to use a conditional to decide whether to include AND column IN ($values) expression in the WHERE clause.

The closest approximation of your query with valid syntax is:

SELECT user FROM tbl1 WHERE id IN (SELECT id FROM tbl1 WHERE FALSE);

which will unconditionally return an empty result set. The subquery in the bracket always returns an empty set, and as I mentioned earlier, no value can be found in an empty set, since an empty set contains no values.

This gives me 0 results as well:

SELECT id FROM User
WHERE id IN (NULL);

Tried on MYSQL 5.6

If you use AUTO_INCREMENT for id (1,2,3, ..) and if array is empty, you can add one item [0]. So it will be

if (empty($arr)) {
   $arr[] = 0;
}

SELECT user WHERE id IN (0);

And there will be no mysql parse error. This case is very usefull in subqueries - when your main query is not dependent on subquery results.


Better way - don't call the query if array is empty.

$data = null;
if (!empty($arr)) {
    $data = ... call query
}

Use an always false statement

Before creating the SQL, check for the array size. If the size is 0, generate the where statement as 1 = 2, as in:

SELECT * FROM USERS WHERE name in () 

becomes

SELECT * FROM USERS WHERE 1 = 2 

For "not in" on an empty array, generate an always true statement as in:

SELECT * FROM USERS WHERE name not in () 

becomes

SELECT * FROM USERS WHERE 1 = 1

This should work for more complex queries as:

SELECT * FROM USERS WHERE (name in () OR name = 'Alice') 

becomes

SELECT * FROM USERS WHERE (1 = 2 OR name = 'Alice') 

I assume that you still need to run the query when your IN is empty; for example with LEFT JOIN ... ON ... AND IN ().

As you are already dynamically building the IN in the application layer, I'd handle the empty case there.

Here's a basic example in PHP

$condition = 'FALSE' // Could feasibly want TRUE under different circumstances
if($values){
   $inParams = **dynamically generated IN parameters from $values**;
   $condition = 'IN (' . $inParams . ')';
}

$sql = 'SELECT * FROM `user` WHERE '.$condition;

If you don't need to run the query with an empty IN, then don't; save yourself a trip to the database!

N.B. In case you aren't already, I'd build/use a function that goes the long way round and binds in your IN parameters properly, rather than just concatting them raw into the SQL. This will give you some protection against SQL injection if it's used on raw data.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!