【问题标题】:Best practice for where = ?, where in (?) clauses in Prepared Statements?准备语句中 where = ?, where in (?) 子句的最佳实践?
【发布时间】:2010-02-25 12:42:46
【问题描述】:

使用带有一个问号的Prepared Statement 50 次,还是使用带有50 个问号的Prepared Statement 一次性能更好?

基本上Where Person = ?Where Person IN (?, ?, ?, ...) 更好?

示例

假设您有一个包含列、国家/地区的表,然后在几个关系表之外您就有该国家/地区的人口。

给定 1000 个国家/地区的列表,获取人口的最佳方法是什么?

记住这是一个假设的例子,Wikipedia puts the number of countries at 223,让我们假设这个例子更大。

  1. 创建一个接受 国家参数并返回人口。 示例:Where Country = ?

  2. 创建准备好的语句 动态地,添加一个?对于每个 使用Where in (?,?,etc) 子句的国家/地区。示例:Where Country = (?, ?, ...)

  3. 创建一个 选项中的简单语句 一,但循环并重用 每个参数都有一个Prepared Statement 国家。

最好的方法是什么?

【问题讨论】:

    标签: sql database performance prepared-statement


    【解决方案1】:

    如果我能够使用一些真实数据进行测试,我的项目达到了一个点。

    基于 1435 个项目,选项 1 大约需要 8 分钟,选项 2 大约需要 15 秒,选项 3 大约需要 3 分钟。

    选项 2 在性能方面是明显的赢家。编写代码有点困难,但性能差异太大,不容忽视。

    虽然我确信此处列出的结果会因网络、数据库引擎、数据库机器规格和其他环境因素而异。

    【讨论】:

    • 考虑到 IN 子句中的数字元素是有限的。如果我没记错的话,在旧版本的 Oracle 中是 255。也许在其他 DBMS 中应用相同。
    • @Lluis Martinez,我打算编辑答案以说同样的话。我同意记住准备语句的参数数量限制很重要,但在大多数现代数据库中,我认为限制相当高。仅供参考,我在这个特定项目中使用的数据库是 DB2。
    【解决方案2】:

    正如人们常说的,“这取决于”。如果您只是在寻找一个国家的人口,我会使用方法 1。我会避免使用 #2,因为我不喜欢使用动态构造的 SQL,除非它是完成工作的唯一方法(有效),这似乎不是其中一种情况。我对 #3 也不是很感兴趣,因为我认为如果您需要获取所有不同国家/地区的人口,循环将效率低下。

    我们添加 #4 怎么样:返回所有国家/地区人口的单个语句,例如

    SELECT C.COUNTRY_NAME, SUM(S.POPULATION)
      FROM COUNTRY C,
           COUNTRY_CENSUS_SUBDIVISION S
      WHERE S.ID_COUNTRY = C.ID_COUNTRY
      GROUP BY C.COUNTRY_NAME;
    

    围绕它构建一个方法,如果您需要一次获取所有国家/地区的人口,让它返回一个国家到人口的地图。

    分享和享受。

    【讨论】:

    • 国家在这里可能是一个糟糕的例子,因为世界上的国家数量是有限的。让我们假设,仅以我为例,有数十万个国家,而且这个数字正在增加。从性能的角度来看,返回所有这些列表是不可行的。
    【解决方案3】:

    RAM 很便宜。将整个列表加载到缓存的哈希表中并以内存速度工作

    如果性能有问题,请使用 RAM。您可能会花费数天或数周的时间来尝试优化可能适合价值 100 美元 RAM 的东西

    【讨论】:

    • 那是选项 2,对吧?所以你说最好使用本地缓存(RAM)然后多次访问数据库。
    • @James McMahon:有点,只是将整个表读入缓存,不需要 IN 子句。然后在内存中使用哈希表/字典索引来查找项目
    • 我上面的例子似乎在绊倒人们。国家显然是一个不好的例子,因为国家的数量太少了。当我说我没有足够的内存来将此数据库加载到内存中时,请相信我,并且期望能够做到这一点也不合理。
    • 你至少可以缓存最常见的表/列。 1000 美元的 RAM 可以容纳大量任何东西
    【解决方案4】:

    执行查询有两个步骤:
    1. 创建执行计划。
    2. 执行计划。

    准备好的语句与步骤 1 相关。在给出的示例中,我认为执行时间最多的将是步骤 2,因此我会选择执行最佳的替代方案。使数据库引擎优化的一般规则是给它范围问题,而不是在客户端循环发出几个小问题。可用索引和客户端-服务器延迟当然会影响差异的大小,但我认为您的选项 #2,通常动态创建准备好的语句是最好的选择。

    您是否对不同的替代方案进行了任何测试?如果有,它们会显示什么?

    【讨论】:

      【解决方案5】:

      正如其他人所说,这取决于参数的数量和数据的大小。根据您在 cmets 中的说明,源表可能具有数十万行。如果是这种情况,问题就归结为允许的过滤输入的数量。您的查询是只允许一小组输入,还是需要允许过滤一千个国家?如果是后者,那么我建议将选择存储到中间表中并将其连接起来。比如:

      Create Table CriteriaSelections
      (
          SessionOrUsername nvarchar(50) not null
          , Country nvarchar(50) not null
      )
      

      在选择时,您将填充此表,然后像这样从中查询

      Select ...
      From BigFatCountryTable
          Join CriteriaSelections
              On CriteriaSelections.Country = BigFatCountryTable.Country
                  And CriteriaSelections.SessionOrUsername = @SessionOrUsername
      

      您可以使用 RNGCryptoServiceProvider 生成一个随机数,如果这可能被同一个“会话”以不同的方式并行调用多次。此设置的问题是您需要定期清除选择表。

      如果所讨论的实体在某种程度上是不可变的(例如,国家、城市等),那么将缓存策略与查询策略结合使用也会有所帮助。

      顺便说一句,另一个类似的解决方案是使用临时表。但是,如果您这样做,则需要小心使用完全相同的连接来创建临时表、临时表的填充及其使用。

      【讨论】:

      • 这听起来是个不错的建议,不幸的是,在机器上我正在从我的访问权限有限的机器上提取数据。我绝对无法创建表,我不确定是否使用临时表。假设我可以创建一个临时表,您建议如何将初始值放入表中?
      • 将值放入临时表很容易。这只是像在常规表中一样进行一堆插入操作。棘手的部分是保持相同的连接。因此,在一次调用中,您将执行“Create Table #Foo...”,然后在同一连接上的另一个调用将执行一堆“Insert #Foo...”,最后,在同一连接上,您将运行您的选择将加入#Foo 的查询。
      • 顺便说一句,解决此问题的另一种方法是将分隔列表传递到存储过程中,然后使用拆分函数将该列表拆分为临时表。
      【解决方案6】:

      根据使用的数据库引擎,可能还有其他选择。

      例如,对于 MS SQL,您可以使用 CSV->Table 函数,例如: http://www.nigelrivett.net/SQLTsql/ParseCSVString.html

      然后您可以为您的查询提供一个逗号分隔的值字符串,然后加入表格:

      SELECT ..
      FROM  table t
      INNER JOIN dbo.fn_ParseCSVString(?, ',') x
           ON  x.s = t.id
      WHERE ...
      

      在这种情况下,将有两个循环:构建 CSV 字符串(如果您还没有这种格式的字符串)和将 CSV 拆分为表格。

      但它可能提供比多次执行 join 以及使用 IN 更好的性能(根据我的经验,它的性能很差)。如果性能确实是个问题,您当然应该进行测试。

      结果也可能因网络开销等而异...

      【讨论】:

        猜你喜欢
        • 2015-03-16
        • 1970-01-01
        • 1970-01-01
        • 2013-04-21
        • 2010-12-07
        • 2013-10-03
        • 2020-06-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多