java—where in子句中没有值时如何执行查询

oewdyzsn  于 2021-06-21  发布在  Mysql
关注(0)|答案(15)|浏览(368)

我有一个基于不同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 ();

当second where in条件中没有值时,如何执行此查询?或者如何在应用程序级(java)中处理这个问题,我需要编写大约28个if-else条件来检查空条件。如何克服这个问题?

ybzsozfc

ybzsozfc1#

如果不将“餐厅菜系”和“餐厅收藏”与其他项目联系起来,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 ();

例如y,表a和b有两列(c1和c2)

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

生成sql

SELECT * FROM C1, C2

选择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
  ...

首先,将表与所需字段关联起来。然后使用适当的联接(左联接或外联接)建立关系如果进行了更正,则与联接的第二部分没有关联的行将为空(就像空集合),并且可以选择其值为空的所有行(ej:restaurant\u collections.collection\u id为空)
示例:(需要填充缺少的关系)

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
hs1rzwqc

hs1rzwqc2#

这是应用程序任务,不是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 有点混乱,但还不错。
处理日期范围和其他变化也是可能的。

zpqajqem

zpqajqem3#

在这个场景中,我对自己的看法类似于@paulspiegel。虽然我不会先检查所有的空数组,甚至是放入查询中。对于小coct,它仍然会增加计时。我会附上 StringBuilder 如果数组不为空 sql.append( AND restaurant_cuisines.cuisine_id IN (?,?) ) ,相同条件适用于参数。

9wbgstp7

9wbgstp74#

如果您的数据库支持它,您可以使用任何(如下面的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也适用于空数组(至少在postgres中是这样)。

yftpprvb

yftpprvb5#

这完全取决于你的需要。通常你应该跳过整个部分。
当你使用 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)
ljsrvy3e

ljsrvy3e6#

如果方向正确,请编写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, ",") + ") ");
  }
}

希望你能明白。

hpcdzsge

hpcdzsge7#

这个问题似乎缺乏一些解释。别人帮助你的最好方法是对以下方面有一个很好的了解:
你的实体关系(er)模型。我看到有四个表,但不清楚表之间的关系
er代表了什么,你想要实现什么,简单的语言也会有帮助。
我可能会在您的提问中补充一些一般性的意见:
为了避免“笛卡尔积”,需要建立n-1 where子句来连接表。关于笛卡尔积的快速信息可以在这里找到:笛卡尔积。我假设您希望避免这种情况,但是您只使用一个子句连接了两个表,然后继续缩小结果的范围,因此缺少了另外两个where子句。那是你的意图还是疏忽?
您使用distinct关键字来避免重复,可能是为了处理笛卡尔(副)积。在你的案例中,它不如使用分组的方法。这可能会让你很好地理解为什么:https://sqlperformance.com/2017/01/t-sql-queries/surprises-assumptions-group-by-distinct
您正在使用隐式联接。我建议您开始改用显式联接,即左联接、内联接等。它使您的查询更易于阅读和维护。在其他主题中也提到了它,例如:隐式与显式sql连接
向in(空)倾斜。与其他建议或不断提示您在(null)中使用的答案不同,我建议您使用where x is null。在包含null或is null的in子句中读取更多信息

xmjla07d

xmjla07d8#

我通常通过增加布尔变量来解决这类问题。在您的示例中,我有一个名为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);

由于支架中的第一个条件不满足,因此不会检查第二个条件。所以你会得到一切。

dpiehjr4

dpiehjr49#

我是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

gv8xihay

gv8xihay10#

首先,下次尝试复制生成该查询的整个代码时,您对我说的方式是,您从几个输入字段中获取该查询,然后合并到in子句中
您应该创建一个集合来存储这些字段,然后再充气到in中

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

collectionIds.remove(null);

然后,您可以在创建查询之前检查id集是否为空,并按照您想要的方式处理它(简单地忽略查询,或者不使用此语句构建查询)
顺便说一下,您应该考虑使用hibernate或其他持久性框架,强烈建议不要使用java或任何其他语言通过连接字符串来构建查询。它导致sql注入

km0tfn4u

km0tfn4u11#

条件 IN () 将在mysql中引发语法异常。
您可以删除 AND 子句,如果该列表被检测为空。分析查询,如果 () 如果检测到故障,则清除故障和状况。
另一种方法是始终存在一个不符合数据中任何标准的条件。例如, AND restaurant_collections.collection_id IN (-1) 然后继续添加到列表中。例如, AND restaurant_collections.collection_id IN (-1,10,12) . 这样,就永远不会出现空名单的情况。

hk8txs48

hk8txs4812#

你可以试试这个:

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);
quhf5bfb

quhf5bfb13#

假设在这些括号之间有一个变量,就可以做这个小把戏。

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);

基本上,它测试 @VARIABLENULL 不包括参数。

iq3niunx

iq3niunx14#

如何在应用程序级(java)中处理这个问题,我需要编写大约28个if else条件来检查空条件。
将查询更改为动态查询,如下所示

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

上面的代码总是用所有的值来执行(基本上说是真的),现在基于if/else条件,你只需要附加“and条件”不是空的
当second where in条件中没有值时,如何执行此查询?

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

因此,只有当u id不为空时,才会添加上述内容。这样做的目的是使您的查询只基于所需的条件运行,而不是添加不必要的附加条件,这些条件可能会改变您的结果或面临您当前遇到的问题。
希望这有帮助:)

sg2wtvxw

sg2wtvxw15#

如果您只想让查询工作,那么您应该能够使用:

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值(或者,如果不使用prepared语句,则传递包含 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 如果使用coalesce函数传递的值不为null,则包含值列表。
场景1中有值,仅返回这些值。
场景2,and=null中没有值。归还所有东西。
总之, LEFT 连接非常灵活,允许您稍后返回并可以选择强制执行严格的 INNER JOIN . 它们不应用于 INNER JOIN 做的工作,但大多数肯定有他们的用途,如这里。
关于 LEFTINNER :
如果希望即使列表为空也执行查询,请使用 LEFT JOIN .
如果希望查询仅在列表中有值时返回结果,请使用 INNER JOIN .

相关问题