- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我将通过说明我正在使用 Oracle 10g 企业版并且我对 Oracle 相对较新来作为这个问题的序言。
我有一个具有以下架构的表:
ID integer (pk) -- unique index
PERSON_ID integer (fk) -- b-tree index
NAME_PART nvarchar -- b-tree index
NAME_PART_ID integer (fk) -- bitmap index
PERSON_ID
是个人记录的唯一 ID 的外键。 NAME_PART_ID
是查找表的外键,具有静态值,如“名字”、“中间名”、“姓氏”等。该表的要点是存储人们的各个部分分别命名。每个人的记录至少有一个名字。在尝试提取数据时,我首先考虑使用连接,如下所示:
select
first_name.person_id,
first_name.name_part,
middle_name.name_part,
last_name.name_part
from
NAME_PARTS first_name
left join
NAME_PARTS middle_name
on first_name.person_id = middle_name.person_id
left join
NAME_PARTS last_name
on first_name.person_id = last_name.person_id
where
first_name.name_part_id = 1
and middle_name.name_part_id = 2
and last_name.name_part_id = 3;
但该表有数千万条记录,NAME_PART_ID
列的位图索引未被使用。解释计划表明优化器正在使用全表扫描和散列连接来检索数据。
有什么建议吗?
编辑:以这种方式设计表的原因是因为数据库在多种不同的文化中使用,每种文化对个人的命名方式都有不同的约定(例如,在某些中东文化中,个人通常有名字,然后是他们父亲的名字,然后是他父亲的名字,等等)。很难创建一个包含多个列的表格来说明所有文化差异。
最佳答案
鉴于您实际上是在进行全表扫描(因为您的查询是从该表中提取所有数据,不包括没有名称部分的少数行,如第一、中间或最后),您可能需要考虑编写查询,以便它仅以稍微不同的格式返回数据,例如:
SELECT person_id
, name_part_id
, name_part
FROM NAME_PART
WHERE name_part_id IN (1, 2, 3)
ORDER BY person_id
, name_part_id;
当然,您最终会得到 3 行,而不是每个名称一行,但是对于您的客户端代码来说,将它们汇总在一起可能是微不足道的。您还可以使用 decode、group by 和 max 将 3 行合并为一行:
SELECT person_id
, max(decode(name_part_id, 1, name_part, null)) first
, max(decode(name_part_id, 2, name_part, null)) middle
, max(decode(name_part_id, 3, name_part, null)) last
FROM NAME_PART
WHERE name_part_id IN (1, 2, 3)
GROUP BY person_id
ORDER BY person_id;
这将产生与您的原始查询相同的结果。两个版本都只会扫描表一次(排序),而不是处理 3 向连接。如果您将表设为 person_id 索引上的索引组织表,则可以省去排序步骤。
我对一张有 56,150 人的 table 进行了测试,以下是结果的概要:
原始查询:
Execution Plan
----------------------------------------------------------
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)|
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 113K| 11M| | 1364 (2)|
|* 1 | HASH JOIN | | 113K| 11M| 2528K| 1364 (2)|
|* 2 | TABLE ACCESS FULL | NAME_PART | 56150 | 1864K| | 229 (3)|
|* 3 | HASH JOIN | | 79792 | 5298K| 2528K| 706 (2)|
|* 4 | TABLE ACCESS FULL| NAME_PART | 56150 | 1864K| | 229 (3)|
|* 5 | TABLE ACCESS FULL| NAME_PART | 56150 | 1864K| | 229 (3)|
------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("FIRST_NAME"."PERSON_ID"="LAST_NAME"."PERSON_ID")
2 - filter("LAST_NAME"."NAME_PART_ID"=3)
3 - access("FIRST_NAME"."PERSON_ID"="MIDDLE_NAME"."PERSON_ID")
4 - filter("FIRST_NAME"."NAME_PART_ID"=1)
5 - filter("MIDDLE_NAME"."NAME_PART_ID"=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
6740 consistent gets
0 physical reads
0 redo size
5298174 bytes sent via SQL*Net to client
26435 bytes received via SQL*Net from client
3745 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
56150 rows processed
我的查询 #1(3 行/人):
Execution Plan
----------------------------------------------------------
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)|
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 168K| 5593K| | 1776 (2)|
| 1 | SORT ORDER BY | | 168K| 5593K| 14M| 1776 (2)|
|* 2 | TABLE ACCESS FULL| NAME_PART | 168K| 5593K| | 230 (3)|
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("NAME_PART_ID"=1 OR "NAME_PART_ID"=2 OR "NAME_PART_ID"=3)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
1005 consistent gets
0 physical reads
0 redo size
3799794 bytes sent via SQL*Net to client
78837 bytes received via SQL*Net from client
11231 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
168450 rows processed
我的查询 #2(1 行/人):
Execution Plan
----------------------------------------------------------
-----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)|
-----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 56150 | 1864K| | 1115 (3)|
| 1 | SORT GROUP BY | | 56150 | 1864K| 9728K| 1115 (3)|
|* 2 | TABLE ACCESS FULL| NAME_PART | 168K| 5593K| | 230 (3)|
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("NAME_PART_ID"=1 OR "NAME_PART_ID"=2 OR "NAME_PART_ID"=3)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
1005 consistent gets
0 physical reads
0 redo size
5298159 bytes sent via SQL*Net to client
26435 bytes received via SQL*Net from client
3745 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
56150 rows processed
事实证明,您还可以更快地挤压它;我试图通过添加索引提示来强制使用 person_id 索引来避免排序。我设法又减少了 10%,但它看起来仍然在排序:
SELECT /*+ index(name_part,NAME_PART_person_id) */ person_id
, max(decode(name_part_id, 1, name_part)) first
, max(decode(name_part_id, 2, name_part)) middle
, max(decode(name_part_id, 3, name_part)) last
FROM name_part
WHERE name_part_id IN (1, 2, 3)
GROUP BY person_id
ORDER BY person_id;
Execution Plan
----------------------------------------------------------
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)|
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 56150 | 1864K| | 3385 (1)|
| 1 | SORT GROUP BY | | 56150 | 1864K| 9728K| 3385 (1)|
| 2 | INLIST ITERATOR | | | | | |
| 3 | TABLE ACCESS BY INDEX ROWID | NAME_PART | 168K| 5593K| | 2500 (1)|
| 4 | BITMAP CONVERSION TO ROWIDS| | | | | |
|* 5 | BITMAP INDEX SINGLE VALUE | NAME_PART_NAME_PART_ID| | | | |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("NAME_PART_ID"=1 OR "NAME_PART_ID"=2 OR "NAME_PART_ID"=3)
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
971 consistent gets
0 physical reads
0 redo size
5298159 bytes sent via SQL*Net to client
26435 bytes received via SQL*Net from client
3745 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
56150 rows processed
但是,上述计划都是基于您从整个表格中进行选择的假设。如果您根据 person_id 限制结果(例如,person_id 在 55968 和 56000 之间),事实证明您使用散列连接的原始查询是最快的(对于我指定的约束,27 对 106 一致获取)。
在第三方面,如果上面的查询被用于填充使用光标滚动结果集的 GUI(这样一开始您只会看到结果集的前 N 行 - 通过添加在此处复制一个“and rowcount < 50”谓词),我的查询版本再次变得很快 - 非常快(4 个一致获取对 417 个)。
这个故事的寓意是,它实际上完全取决于您访问数据的方式。适用于整个结果集的查询在应用于不同的子集时可能会更差。
关于sql - 帮助优化 Oracle 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1272990/
Oracle 即时客户端和 Oracle 客户端有什么区别?你能给我解释一下吗?谢谢 最佳答案 Oracle 客户端带有安装程序和许多可执行文件,例如 sqlplus,tnsping,它是完整而庞大的
我正在寻找一个Delphi组件来直接连接到ORACLE数据库服务器,而无需安装oracle客户端。 我知道Oracle Data Access (ODAC)来自DevArt 。还有其他组件具有此功能吗
如何编写 Oracle 存储过程,以表 (X) 作为输入参数,并在过程内部使用表 X 来与另一个表 Y 联接? 表 X 将包含数千条记录。 不希望将表名作为 varchar 传递,然后使用动态 SQL
如何编写 Oracle 存储过程,以表 (X) 作为输入参数,并在过程内部使用表 X 来与另一个表 Y 联接? 表 X 将包含数千条记录。 不希望将表名作为 varchar 传递,然后使用动态 SQL
我要过滤COMMENTS属性为空的记录 我试过了 SELECT TABLE_NAME, COMMENTS FROM (SELECT TABLE_NAME, COMMENTS FROM (sel
我要下载 Oracle Instant Client for Linux x86-64 (64-bit)现在有一段时间了。 现在我注意到该网站在过去几个月中一直遇到技术问题。 要从 Oracle 下载
有什么方法可以将我的 Delphi 应用程序 (FireDac) 直接连接到 Oracle 数据库? 目前可以连接,但需要安装Oracle Client 在 embarcadero 站点 ( http
我有一张表,其中日期列的数据格式如下:“7/25/2014 12:14:27 AM”。我需要通过放入 where 子句来获取此日期。有人可以建议我该怎么做吗? 最佳答案 日期(存储在表中)是 repr
如果两个事务试图同时修改同一行会发生什么?通常,一旦行被修改,另一个事务等待直到第一个事务执行提交或回滚。但是,如果他们恰好在同一时刻发送更新请求怎么办? 最佳答案 答案是否定的。两个事务不能同时修改
我想知道为什么我不能在 Oracle 模式中有两个同名的索引?它抛出一个错误,指出该名称已被使用。我的印象是,由于索引在一个特定的表上,这应该不会导致任何错误,除非我们对同一个表上的两个不同列使用相同
我需要构建一个查询来按成员和到期日期检索信息组,但我需要为每个成员提供一个序列号.. 例如: 如果成员“A”有 3 条记录要过期,“B”只有 1 条,“C”有 2 条,我需要这样的结果: Number
独立程序 create procedure proc1 ( begin end; ) 存储过程 create package pkg1 ( procedure proc2 begin end; ) 最
在 Oracle 9i 中声明 FK 时遇到问题。我在这里查看了许多关于 SO 和一些在线文档(例如 http://www.techonthenet.com/oracle/foreign_keys/f
我和我的同事维护的应用程序在后端有一个 Oracle 数据库。我们正在考虑偶尔以“受限”模式运行应用程序,其中一个数据库表空间设置为只读。我们可以轻松地将必要的表和索引移动到单独的表空间,这些表空间将
我想实现一个自定义的回归聚合函数,类似于现有的 REGR_SLOPE . 我要定义的函数需要获取两列作为参数,例如 select T.EMPLOYEE_ID, CUSTOM_REGR_SL
我已经尝试解决这个问题一段时间了,我认为是时候寻求帮助了。我正在构建一个架构配置脚本,我想添加一些脚本输出和错误处理。这个想法是脚本输出窗口只会向我显示关键消息而没有所有噪音。 Create Temp
在旧的 Oracle 服务器(我被告知是 8i)上使用 JDBC 时,我遇到了一个非常令人困惑和奇怪的问题。我在那里准备了一个表,其中包含大约 10 列、数字、varchars、一个 raw(255)
我有一张 table Customer_Chronics在 Oracle 11g 中。 该表具有三个关键列,如下所示: 分支代码 客户 ID 期 我已按 branch_code 列表按表分区,现在我进
是否有存储用户自定义异常的oracle表? 最佳答案 没有。 与其他变量一样,用户定义的异常在 PL/SQL block 中定义,并且具有 PL/SQL 变量将具有的任何范围。所以,例如 DECLAR
在 oracle 中使用序列并使用 Before insert trigger 自动递增列或使用标识列是否更好,因为它在 Oracle 12 c 中可用? 最佳答案 无论哪种方式,您都将使用序列。 1
我是一名优秀的程序员,十分优秀!