#
第六章 SQL命令 CREATE INDEX(一)
为表创建索引。
# 大纲
```sql
CREATE [UNIQUE | BITMAP | BITMAPEXTENT | BITSLICE ] INDEX index-name
ON [TABLE] table-name
(field-name, ...)
[AS index-class-name [ (parameter-name = parameter_value, ... ) ] ]
[WITH DATA (datafield-name, ...)]
```
# 参数
- `UNIQUE` - 可选的——一个约束,确保表中不会有两行索引中所有字段的值相同。不能为位图或位片索引指定此关键字。`UNIQUE`关键字后面可以跟(或被)`CLUSTERED`或`NONCLUSTERED`关键字替换。
这些关键字是`no-ops`;
它们是为了与其他供应商兼容而提供的。
- `BITMAP` - 可选—表示创建位图索引。
位图索引允许对具有少量不同值的字段进行快速查询。
- `BITMAPEXTENT` - 可选-表示应该创建位映射范围索引。
一个表最多只能创建一个位映射范围索引。
`BITMAPEXTENT`中没有指定字段名。
- `BITSLICE` - 可选—创建位片索引。
位片索引可以非常快速地计算某些表达式,比如求和和范围条件。
这是一种专门化的索引类型,只能用于解决非常特定的问题。
- `index-name` - 定义的索引。名称是一个标识符。
- `table-name` - 为其定义索引的现有表的名称。不能为视图创建索引。表名可以是限定的(`schema.table`),也可以是非限定的(`Table`)。未限定的表名采用缺省模式名。
- `field-name` - 用作索引基础的一个或多个字段名。字段名必须用括号括起来。多个字段名称用逗号分隔。每个字段名后面可以跟一个`ASC`或`DESC`关键字。这些关键词是禁止操作;提供它们是为了与其他供应商兼容。
- `AS index-class-name` - 可选—定义索引的类,可选地后跟圆括号,圆括号中包含一对或多对逗号分隔的参数名称和关联值。
- `WITH DATA (datafield-name)` - 可选—要定义为索引数据属性的一个或多个字段名。字段名必须用括号括起来。多个字段名称用逗号分隔。指定位图或位片索引时,不能指定`WITH DATA`子句。
# 描述
`CREATE INDEX`在命名表的指定字段(或多个字段)上创建排序索引。IRIS使用索引来提高查询操作的性能。 IRIS在`INSERT`、`UPDATE`和`DELETE`操作期间自动维护索引,这种索引维护可能会对这些数据修改操作的性能产生负面影响。
可以使用`CREATE INDEX`命令或通过将索引定义添加到类定义来创建索引,可以使用`DROP INDEX`命令删除索引。
`CREATE INDEX`可用于创建以下三种类型的索引中的任何一种:
- 常规索引(`Type=index`):指定`CREATE INDEX`(用于非唯一值)或`CREATE UNIQUE INDEX`(用于唯一值)。
- 位图索引(`Type=bitmap`):指定`CREATE bitmap index`。
- `bitslice`索引(`Type=bitslice`):指定`CREATE bitslice index`。
还可以使用`%Dictionary.IndexDefinition`类定义索引。
可以使用`CREATE INDEX`向分片表添加索引。
# 权限与锁
`CREATE INDEX`命令属于特权操作。
用户必须具有`%ALTER_TABLE`管理权限才能执行`CREATE INDEX`。
如果不这样做,将导致`%msg`的`SQLCODE -99`错误,`User 'name' does not have %ALTER_TABLE privileges`。
如果拥有适当的权限,可以使用`GRANT`命令为用户或角色分配`%ALTER_TABLE`权限。
管理权限是特定于名称空间的。
用户必须对指定的表具有`%ALTER`权限。
如果用户是表的`Owner`(创建者),则自动授予该用户对该表的`%ALTER`权限。
否则,用户必须被授予`%ALTER`权限。
如果不这样做,将导致一个`SQLCODE -99`错误,其中`%msgUser 'name' does not have required %ALTER privilege needed to change the table definition for 'Schema.TableName'`。
可以通过调用`%CHECKPRIV`命令来确定当前用户是否具有%ALTER权限。
可以使用`GRANT`命令为指定的表分配`%ALTER`权限。
- 不能在从持久类投射的表上使用`CREATE INDEX`,除非表类定义包含`[DdlAllowed]`。
否则,操作将失败,出现`SQLCODE -300`错误,并且`%msg DDL not enabled for class 'Schema.tablename'`。
- 不能在部署的持久类映射的表上使用`CREATE INDEX`。
此操作失败,出现`SQLCODE -400`错误,并显示`%msg Unable to execute DDL that modifies a deployed class: 'classname'`。
`CREATE INDEX`语句获取表名上的表级锁。
这可以防止其他进程修改表的数据。
这个锁在`CREATE INDEX`操作结束时自动释放。
`CREATE INDEX`在相应的类定义上维护一个锁,直到创建索引操作(包括索引数据的填充)完成。
在创建索引时,表不能被其他进程以`EXCLUSIVE`模式或`SHARE`模式锁定。
在被锁定的表上尝试`CREATE INDEX`操作会导致`SQLCODE -110`错误,并带有`%msg`,如下所示: `Unable to acquire exclusive table lock for table 'Sample.MyTest'`。
# 仅支持兼容性选项
`SQL`仅接受以下``CREATE INDEX选项用于解析目的,以帮助将现有SQL代码转换为 SQL。
这些选项不提供任何实际的功能。
```sql
CLUSTERED | NONCLUSTERED owner.catalog. ASC | DESC
```
下面的例子展示了这些`no-op`关键字的位置:
```sql
CREATE UNIQUE CLUSTERED INDEX index-name
ON TABLE owner.catalog.schema.table
(field1 ASC, field2 DESC)
```
# 索引名称
索引的名称在给定的表中必须是唯一的。
索引名称遵循标识符约定,受以下限制。
默认情况下,索引名是简单的标识符;
索引名称可以是分隔的标识符。
索引名不能超过`128`个字符。
索引名不区分大小写。
IRIS使用提供的名称(它引用为`“SqlName”`)在类和全局中生成相应的索引属性名称。
此索引属性名称仅包含字母和数字字符(字母和数字),长度最多为`96`个字符。
为了生成索引属性名, IRIS首先从提供的`SqlName`中去掉标点字符,然后生成一个只有`96`个字符(或更少)的唯一标识符来创建唯一的索引属性名。
- 索引名称可以与字段、表或视图名称相同,但不建议重复。
- 索引属性名(在标点剥离后)必须是唯一的。
如果指定了重复的`SQL`索引名,系统将生成`SQLCODE -324`错误。
如果指定的`SQL`索引名称仅在标点字符方面与现有`SQL`索引名称不同, IRIS将最后一个字符替换为大写字母(以`“a”`开头),以创建唯一的索引属性名称。
因此,可以(尽管不建议)创建仅在标点字符上不同的`SQL`索引名。
- 索引属性名必须以字母开头。
因此,索引名的第一个字符或去掉初始标点字符后的第一个字符必须是字母。
有效的字母是通过`$ZNAME`测试的字符。
如果`SQL`索引名称的第一个字符是一个标点符号字符(`%`或`_`),第二个字符是一个数字, IRIS会附加一个小写的`“n”`作为剥离后的索引属性名称的第一个字符。
- 索引名可能比`31`个字符长得多,但是前`31`个字母数字字符不同的索引名更容易处理。
管理门户SQL接口Catalog Details显示每个索引的SQL索引名称(SQL Map name)和相应的索引属性名称(index name)。
当试图创建与现有索引同名的索引时,将会发生什么呢?
## 现有索引
默认情况下, IRIS拒绝创建与该表的现有索引同名的索引,并发出`SQLCODE -324`错误。
要确定当前设置,调用`$SYSTEM.SQL.CurrentSettings()`,它为现有索引设置显示一个`Allow DDL CREATE INDEX for existing index`。
默认值是0,这是该选项的推荐设置。
如果该选项设置为1, IRIS将从类定义中删除现有索引,然后通过执行`CREATE index`重新创建它。
它从`CREATE index`中指定的表中删除指定的索引。
此选项允许删除/重新创建`UNIQUE`约束索引(不能使用`DROP index`命令完成)。
要删除/重新创建主键索引,请参考`ALTER TABLE`命令。
在管理门户、系统管理、配置、SQL和对象设置、SQL中,可以通过选择“忽略冗余DDL语句”复选框,在系统范围内设置此选项(以及其他类似的创建、更改和删除选项)。
但是,即使将此选项设置为允许重新创建现有索引,如果表包含数据,则不能重新创建`Primary Key IDKEY`索引。
尝试这样做会产生`SQLCODE -324`错误。
## 表明
必须指定现有表的名称。
- 如果`table-name`是一个不存在的表,则`CREATE INDEX`失败,出现`SQLCODE -30`错误,并将`%msg`设置为`Table 'SQLUSER.MYTABLE' does not exist`。
- 如果`table-name`是视图,则`CREATE INDEX`失败,出现`SQLCODE -30`错误,并将`%msg`设置为`Attempt to CREATE INDEX 'My_Index' on view SQLUSER.MYVIEW failed. Indices only supported for tables, not views`。
创建索引修改表的定义;
如果没有更改表定义的权限,则`CREATE INDEX`将失败,出现`SQLCODE -300`错误,并将`%msg`设置为`DDL not enabled for class 'schema.tablename'`。
## 字段名称
必须指定至少一个要索引的字段名。
指定一个字段名或用逗号分隔的字段名列表,这些字段名用括号括起来。
在索引定义中允许并保留重复的字段名。
指定多个字段可能会提高`GROUP BY`操作的性能,例如,先按州分组,然后再按每个州中的城市分组。
通常,应该避免在一个或多个具有大量重复数据的字段上建立索引。
例如,在人员数据库中,在`Name`字段上建立索引是合适的,因为大多数名称都是惟一的。
在`State`字段上建立索引(在大多数情况下)是不合适的,因为存在大量重复的数据值。
指定的字段必须在表中定义,或者在表的持久类的超类中定义。
(当然,所有的类都必须经过编译。)
指定不存在的字段将产生`SQLCODE -31`错误。
除了普通的数据字段,还可以使用`CREATE INDEX`来创建索引:
- 在`SERIAL`字段(`%Counter`字段)上。
- 在`IDENTITY`字段上。
- 在集合的`ELEMENTS`或`KEYS`值上。
不能在流值字段上创建索引。
如果一个`IDKEY`字段(属性)是`SQL Computed`,则不能创建具有多个`IDKEY`字段的索引。
这个限制不适用于单个字段`IDKEY`索引。
因为索引中的多个`IDKEY`字段是用`“||”`(双竖条)字符分隔的,所以不能在`IDKEY`字段数据中包含这个字符串。
### 嵌入对象中的字段(`%SerialObject`)
要在嵌入式对象中创建字段索引,需要在引用该嵌入式对象的表(`%Persistent`类)中创建索引。
在`CREATE INDEX`中,字段名指定了表(`%Persistent object`)中引用字段的名称(通过下划线连接到嵌入对象(`%SerialObject`)中的字段名),如下面的示例所示:
```sql
CREATE INDEX StateIdx ON TABLE Sample.Person (Home_State)
```
这里`Home`是`Sample.Person`中的一个字段,它引用嵌入式对象`Sample.Address`,其中包含`State`字段。
只有那些与持久类引用属性相关联的嵌入对象记录才会被索引。不能直接索引`%SerialObject`属性。
## 索引类名
此可选语法允许用户使用SQL指定函数索引的类和参数。
SQL示例如下:
```sql
CREATE INDEX HistIdx ON TABLE Sample.Person (MedicalHistory) AS %iFind.Index.Basic (LANGUAGE='en', LOWER=1)
```
## WITH DATA子句
指定此子句可能允许仅通过读取索引来解析查询,这将极大地减少磁盘`I/O`量,从而提高性能。
如果`field-name`使用字符串排序,你应该在`field-name`和`WITH DATA datfield -name`中指定相同的字段;
这允许检索未排序的值,而不必转到主映射。
如果`field-name`中的值不使用字符串排序规则,那么在`WITH DATA datfield -name`中指定该字段没有任何好处。
可以在`WITH DATA datfield -name`中指定未被索引的字段。
这允许从索引中满足更多的查询,而不需要访问主映射。
权衡是你想维护多少个索引;
而向索引中添加数据会使索引变得更大,这会减慢不需要数据的操作。
可以在`WITH DATA DATA -name`中指定在表的持久化类的超类中定义的字段。