1

我正在编写一个存储过程来执行跨越 10 多个数据库表的动态搜索。每个表中有数百万条记录和一组动态搜索参数*,我在优化过程时遇到了一些麻烦。

是否有构建此类查询的“最佳实践”?例如,使用字符串构建动态查询,使用大量 IF THEN .. ELSE 语句等?任何人都可以提供一个简单的例子或指出一些有帮助的文献吗?这是我正在开发的存储过程的一些伪代码,它接受参数集合和引用游标。

v_query = "SELECT .....";
v_name = ... -- retrieve "name" parameter from collection
if v_name is not null then
   v_query := v_query || ' AND table.Name = ' || v_name;
end if;
open search_cursor for v_query;
...

*通过“动态搜索参数集”,我的意思是我传入了一组参数。我认为如果他们只想搜索一个参数,这将比让调用者传入 20 个参数更容易。

4

4 回答 4

4

使用静态查询方式存在问题;在使用 CURSOR_SHARING=FORCE 选项时也要非常小心——如果您没有进行覆盖测试以确保您的所有其他查询都能按照您想要的方式工作,它真的会让您的系统陷入困境。

静态查询的问题:

  1. (x is null or x = col) 谓词往往会扼杀任何使用索引的机会。由于查询计划是在第一次解析查询时计算的,因此您使用的索引将基于第一次运行查询的值;稍后的运行可能不会限制在相同的列上,但仍将使用相同的索引。

  2. 拥有一个带有替换变量的静态语句将阻止优化器根据数据分布对要使用的索引做出明智的选择。在动态查询中(或在第一次运行带有绑定变量的查询时),Oracle 将看到您的约束的选择性;高度选择性的约束将成为索引使用的主要候选者。例如,如果您的表为美国的每个人都有一行,则 STATE='Alaska' 将更有可能使用 STATE 上的索引而不是 STATE='California'。

当然,在这两种情况下,如果您的 WHERE 子句中的动态列无论如何都没有被索引,那没关系,尽管如果在您正在谈论的大小的数据库中出现这种情况,我会感到惊讶。

此外,请考虑所有这些硬解析的实际成本。是的,硬解析序列化系统资源,这使得它们变得昂贵,但仅限于大量查询的上下文中。就其性质而言,即席查询不会经常运行。您为一整天内发生的所有硬解析所支付的成本可能比使用错误索引的单个查询的成本低数百倍。

过去,我实现这些系统的方式与您在此处所做的非常相似——基本查询部分,然后迭代约束列表并添加 WHERE 子句谓词。我不认为有人很难维护或理解,尤其是当您谈论不涉及向 FROM 子句添加大量子查询或额外表的约束时。

需要考虑的一件事:如果该系统主要是离线系统(换句话说,不是不断更新或插入 - 由定期加载的批量数据填充),您可能需要考虑使用 BITMAP 索引。位图索引与常规 b-tree 索引的不同之处在于,可以同时使用单个表上的多个索引,并且位图索引在磁盘上比 b-tree 小得多。它们非常适用于这样的应用程序 - 您将有各种无法在设计时定义的约束。您只想将位图索引放在具有相对较少不同值的列上 - 例如,一个值构成不少于表的 1/1000 - 所以不要在唯一列上使用位图。

However, the downside is that bitmap indexes will noticeably degrade the performance of inserts and updates. The best practice for bitmaps is to use them in data warehouse applications, and they are dropped prior to loads and recreated afterwards.

于 2009-06-16T20:22:38.397 回答
2

除了在非常特殊的情况下,我认为尝试生成优化查询是不可取的(甚至是不可能的)。我的建议是尽可能不要使用动态 SQL:难以阅读、难以调试、难以优化、难以维护。

首先,编写一个通用查询,该查询将与发送到您的过程的任何参数一起使用。根据您的示例,这将给出如下内容:

SELECT * FROM table WHERE ((v_name IS NULL) OR (table.Name=v_name));

如您所见,您可以轻松地向该查询添加其他参数,而无需使用动态 SQL。这个查询更容易阅读和调试。向您的 DBA 询问优化技巧。

然后,如果您有一组您知道经常一起传递的特定参数,您可以为该组编写一个您可以专门优化的特定查询。伪代码:

IF particular_set
THEN
    /* Specific query */
ELSE
    /* Generic query */
END IF;

困难的部分是尽量不要在这里有太多特定的查询,否则你可能会陷入维护地狱。

于 2009-06-16T12:59:35.827 回答
1

我们对我们的一位客户也有类似的要求。他们有六张表,其中包含数百万行,并且他们希望在大多数列上具有临时搜索功能。

解决方案是为每个表创建一个单独的包,它将采用搜索条件并构造 SQL 来运行搜索。我们利用被替换的旧系统来发现用户最常见的搜索类型,并通过调整正在生成的查询(由战略用途支持)确保这些搜索运行得最好索引)。因为每个包只负责对一个表的查询,所以它可能具有设计用于处理该表的特定代码(包括奇怪的提示,在少数情况下)。

您需要解决的一个问题/问题是,您是硬编码标准(例如WHERE SURNAME='SMITH')还是使用绑定变量?使用绑定变量减少了硬解析,从而减少了数据库服务器的负载;但是,在动态生成 SQL 时使用绑定变量可能是不切实际的。我们最终采用的方式是设置CURSOR_SHARING=FORCE(它有其自身的缺点),这在我们的案例中是一个合理的折衷方案。

于 2009-06-16T02:15:53.980 回答
1

阅读http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:6711305251199

于 2009-06-16T07:09:00.633 回答