如果

时间:2018-04-29 09:04:07

标签: java mysql

我有一个查询根据不同的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条件来检查空条件。如何克服这个?

15 个答案:

答案 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函数。

  1. 场景1,有值,只返回那些值。
  2. 场景2,没有值和= NULL。归还一切。
  3. 总之,LEFT联接非常灵活,允许您稍后返回并有选择地强制执行严格的INNER JOIN。当INNER JOIN完成工作时,不应该使用它们,但绝大多数都有它们的用途,例如此处。

    关于LEFT vs INNER的说明:

    • 如果您希望查询执行即使列表为空,请使用LEFT JOIN
    • 如果您希望查询仅在列表中 值时返回结果,请使用INNER JOIN

答案 2 :(得分:3)

这个问题似乎缺乏一些解释。其他人帮助您的最佳方式是提供良好的见解:

  1. 您的实体关系(ER)模型。我看到涉及四个表但是表格之间的关系并不清楚
  2. ER代表什么以及您想要实现的目标,简单的单词也可以提供帮助。
  3. 我可能会从get-go中添加一些一般性评论:

    • 为了避免使用笛卡尔积&#34;,您需要建立N-1 WHERE子句来连接您的表。有关笛卡尔积的快速信息,请访问:Cartesian product。我假设您要避免这种情况,但是您只使用一个子句连接了两个表,然后继续缩小结果,因此您缺少其他两个WHERE子句。那是你的意图还是遗漏?
    • 您正在使用DISTINCT关键字来避免重复,可能是为了处理笛卡尔(by)产品。在您的情况下,它比使用GROUP BY方法的性能要差。这可能会让您深入了解原因:https://sqlperformance.com/2017/01/t-sql-queries/surprises-assumptions-group-by-distinct
    • 您正在使用隐式联接。我建议你开始使用EXPLICIT连接,即LEFT JOIN,INNER JOIN等。它使你的查询更容易阅读和维护。在其他主题上已经提到过,即:implicit vs explicit sql join
    • 向IN倾斜(null)。与其他建议或暗示您使用IN(null)的答案不同,我建议您使用WHERE x IS NULL。阅读更多 IN clause with NULL or IS NULL

答案 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 (?,?) ),同样的条件适用于参数。