我有一个查询根据不同的ID提取结果, 我的查询如下所示: 我已经缩短了查询,但是,还有两个WHERE In条件。
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 ();
如果条件中第二个WHERE中没有值,如何执行此查询? 或者如何在应用程序级别(Java)中处理这个问题,我需要编写大约28个if if条件来检查空条件。如何克服这个?
答案 0 :(得分:5)
这完全取决于您的需求。通常你应该简单地跳过整个部分。
当您对所有条件使用AND
时,我假设您要通过空列表返回所有行。(我猜最终用户有某种多个复选框或类似的东西)。
如果列定义为NOT NULL
,您可以在JAVA中生成代码,如下所示:
...
AND restaurant_collections.collection_id IN(restaurant_collections.collection_id)
-- which is always true and good query optimizer should skip entire part
如果您需要特定值,请将其用作上一个:
...
AND restaurant_collections.collection_id IN (1,2,3)
答案 1 :(得分:3)
如果您只想让查询生效,您应该可以使用:
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);
但这总是没有结果。
如果你想要一些更灵活的东西,或者如果collection_id
没有传递任何值,你期望所有结果,我建议从隐式连接更改为显式连接。
现在你正在使用隐式连接语法:
SELECT A.*, B.*, C.* FROM A,B,C
WHERE A.j=B.j AND A.j=C.j
问题在于,如果C.j
为空,即使您修复了语法,查询也不会返回任何内容。
显式连接允许更细微的方法:
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);
编码时,不传递空字符串,而是传递空值(或者,如果您没有使用预准备语句,则传递包含NULL
的字符串)。否则,将值作为列表传递。
如果只在没有值时才需要忽略行,你可以这样做:
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>));
当您添加LEFT JOIN
然后在WHERE
子句中引用它时,您可以轻松检查C.v
是否包含值列表,如果传递的值不为null使用COALESCE函数。
总之,LEFT
联接非常灵活,允许您稍后返回并有选择地强制执行严格的INNER JOIN
。当INNER JOIN
完成工作时,不应该使用它们,但绝大多数都有它们的用途,例如此处。
关于LEFT
vs INNER
的说明:
LEFT JOIN
。 INNER JOIN
。答案 2 :(得分:3)
这个问题似乎缺乏一些解释。其他人帮助您的最佳方式是提供良好的见解:
我可能会从get-go中添加一些一般性评论:
答案 3 :(得分:2)
我通常通过添加额外的布尔变量来解决这种情况。 在你的例子中,我有一个名为 searchByCollectionIds 的额外布尔参数,所以它需要像:
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);
当:collectionIds为空时,你将-1加入其中,:searchByCollectionIds设置为false,你的查询将被翻译成:
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);
由于括号中的第一个条件不符合,因此不会检查第二个条件。所以你会得到一切。
答案 4 :(得分:1)
这是一项应用任务,而非SQL任务。
当我有一个带有多个可选输入的API时,我会动态构造WHERE
。有点像(抱歉,这是PHP,而不是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
...";
注意:
x
没有条目,则省略WHERE
的该部分。WHERE
条款。IN
有点麻烦,但还不错。答案 5 :(得分:1)
如果您没有将“restaurant_cuisines”和“restaurant_collections”与其他项目关联起来,那么SQL没有任何意义。如果这两个表与其他表无关,那么SQL就是这些表的笛卡尔积。
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 ();
例如,我们有表A和B,有两列(C1和C2)
Table values (A & B)
C1 | C2
------|------
1 | 1
2 | 2
制作SQL
SELECT * FROM C1, C2
选择A&amp; A之间的所有组合。 B保持“空洞”的空值。
A.C1 | A.C2 | B.C1 | B.C2
------|------|------|------
1 | 1 | 1 | 1
2 | 2 | 1 | 1
1 | 1 | 2 | 2
2 | 2 | 2 | 2
...
首先,将表与所需字段相关联。然后使用正确的JOIN(LEFT JOIN或OUTER JOIN)创建关系,如果使其正确,与JOIN的第二部分无关的行将为null(如空集合)并且您可以选择具有空值的所有行在它上面(ej:restaurant_collections.collection_id IS NULL)
示例:(需要填写缺失的关系)
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
答案 6 :(得分:1)
假设你在这些括号之间有变量,你可以做这个小技巧。
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);
基本上,它会测试@VARIABLE
是否为NULL
并且不包含参数。
答案 7 :(得分:1)
如何在应用程序级别(Java)中处理这个问题,我需要写一下 28如果有条件检查空条件。
将您的查询更改为 DYNAMIC QUERY ,如此
StringBuffer sql = "SELECT DISTINCT restaurant.*,branch.*"+
"from restaurant,branch,restaurant_cuisines,restaurant_collections"+
" WHERE 1=1 ";
以上将始终执行所有值(基本上说是真的),现在基于你的if / else条件,你只需要附加你的&#34; AND条件&#34;这样不是空的
如果第二个WHERE中没有值,如何执行此查询 条件?
if(restaurant_cuisines.cuisine_id != null) {
sql.append("AND restaurant_cuisines.cuisine_id IN (2,3)");
}
所以只有在cuisine_id不为null时才会添加上述内容。 这样做会使您的查询仅根据所需条件运行,而不会添加可能会改变您的结果或面临您当前遇到的问题的不必要的附加条件。
希望这会有所帮助:)
答案 8 :(得分:1)
我是SQL Server开发人员所以我会这样做:
/*
@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
然后从您的Java代码中调用此SP
答案 9 :(得分:1)
你可以试试这个:
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);
答案 10 :(得分:0)
条件IN ()
将在MySQL中引发语法异常。
如果列表被检测为空,则可以删除AND
子句。解析查询,如果检测到()
,则删除AND条件。
另一种方法是始终在那里具有不符合数据中任何标准的条件。例如,AND restaurant_collections.collection_id IN (-1)
然后继续附加到列表中。例如,AND restaurant_collections.collection_id IN (-1,10,12)
。这样,永远不会出现空列表情况。
答案 11 :(得分:0)
首先,下次尝试复制生成该查询的整个代码,顺便说一下,当你说它连接到我时,你从几个输入字段中获取它然后合并到IN子句中
你应该创建一个Set来存储这些字段,然后再膨胀到IN
Set<String> collectionIds = new Hashset<>();
collectionIds.add(field1);
collectionIds.add(field2);
....
collectionIds.remove(null);
然后你可以在创建查询之前检查id set是否为空,并按照你想要的方式处理它(简单地忽略查询,或者在没有这个语句的情况下构建它)
顺便说一句,您应该考虑使用Hibernate或其他持久性框架,但强烈要求通过连接字符串来构建java或任何其他语言的查询。它导致sql注入
答案 12 :(得分:0)
如果你的数据库支持它,你可以使用ANY(如下面的例子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()]))
任何也可以使用空数组(至少在postgres中)。
答案 13 :(得分:0)
你来正确的方向,写if / else去除空数组条件。
但是你不需要写那么多(~28个函数),只需将它们抽象为单个函数。
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, ",") + ") ");
}
}
希望你明白这一点。
答案 14 :(得分:0)
我对自己想要的方式类似于@PaulSpiegel。虽然我不会先检查所有空数组,甚至先放入查询。对于小coct,它仍会增加时间。如果数组不为空,我会附加StringBuilder
sql.append( AND restaurant_cuisines.cuisine_id IN (?,?) )
,同样的条件适用于参数。