【问题标题】:When to 'IN' and when not to?什么时候“IN”,什么时候不?
【发布时间】:2009-07-20 12:52:53
【问题描述】:

假设您正在为一家零售连锁店编写应用程序。因此,您将设计您的对象模型,以便将“Store”定义为核心业务对象和许多支持对象。假设“商店”如下所示:

class Store implements Validatable{
int storeNo;
int storeName;
... etc.... 
}

因此,您的客户告诉您,您必须将商店时间表从 Excel 表导入应用程序,并且您必须对它们运行一系列验证。例如,'StoreIsInSameCountry';'StoreIsValid'...等。因此,您将设计一个规则接口来检查所有业务条件。像这样的:

interface Rule T extends Validatable> {
public Error check(T value) throws Exception;
}

现在,问题来了。我正在从这个 excel 表中上传 2000 家商店。因此,我最终会多次运行为商店定义的每个规则。如果我有 4 条规则 = 8000 次数据库查询,即 16000 次访问连接池。对于一个简单的检查,我只需要检查商店是否存在,查询将是:

 SELECT STORE_ATTRIB1, STORE_ATTRIB2... from STORE where STORE_ID = ?

这样我就可以获得我的“商店”对象。当我没有从数据库中得到任何东西时,那个商店就不存在了。因此,对于这样一个简单的检查,我必须为 2000 家商店访问数据库 2000 次。

或者,我可以这样做:

SELECT STORE_ATTRIB1, STORE_ATTRIB2... from STORE where STORE_ID in (1,2,3..... ) 

这个查询实际上会比执行上面的查询 2000 次快得多。 但是,规则只能针对单个商店运行的设计并不适合。

我知道使用 IN 不是推荐的方法。所以,你觉得我应该怎么做?我应该继续在这里使用 IN,因为它在这种情况下提供了更好的性能吗?或者我应该改变我的设计?

如果你处于我的位置,你会怎么做?最佳做法是什么?

【问题讨论】:

    标签: java database performance jdbc


    【解决方案1】:

    这样我就可以从数据库中获取我的“Store”对象。当我没有从数据库中得到任何东西时,那个商店就不存在了。因此,对于这样一个简单的检查,我必须为 2000 家商店访问数据库 2000 次。

    这是你不应该做的。

    创建一个临时表,用你的值和JOIN这个表填充表,像这样:

    SELECT  STORE_ATTRIB1, STORE_ATTRIB2...
    FROM    temptable tt
    JOIN    STORE s
    ON      s.STORE_ID = t.id
    

    或者这个:

    SELECT  STORE_ATTRIB1, STORE_ATTRIB2...
    FROM    STORE s
    WHERE   s.STORE_ID IN
            (
            SELECT  id
            FROM    temptable tt
            )
    

    我知道使用 IN 不是推荐的方法。所以,你觉得我应该怎么做?我应该继续在这里使用 IN,因为它在这种情况下提供了更好的性能吗?还是我应该改变我的设计?

    IN 过滤掉重复项。

    如果您希望为列表中的每个重复值选择每个符合条件的行,请使用JOIN

    IN 绝不是“不建议的方法”。

    事实上,曾经有一段时间,有些数据库并不能有效地支持IN 查询,这就是为什么民间智慧仍然建议不要使用它的原因。

    但是如果您的store_id 被正确索引(并且很可能是,如果它看起来像PRIMARY KEY),那么所有现代版本的主要数据库(即OracleSQL Server、@ 987654333@ 和 PostgreSQL) 将使用有效的计划来执行此查询。

    请参阅我的博客中的这篇文章,了解SQL Server 中的性能详细信息:

    请注意,在设计合理的数据库中,验证规则也是基于集合的。

    我。 e.您将验证规则实现为针对 temptable 的查询。

    但是,为了支持旧规则,您可以从临时的逐行中选择值,应用规则,并删除未通过验证的值。

    【讨论】:

      【解决方案2】:
      SELECT store_id FROM store WHERE store_active = 1
      

      甚至

      SELECT store_id FROM store
      

      将在一个查询中告诉您所有活跃的商店。您现在可以对您知道存在的商店进行其他测试,并且您已将 1,999 次点击保存到数据库中。

      如果您拥有相对无争议的数据库访问权限,并且没有时间限制整个事情需要多长时间,那么您就无需担心一遍又一遍地访问连接池。毕竟,这就是它的设计目的!

      【讨论】:

      • 按照您所说的,如果我要将结果集缓存在某处,这样做是有意义的。即使使用上述方法,我也必须一次验证一个商店,并且不缓存结果集,我不知道如何将自己 1999 年的点击保存到数据库中。
      • 运行查询并将结果存储在 List 中。那是你的第一个测试;列表的大小是否与预期的商店数量相匹配。然后,您可以使用每个 Integer 作为任何 JDBC 查询的存储 id 来迭代在存储上运行其他测试的列表。
      • 您可以在 IN (select ...) 中使用上面的查询作为子查询。根本没有使用缓存。
      【解决方案3】:

      我认为这更像是一个业务问题,其中包含客户端运行导入的频率、实施任一解决方案需要多长时间以及每小时的时间成本是多少。

      如果它是偶尔运行的东西,我认为有点糟糕的性能是可以接受的,特别是如果你可以使用干净的代码快速完成工作。

      【讨论】:

        【解决方案4】:

        ...规则只能针对单个商店运行。

        管理业务规则和性能是一项棘手的任务,因此有一个库 ("Persistence Layer") 可以做到这一点。您定义规则,然后执行大量命令,然后库从数据库中获取单个查询中规则所需的任何内容(通过使用临时表而不是“IN”),然后将其传递给规则。 here 中有一个验证器示例。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-01-09
          相关资源
          最近更新 更多