如何在oracle中按行号查询

时间:2016-03-28 20:04:14

标签: oracle11g query-optimization

有没有办法直接通过Oracle中的表中的行号进行查询?换句话说,在一些基本语言(如C或Java)中实现数组中普通查找的相同效果。我还没有尝试过虚拟列。

例如,以下是高效查询的示例,但它浪费了磁盘空间:

create table ary (row_position_id number(10) NOT NULL,
                  datum binary_float NOT NULL);
declare i pls_integer;
begin
    for i in 0..10000000
    loop
        insert into ary values (i, dbms_random.normal());
    end loop;
    commit;
end;

create unique index ary_rp on ary(row_position_id);

现在,我要创建一组查询值来存储在另一个"参数"表:

create table query_values (qval number(10) NOT NULL);
declare i pls_integer;
begin
    for i in 0..10000
    loop
        insert into query_values (abs(dbms_random.random() % 10000000));
    end loop;
    commit;
end;

现在,拥有这些查询值,我将查询原始表

select d.* from ary d where exists (select 0 from query_values v
                                    where d.row_position_id = v.qval);

现在,这个查询没问题 - 它将使用INDEX UNIQUE SCAN和ROWID的TABLE访问。我遇到的问题是row_position_id在表格块中占用的空间与实际数据(DATUM列)一样多。

我知道索引组织表和虚拟列(不能与IOT一起使用)。当然,像ROWNUM和ROW_NUMBER这样的东西在这里是无关紧要的(除非我误解了一些东西)。

另外值得指出的是,这个表是静态数据 - 一旦加载,它就永远不会改变。我可能会做一个ALTER TABLE ARY READ ONLY;

我真正想要的是:

create table ary (datum binary_float not null);
-- load rows in a specific order
-- efficiently query this table by implicit row position

非常感谢!

亨利

1 个答案:

答案 0 :(得分:0)

我想你会想要保留额外的专栏。原因如下:

正如您所说,ROWNUM和ROW_NUMBER在这里不适用,因为它们是在查询中返回行时生成的;他们不会告诉你有关插入顺序的任何信息。

ROWID怎么样? ROWID就是存储行的位置 - 再次来自docs

  • 对象的数据对象编号
  • 行所在的数据文件中的数据块
  • 数据块中行的位置(第一行为0)
  • 行所在的数据文件(第一个文件为1)。文件号相对于表空间。

“数据块中的位置”听起来很有趣,但你不知道插入的数据块的顺序是什么(Oracle可以使用它可以快速使用的任何数据块),所以这不是一个可靠的选择,即便如此,你将不得不解析人类不可读的ROWID(例如在12g中它们看起来像这样:* BAGAASMCwQL +)

另一个选项是ORA_ROWSCN,它的有趣之处在于它根据系统更改编号确实为您提供了一些订单概念。但是,它不是免费的。首先,您必须使用ROWDEPENDENCIES选项创建表格,并按docs创建:

  

ROWDEPENDENCIES如果要启用,请指定ROWDEPENDENCIES   行级依赖关系跟踪。此设置主要用于   允许在复制环境中进行并行传播。的   将每行的大小增加6个字节

另一个问题是,你必须要跟随用提交插入的每一行,这样每一行都会得到一个不同的SCN。

如果您愿意这么做,您仍然需要将行转换为可以用来连接到其他表的索引(从0或1开始)。

以下是它将涉及的内容的快速示例:

DROP TABLE temp;

CREATE TABLE temp 
   ( a number(10)
   , b varchar2(10)
   ) 
ROWDEPENDENCIES
;

-- one commit after all rows    
INSERT INTO temp VALUES (1, 'A');
INSERT INTO temp VALUES (2, 'B');
INSERT INTO temp VALUES (3, 'C');
INSERT INTO temp VALUES (4, 'D');
INSERT INTO temp VALUES (5, 'E');
INSERT INTO temp VALUES (6, 'F');
COMMIT;

SELECT X.*, ROWNUM
FROM (SELECT T.*
           , ORA_ROWSCN
        FROM TEMP T
       ORDER BY ORA_ROWSCN
     ) x
;    

A   B   ORA_ROWSCN  ROWNUM
1   A   2272340          1
2   B   2272340          2
6   F   2272340          3
4   D   2272340          4
5   E   2272340          5
3   C   2272340          6

糟糕。那些行肯定不是他们进来的顺序。

现在每行使用一次提交:

TRUNCATE TABLE temp;

INSERT INTO temp VALUES (1, 'A');
COMMIT;
INSERT INTO TEMP VALUES (2, 'B');
COMMIT;
INSERT INTO temp VALUES (3, 'C');
COMMIT;
INSERT INTO temp VALUES (4, 'D');
COMMIT;
INSERT INTO temp VALUES (5, 'E');
COMMIT;
INSERT INTO temp VALUES (6, 'F');
COMMIT;

SELECT X.*, ROWNUM
FROM (SELECT T.*
           , ORA_ROWSCN
        FROM TEMP T
       ORDER BY ORA_ROWSCN
     ) x
;

A   B   ORA_ROWSCN  ROWNUM
1   A   2272697          1
2   B   2272699          2
3   C   2272701          3
4   D   2272703          4
5   E   2272705          5
6   F   2272707          6

更好。但如果你有大量的行,它就不会快速进入。 (我想如果你故意想要减慢你的插入内容,你就会这样做。))

我认为这与您尝试使用自己的专栏一样好,但仍然希望节省存储空间:您可以取消表+索引,只需使用索引组织表。它基本上是您直接查询的索引。

就是这么简单:

CREATE TABLE TEMP2
( A NUMBER(10)
, B VARCHAR2(10)
, CONSTRAINT PK_CONSTRAINT PRIMARY KEY (A)
)
ORGANIZATION INDEX 
;

您还需要考虑其他参数,但有关详细信息,请查看docs