[关闭]
@Catyee 2021-04-29T11:59:27.000000Z 字数 9111 阅读 563

oracle索引和约束的迁移

工作


一、索引的迁移

1.1 oracle中的索引

oracle中的索引从实现上可以分为B-Tree索引和非B-Tree索引,B-Tree索引是oracle默认的索引方式,一般创建的索引都是B-Tree索引,所以也被成为"普通索引(normal index)",非B-tree索引指的是位图索引(bitmap索引)、基于函数的索引(function-based index)、域索引(application domain indexes)

1.1.1 普通索引:

  1. -- 最普通的索引
  2. CREATE INDEX index_1 ON index_test_table (col2);
  3. -- 复合索引
  4. CREATE INDEX index_2 ON index_test_table (col3, col4);
  5. -- 唯一索引
  6. CREATE UNIQUE INDEX index_3 ON index_test_table (col5);
  7. -- 指定排序方式
  8. CREATE INDEX index_4 ON index_test_table (col6 ASC, col7 DESC);
  9. -- 索引压缩
  10. CREATE INDEX index_5 ON index_test_table (col8, col9) COMPRESS ADVANCED; -- oracle 12c才支持
  11. CREATE INDEX index_6 ON index_test_table (col10, col11) COMPRESS 1;
  12. -- 以上可以组合:
  13. CREATE UNIQUE INDEX index_7 ON index_test_table (col12, col13 ASC, col14 DESC) COMPRESS 1;
  14. // 反转索引
  15. create index PK_REV_TEST02 on TEST02(EMPNO) REVERSE;
  16. -- 主键索引:主键索引其实是唯一索引+非空约束

二级索引(multivalue index):
二级索引只作用于JSON类型的数据。用"."语法指定需要进行索引的数据。

  1. CREATE MULTIVALUE INDEX mvi_1 ON mytable t (t.jcol.credit_score.numberOnly());

1.1.2 基于函数的索引

  1. CREATE INDEX index_8 ON index_test_table (12 * col15 * col16, col15, col16);
  2. CREATE INDEX index_9 ON index_test_table (UPPER(col17));
  3. CREATE INDEX index_10 ON index_test_table (CASE col18 WHEN 'A' THEN 'A' END);
  4. CREATE UNIQUE INDEX index_11 ON index_test_table
  5. (CASE WHEN col19 =2 THEN col20 ELSE NULL END,
  6. CASE WHEN col19= 2 THEN col19 ELSE NULL END);

1.1.2 分区索引

oracle中的分区索引是给分区表用的,分区表只有oracle商业版才能使用,测试版和教育版无法使用。
分区索引分为局部分区索引和全局分区索引。
局部分区索引即局部索引于分区一一对应,每个分区都有一个分区索引,每个分区索引都只索引它对应的分区。
局部分区索引又分为局部前缀索引(Local prefixed index)和局部非前缀索引(
Local nonprefixed index)

全局分区索引是针对所有分区的,一个全局分区索引可能指向所有表分区。全局前缀索引(prefixed global index),索引分区键必须作为索引定义的第一列。

  1. -- 局部分区索引
  2. 创建分区表
  3. CREATE TABLE hash_sales
  4. ( prod_id NUMBER(6)
  5. , cust_id NUMBER
  6. , time_id DATE
  7. , channel_id CHAR(1)
  8. , promo_id NUMBER(6)
  9. , quantity_sold NUMBER(3)
  10. , amount_sold NUMBER(10,2)
  11. )
  12. PARTITION BY HASH (prod_id)
  13. PARTITIONS 2;
  14. 创建分区索引:
  15. CREATE INDEX hash_sales_idx ON hash_sales(time_id) LOCAL;
  16. 创建分区表:
  17. CREATE TABLE hash_products
  18. ( product_id NUMBER(6) PRIMARY KEY
  19. , product_name VARCHAR2(50)
  20. , product_description VARCHAR2(2000)
  21. , category_id NUMBER(2)
  22. , weight_class NUMBER(1)
  23. , warranty_period INTERVAL YEAR TO MONTH
  24. , supplier_id NUMBER(6)
  25. , product_status VARCHAR2(20)
  26. , list_price NUMBER(8,2)
  27. , min_price NUMBER(8,2)
  28. , catalog_url VARCHAR2(50)
  29. , CONSTRAINT product_status_lov_demo
  30. CHECK (product_status in ('orderable'
  31. ,'planned'
  32. ,'under development'
  33. ,'obsolete')
  34. ) )
  35. PARTITION BY HASH (product_id)
  36. PARTITIONS 4
  37. STORE IN (tbs_01, tbs_02, tbs_03, tbs_04);
  38. CREATE INDEX prod_idx ON hash_products(category_id) LOCAL
  39. STORE IN (tbs_01, tbs_02);
  40. -- 全局分区索引
  41. 范围分区
  42. CREATE INDEX index_par1 ON index_test_table (col2)
  43. GLOBAL PARTITION BY RANGE (col2)
  44. (PARTITION p1 VALUES LESS THAN (1000),
  45. PARTITION p2 VALUES LESS THAN (2000),
  46. PARTITION p3 VALUES LESS THAN (MAXVALUE));
  47. hash分区
  48. CREATE INDEX index_4 ON index_test_table (col2)
  49. GLOBAL PARTITION BY HASH (col2)
  50. PARTITIONS 4;

1.1.3 位图索引

位图索引只在商业版中可用,测试版和教育版是不能用的。

位图索引用一个索引键条目存储指向多行的指针。B-Tree索引则是一个索引键对应表中的一行数据。

比如说一张表有一列存储性别,取值可以是'男'、'女',如果创建普通索引,也就是B-Tree索引,依然要扫描近一半的数据,但如果在这一列上建立了位图索引,针对每行的rowid,位图索引形成两个向量,男向量为10100...,向量的每一位表示该行是否是男,如果是则位1,否为0,同理,女向量位01011...,可以理解为给每行数据的性别列中为产生两个向量分别为男向量和女向量:男向量中值为男:用1表示,值不是男用0表示,同理女向量中值为女:用1表示,值不是女:用0表示。当我们使用查询语句“select * from table where Gender='男'的时候,首先取出男向量10100...,然后取出未婚向量00100...,将两个向量做与操作,这时生成新向量00100...,可以发现rowid=3的与操作之后的结果为1,表示该表的rowid=3的这行数据就是我们需要查询的结果。

位图索引适合只有几个固定值的列,如性别、婚姻状况、行政区等等,而身份证号这种类型不适合用位图索引。

位图索引在读密集的环境中能很好地工作,但是对于写密集的环境则极不适合,原因在于,一个位图索引键条目(可以理解为前面的男 、女、未婚、已婚等)指向多行。如果一个会话修改了有索引的列的数据,那么大多数情况下,这个索引条目指向的所有行都会被锁定。这样就极大的影响了并发性,因为每个更新都有可能锁定数百行。

所以位图索引不适合用于OLTP的场景,一般都是用oracle建数仓的时候才会用到。

创建位图索引:

  1. -- 普通位图索引
  2. CREATE BITMAP INDEX bm_index_1 on par_table (gender);
  3. -- 位图和基于函数的索引
  4. CREATE BITMAP INDEX typeid_i ON books (SYS_TYPEID(author));
  5. -- bitmap join indexs
  6. CREATE BITMAP INDEX employees_bm_idx
  7. ON employees (jobs.job_title)
  8. FROM employees, jobs
  9. WHERE employees.job_id = jobs.job_id;

1.1.4 应用域索引(application domain index)

应用域索引是特定于应用程序的自定义索引。也就是用户自定义的索引结构。
Oracle数据库本身实现的全文索引,就是应用程序域索引的一个很好的例子。

  1. create table test_tab(dsc clob);
  2. -- 全文索引
  3. create index test_idx on test_tab(dsc) indextype is ctxsys.context;

应用域索引一般是第三方解决方案供应商使用,因为他们需要使用自定义的索引来访问各类非传统的数据,比如文档,空间数据,图像和视频剪辑,Oracle提供的数据类型可能不太适用。

也就是说应用域索引可能经常和自定义的数据类型组合使用。

1.2 mysql中的索引

1、普通索引

  1. CREATE INDEX part_of_name ON customer (name(10)); 对于字符串类型,可以指定索引作用的长度

2、基于函数的索引
基于函数的索引,只在mysql8中才进行支持

  1. CREATE TABLE t1 (col1 INT, col2 INT, INDEX func_index ((ABS(col1))));
  2. CREATE INDEX idx1 ON t1 ((col1 + col2));
  3. CREATE INDEX idx2 ON t1 ((col1 + col2), (col1 - col2), col1);
  4. ALTER TABLE t1 ADD INDEX ((col1 * 40) DESC);

3、fulltext索引

  1. CREATE FULLTEXT INDEX ft_email_name ON `student` (`name`)

4、空间索引

  1. CREATE TABLE `gim` (
  2. `path` varchar(512) NOT NULL,
  3. `box` geometry NOT NULL,
  4. PRIMARY KEY (`path`),
  5. SPATIAL KEY `box` (`box`)
  6. ) ;

5、二级索引
mysql支持二级索引,二级索引用于对JSON数组进行索引。

  1. Create Table: CREATE TABLE `customers` (
  2. `id` bigint(20) NOT NULL AUTO_INCREMENT,
  3. `modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  4. `custinfo` json DEFAULT NULL,
  5. PRIMARY KEY (`id`),
  6. KEY `zips` ((cast(json_extract(`custinfo`,_latin1'$.zip') as unsigned array)))
  7. )

目前能够迁移的索引类型:
普通索引
基于函数的索引

1.3 oracle中索引信息的获取

索引信息的获取有两种途径:jdbc和系统表
如果通过jdbc:

  1. ResultSet getIndexInfo(String catalog,String schema,String table,Boolean unique,Boolean approximate)
  2. resultset中可以获取的信息:
  3. TABLE_CAT:表的目录名,可以为null
  4. TABLE_SCHEMA:表的模式名,可以为null
  5. TABLE_NAME:表名
  6. NON_UNIQUE:如果为真则说明索引值不唯一,为假则说明索引值必须唯一。
  7. INDEX_QUALIFIER:表示索引目录,可以为null
  8. INDEX_NAME:不为空则代表索引名。
  9. TYPE:指出索引类型
  10. ORDINAL_POSITION:指出索引中列的序号,当TYPEtableIndexStatistic时为0
  11. COLUMN_NAME:指出索引的列名,当TYPEtableIndexStatistic时为null
  12. ASC_OR_DESC:为ad。当为a的时候,说明索引是按照升序排列的;当为d的时候,说明索引是按照降序排列的;当为null时,说明不支持按一定顺序排序,或者TYPE的值是tableIndexStatistic
  13. CARDINALITY:指出在索引中值是唯一的列的个数,当TYPEtableIndexStatistic时,这个值是表中的行数。
  14. PAGES:当前索引所使用的页数,当TYPEtableIndexStatic时,这个值表明该表使用的页数。
  15. FILTER_CONDITION:过滤条件,可以为null

可以看到通过jdbc能获取到的信息有限,第二种就是通过查询系统表来获取,但是要通过多个sql进行组合

  1. SELECT INDEX_name,INDEX_type,table_owner,table_name,uniqueness FROM user_indexes WHERE table_name='INDEX_TEST_TABLE';
  2. 查询结果:
  3. INDEX_11 FUNCTION-BASED NORMAL ORA1 INDEX_TEST_TABLE UNIQUE
  4. INDEX_10 FUNCTION-BASED NORMAL ORA1 INDEX_TEST_TABLE NONUNIQUE
  5. INDEX_9 FUNCTION-BASED NORMAL ORA1 INDEX_TEST_TABLE NONUNIQUE
  6. INDEX_8 FUNCTION-BASED NORMAL ORA1 INDEX_TEST_TABLE NONUNIQUE
  7. INDEX_7 FUNCTION-BASED NORMAL ORA1 INDEX_TEST_TABLE UNIQUE
  8. INDEX_6 NORMAL ORA1 INDEX_TEST_TABLE NONUNIQUE
  9. INDEX_4 FUNCTION-BASED NORMAL ORA1 INDEX_TEST_TABLE NONUNIQUE
  10. INDEX_3 NORMAL ORA1 INDEX_TEST_TABLE UNIQUE
  11. INDEX_2 NORMAL ORA1 INDEX_TEST_TABLE NONUNIQUE
  12. INDEX_1 NORMAL ORA1 INDEX_TEST_TABLE NONUNIQUE
  13. SYS_C007886 NORMAL ORA1 INDEX_TEST_TABLE UNIQUE

第一个sql能够知道索引名称、索引的类型、索引的唯一性、索引作用的表。还不知道的信息是索引作用的列

  1. SELECT * FROM user_ind_columns WHERE table_name='INDEX_TEST_TABLE' AND index_name='INDEX_2';
  2. 查询结果:
  3. INDEX_2 INDEX_TEST_TABLE COL3 1 20 20 ASC
  4. INDEX_2 INDEX_TEST_TABLE COL4 2 20 20 ASC
  5. SELECT * FROM user_ind_columns WHERE table_name='INDEX_TEST_TABLE' AND index_name='INDEX_11';
  6. 查询结果:
  7. INDEX_11 INDEX_TEST_TABLE SYS_NC00026$ 1 22 0 ASC
  8. INDEX_11 INDEX_TEST_TABLE SYS_NC00027$ 2 22 0 ASC

可以获取列名,列处于索引中的位置,升序还是降序
但是有些是虚拟列,虚拟列中实际存的是函数相关信息,进一步查询:

  1. SELECT * FROM all_tab_cols WHERE table_name='INDEX_TEST_TABLE' AND column_name='SYS_NC00026$';
  2. 查询结果:
  3. CASE "COL19" WHEN 2 THEN "COL20" ELSE NULL END

看到了函数的定义,但是是字符串的类型。

如果要进行函数的迁移,同样要解析字符串的语法。

1.4 索引对象的建模

在Java中用一个对象进行建模:

  1. public class TableIndex {
  2. private String indexName; // 索引名称
  3. private IndexType indexType; // 索引类型
  4. private Boolean uniqueness; // 是否唯一
  5. private List<ColExpression> colExpressions; // 作用的列
  6. }
  7. public class ColExpression {
  8. private String colExpression; // 表达式(列或函数字符串)
  9. private Integer effectLength; // 列作用的长度
  10. private OrderType orderType; // 排序方式,降序还是升序
  11. private Integer seq; // 列在索引中的序号
  12. }

通过上面的对象恢复成mysql中能执行的索引:

  1. -- 普通索引
  2. CREATE INDEX <index_name> on <catalog.table> (<colExpression> <orderType>, <colExpression> <orderType>...);
  3. -- 基于函数的索引:
  4. CREATE INDEX <index_name> on <catalog.table> ((<colExpression>, <colExpression>...));

二、约束的迁移

约束只有几种:
主键约束、唯一性约束、非空约束、检查约束、外键约束
由于主键在获取表结构的时候已经获得,不用特意处理。
唯一性约束在迁移索引的时候已经获得,不用特意处理。
非空约束在获取列的时候已经获得,不用特意处理。
要处理的只有检查约束和外键约束。

2.1 oracle中的约束

  1. CREATE TABLE foreign_table(col1 int PRIMARY KEY, col2 int);
  2. CREATE TABLE constraint_table
  3. (col1 int PRIMARY key,
  4. col2 int UNIQUE,
  5. col3 varchar(5) NOT NULL,
  6. col4 char(1) constraint ck_constraint check(col4 in('M','W')),
  7. col5 int CONSTRAINT fk_constraint REFERENCES foreign_table(col1) ON DELETE SET NULL)

一个更复杂外键约束的例子:

  1. CREATE TABLE departments
  2. (department_id int,
  3. manager_id int,
  4. department_name varchar(20),
  5. PRIMARY KEY(department_id, manager_id));
  6. CREATE TABLE dept_20
  7. (employee_id NUMBER(4),
  8. last_name VARCHAR2(10),
  9. job_id VARCHAR2(9),
  10. department_id int,
  11. manager_id int,
  12. hire_date DATE,
  13. salary NUMBER(7,2),
  14. commission_pct NUMBER(7,2)
  15. );
  16. ALTER TABLE dept_20
  17. ADD CONSTRAINT fk_empid_hiredate
  18. FOREIGN KEY (department_id, manager_id)
  19. REFERENCES departments(department_id, manager_id)
  20. EXCEPTIONS INTO wrong_emp;
  21. CREATE TABLE wrong_emp
  22. (row_id rowid,
  23. owner varchar2(30),
  24. table_name varchar2(30),
  25. constraint_name varchar2(30)
  26. );

2.2 mysql中的约束

mysql中没有检查约束
下面是一个外键约束的例子:

  1. create table tab1 (id int primary key);
  2. create table tab2 (
  3. id int primary key,
  4. col1 int,
  5. foreign key (col1) references tab1(id));

2.3 oracle中约束信息的获取

只能通过系统表的方式获取:

  1. SELECT * FROM user_constraints WHERE TABLE_NAME='DEPT_20';
  2. SELECT * FROM user_cons_columns WHERE TABLE_NAME='DEPT_20' ;
  3. SELECT * FROM user_cons_columns WHERE constraint_name = 'SYS_C007863';

2.4 约束对象的建模:

  1. public class CheckConstraint {
  2. private String constraintName;
  3. private ConstraintType constType;
  4. private String colName;
  5. private String expression;
  6. }
  7. public class ForiginConstraint {
  8. private String constraintName;
  9. private ConstraintType constType;
  10. private String colName;
  11. }
  12. public class ForiginPart {
  13. private String referTable;
  14. private List<String> cols;
  15. private List<String> referCols;
  16. }

mysql关闭外键约束检查:
SET FOREIGN_KEY_CHECKS=0;
外键约束还原:

  1. ALTER TABLE <table> ADD CONSTRAINT <fk_name> FOREIGN KEY(<cols...>) REFERENCES <refer table>(<referr cols...>)

mysql打开外键约束检查:
SET FOREIGN_KEY_CHECKS=1;

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注