使用 API 创建索引

可以使用 SQL 命令或使用 TableRequest API 为 NoSQL 表创建索引。

相关主题

使用 SQL 命令

可以使用 CREATE INDEX 命令创建索引。

创建单个字段索引:

示例:为乘客预订代码创建索引。
CREATE INDEX fixedschema_conf ON baggageInfo(confNo)

以上是单列固定方案索引的示例。在 baggageInfo 表中具有 string 数据类型的 confNo 字段上创建索引。

创建组合索引:

示例:对乘客的全名和电话号码创建索引。
CREATE INDEX compindex_namephone ON baggageInfo(fullName,contactPhone)
以上是组合索引示例。索引是在 baggageInfo 方案中的两个字段(全名和联系人电话号码)上创建的。

注意:

此索引的一个或多个字段可以作为固定方案列。

创建 JSON 索引:

如果 JSON 数据中至少有一个字段,则索引称为 JSON 索引。由于 JSON 是无模式的,因此索引 JSON 字段的数据类型可能因行而异。在 JSON 字段上创建索引时,如果不确定 JSON 字段需要什么数据类型,可以使用 anyAtomic 数据类型。或者,也可以指定 Oracle NoSQL Database 原子数据类型之一。您可以通过使用 AS 关键字在 JSON 字段的每个索引路径旁声明数据类型来实现此目的。

示例 1:对乘客包的标记编号创建索引。
CREATE INDEX jsonindex_tagnum ON baggageInfo(bagInfo[].tagnum as INTEGER)

以上是 JSON 索引的示例。索引是在 baggageInfo 表中 baginfo JSON 字段中的 tagnum 字段上创建的。请注意,您在创建索引时为 tagnum 字段提供数据类型。

如果关联的表包含任何数据违反声明的数据类型的行,则创建 JSON 索引将失败。同样,在创建 JSON 索引后,如果新行不符合 JSON 索引中声明的数据类型,则插入/更新操作将失败。

示例 2:在乘客路线上创建一个索引。
CREATE INDEX jsonindex_routing ON baggageInfo(bagInfo[].routing as ANYATOMIC)
将 JSON 索引路径声明为 anyAtomic 的优点是允许索引的 JSON 字段具有各种数据类型的值。索引条目按升序排序。当这些值存储在索引中时,它们的排序如下:
  • 数字
  • 字符串
  • 布尔值

但是,这一优势被空间和 CPU 成本所抵消。这是因为在索引中存储之前,索引字段中任何类型的数字值都将转换为数字。此强制转换需要 CPU 时间,生成的数字存储将大于该数字的原始存储。

创建简单索引:

如果表中每行数据都有一个条目在索引中创建,则索引称为简单索引。该索引将返回原子数据类型或任何特殊值(SQL NULL、JSON NULL、EMPTY)的单个值。从本质上讲,简单索引的索引路径不能返回数组、映射或嵌套数据类型。

示例:在三个字段上创建一个索引,当上次看到包时,最后一个看到的站以及到达日期和时间。
CREATE INDEX simpleindex_arrival ON baggageInfo(bagInfo[].lastSeenTimeGmt as ANYATOMIC,
bagInfo[].bagArrivalDate as ANYATOMIC, bagInfo[].lastSeenTimeStation as ANYATOMIC)

以上是针对 JSON 字段中的 JSON 文档创建的简单索引示例。该索引是在 lastSeenTimeGmtbagArrivalDatelastSeenTimeStation 上创建的,所有索引都来自 baggageInfo 表信息 JSON 字段中的 bagInfo JSON 文档。如果简单索引路径的求值返回空结果,则将特殊值 EMPTY 用作索引条目。在上面的示例中,如果 bagInfo JSON 文档中没有 lastSeenTimeGmtbagArrivalDatelastSeenTimeStation 条目,或者没有 bagInfo JSON 数组,则为特殊值 EMPTY 编制索引。

创建多键索引:

索引称为多键索引,前提是对于表中的每一行数据,在索引中创建多个条目。在多键索引中,至少有一个索引路径使用数组或嵌套数据类型。在多键索引中,对于每个表行,都会对数组中要索引的所有元素创建索引条目。

示例 1:多键索引:在流处理帐户应用程序的系列信息数组上创建一个索引。
CREATE INDEX multikeyindex1 ON stream_acct (acct_data.contentStreamed[].seriesInfo[] AS ANYATOMIC)

索引是在 stream_acct 表中的 seriesInfo[] 数组上创建的。此处,将索引 stream_acct 表每一行的 seriesInfo[] 数组中的所有元素。

示例 2:嵌套多键索引:在流帐户应用程序的情节详细信息数组上创建一个索引。

如果创建索引时所在的字段位于数组中,而数组又位于另一个数组中,则索引是嵌套的多键索引。
CREATE INDEX multikeyindex2 ON stream_acct (
    acct_data.contentStreamed[].seriesInfo[].episodes[]  AS ANYATOMIC)

以上是嵌套多键索引的示例,其中字段存在于位于另一个数组内的数组中。该索引是在 stream_acct 表的 acct_data JSON 中 seriesInfo[] 数组中的 episodes[] 数组上创建的。

示例 3:组合多键索引:

如果索引是在多个字段上创建的,则称其为组合多键索引,其中至少有一个字段是多键字段。组合多键索引可以组合使用多键索引路径和简单索引路径。
CREATE INDEX multikeyindex3 ON stream_acct (acct_data.country AS ANYATOMIC,
acct_data.contentStreamed[].seriesInfo[].episodes[]  AS ANYATOMIC)

以上是具有一个多键索引路径和一个简单索引路径的组合多键索引的示例。对 stream_acct 表的 acct_data JSON 列中的 country 字段和 episodes[] 数组创建索引。

要了解多键索引的限制,请参阅多键索引的规范和限制

使用 NO NULLS 子句创建索引

可以使用可选的 WITH NO NULLS 子句创建索引。在这种情况下,不会对索引字段上有 NULL 和/或 EMPTY 值的行进行索引。
CREATE INDEX nonull_phone ON baggageInfo (contactPhone) WITH NO NULLS
  • 上面的查询在乘客的电话号码上创建一个索引。如果有些乘客没有电话号码,则这些字段将不会成为索引的一部分。
  • 当数据在索引字段中包含大量 NULL 和/或 EMPTY 值时,使用 WITH NO NULLS 子句创建的索引可能非常有用。它可以减少索引期间的时间和空间开销。
  • 但是,查询对这些索引的使用受到限制。如果使用 WITH NO NULLS 子句创建索引,则 IS NULL 和 NOT EXISTS 谓词不能用作该索引的索引谓词。
  • 事实上,只有在查询具有每个索引字段的索引谓词时,查询才能使用此索引。

创建每行具有唯一键的索引

可以使用每行属性的唯一键创建索引。
CREATE INDEX idx_showid ON 
stream_acct(acct_data.contentStreamed[].showId AS INTEGER)
WITH UNIQUE KEYS PER ROW

在上面的查询中,在 showId 上创建一个索引,单个 contentStreamed 数组不能有重复的 showId。这将通知查询处理器,对于任何流用户,contentStreamed 数组不能包含具有相同显示 ID 的两个或多个显示。限制是必要的,因为如果存在重复的显示 ID,它们不会包含在索引中。如果在单个 contentStreamed 数组中插入具有相同 showId 的行,则会引发错误,并且插入操作不成功。

查询运行时优化:

创建每行具有唯一键的索引时,索引的条目数将少于 contentStreamed 数组中的元素数。可以编写一个高效的查询来使用此索引。与未使用索引相比,查询使用此类索引将从 FROM 子句中得到的结果更少。

对函数创建索引的示例:

示例 1:创建一个索引,该索引按 BaggageInfo 表的最新修改时间索引这些行:
CREATE INDEX idx_modtime ON BaggageInfo(modification_time())
此索引将用于将 modification_time 作为筛选条件的查询。
SELECT * FROM BaggageInfo $u WHERE 
modification_time($u) > "2019-08-01T10:45:00"

此查询返回最近修改时间晚于 2019-08-01T10:45:00 的所有行。它使用上面定义的 idx_modtime 索引。可以通过使用 show query 命令查看查询计划来验证这一点。

示例 2:创建一个索引,在路由字段的长度上为 BaggageInfo 表的行编制索引。
CREATE INDEX idx_routlen ON BaggageInfo (length(bagInfo[].routing as string))
此索引将用于将 length 作为筛选条件的查询。
SELECT * from BaggageInfo $bag where length($bag.bagInfo[].routing) > 10

此查询返回路由字段长度大于 10 的所有行。它使用上面定义的 idx_routlen 索引。可以通过使用 show query 命令查看查询计划来验证这一点。

示例 3:使用多键索引路径

在以下示例中,按观看的节目的 ID 以及观看节目的日期的年份和月份为 stream_acct 表中的用户编制索引。
CREATE INDEX idx_showid_year_month ON 
stream_acct(acct_data.contentStreamed[].showId AS INTEGER,
substring(acct_data.contentStreamed[].seriesInfo[].episodes[].date AS STRING,0, 4),
substring(acct_data.contentStreamed[].seriesInfo[].episodes[].date AS STRING,5, 2))
使用此索引的查询示例如下所示。此查询计算在 2022 年观看任何节目 16 的用户数。
SELECT count(*) FROM stream_acct s1 WHERE EXISTS 
s1.acct_data.contentStreamed[$element.showId = 16].seriesInfo.
episodes[substring($element.date, 0, 4) = "2022"]
此查询将使用索引 idx_showid_year_month。可以通过使用 show query 命令查看查询计划来验证这一点。
show query SELECT count(*) FROM stream_acct s1 WHERE EXISTS
> s1.acct_data.contentStreamed[$element.showId = 16].seriesInfo.episodes[substring($element.date, 0, 4) = "2022"]

{
  "iterator kind" : "GROUP",
  "input variable" : "$gb-1",
  "input iterator" :
  {
    "iterator kind" : "RECEIVE",
    "distribution kind" : "ALL_SHARDS",
    "distinct by fields at positions" : [ 1 ],
    "input iterator" :
    {
      "iterator kind" : "SELECT",
      "FROM" :
      {
        "iterator kind" : "TABLE",
        "target table" : "stream_acct",
        "row variable" : "$$s1",
        "index used" : "idx_showid_year_month",
        "covering index" : true,
        "index row variable" : "$$s1_idx",
        "index scans" : [
          {
            "equality conditions" : {"acct_data.contentStreamed[].showId":16,"substring#acct_data.contentStreamed[].seriesInfo[].episodes[].date@,0,4":"2022"},
            "range conditions" : {}
          }
        ]
      },
      "FROM variable" : "$$s1_idx",
      "SELECT expressions" : [
        {
          "field name" : "Column_1",
          "field expression" :
          {
            "iterator kind" : "CONST",
            "value" : 1
          }
        },
        {
          "field name" : "acct_id_gen",
          "field expression" :
          {
            "iterator kind" : "FIELD_STEP",
            "field name" : "#acct_id",
            "input iterator" :
            {
              "iterator kind" : "VAR_REF",
              "variable" : "$$s1_idx"
            }
          }
        }
      ]
    }
  },
  "grouping expressions" : [

  ],
  "aggregate functions" : [
    {
      "iterator kind" : "FUNC_COUNT_STAR"
    }
  ]
}

使用 TableRequest API

可以使用 TableRequest API 对 NoSQL 表创建索引。

TableRequest 类用于对表创建索引。执行此请求指定的操作是异步的。这些可能是长时间运行的操作。TableResultTableRequest 操作返回,它封装表的状态。有关 TableRequest 类及其方法的更多详细信息,请参阅 Oracle NoSQL Java SDK API Reference

Download the full code Indexes.java from the examples here.

/**
* Create an index acct_episodes in the stream_acct table
*/
private static void crtIndex(NoSQLHandle handle) throws Exception {
   String createIndexDDL = "CREATE INDEX acct_episodes ON " + tableName +
                           "(acct_data.contentStreamed[].seriesInfo[].episodes[]  AS ANYATOMIC)";
   TableRequest treq = new TableRequest().setStatement(createIndexDDL);
   TableResult tres = handle.tableRequest(treq);
   tres.waitForCompletion(handle, 60000, /* wait 60 sec */
          1000); /* delay ms for poll */
   System.out.println("Index acct_episodes on " + tableName + " is created");
}

borneo.TableRequest 类用于对表创建索引。对 borneo.NoSQLHandle.table_request() 的所有调用都是异步的,因此需要检查结果并调用 borneo.TableResult.wait_for_completion() 等待操作完成。有关 table_request 及其方法的更多详细信息,请参阅 Oracle NoSQL Python SDK API Reference

Download the full code Indexes.py from the examples here.

#create an index
def create_index(handle):
   statement = '''CREATE INDEX acct_episodes ON stream_acct (acct_data.contentStreamed[].seriesInfo[].episodes[]  AS ANYATOMIC)'''
   request = TableRequest().set_statement(statement)
   table_result = handle.do_table_request(request, 40000, 3000)
   table_result.wait_for_completion(handle, 40000, 3000)
   print('Index acct_episodes on the table stream_acct is created')

The TableRequest class is used to create an index on a table. Execution of operations specified by TableRequest is asynchronous. These are potentially long-running operations. This request is used as the input of a Client.DoTableRequest() operation, which returns a TableResult that can be used to poll until the table reaches the desired state. See Oracle NoSQL Go SDK API Reference for more details on the various methods of the TableRequest class.

Download the full code Indexes.go from the examples here.

//create an index on a table
func createIndex(client *nosqldb.Client, err error, tableName string)(){
   stmt := fmt.Sprintf("CREATE INDEX acct_episodes ON %s "+
		"(acct_data.contentStreamed[].seriesInfo[].episodes[]  AS ANYATOMIC)",tableName)
   tableReq := &nosqldb.TableRequest{
		Statement: stmt,
   }
   tableRes, err := client.DoTableRequest(tableReq)
   if err != nil {
      fmt.Printf("cannot initiate CREATE INDEX request: %v\n", err)
      return
   }
   // The create index request is asynchronous, wait for index creation to complete.
   _, err = tableRes.WaitForCompletion(client, 60*time.Second, time.Second)
   if err != nil {
      fmt.Printf("Error finishing CREATE INDEX request: %v\n", err)
      return
   }
   fmt.Println("Created Index acct_episodes on table ", tableName)
   return
}

可以使用 tableDDL 方法对表创建索引。此方法为异步方法,返回 Promise TableResultTableResult 是封装表状态的普通 JavaScript 对象。有关方法详细信息,请参见 NoSQLClient 类。

Download the full JavaScript code Indexes.js from the examples here and the full TypeScript code Indexes.ts from the examples here.

//creates an index
async function createIndex(handle) {
   const crtindDDL = `CREATE INDEX acct_episodes ON ${TABLE_NAME}(acct_data.contentStreamed[].seriesInfo[].episodes[]  AS ANYATOMIC)`;
   let res =  await handle.tableDDL(crtindDDL);
   console.log('Index acct_episodes is created on table:' + TABLE_NAME);
}

要对表创建索引,请使用方法 ExecuteTableDDLAsyncExecuteTableDDLWithCompletionAsync。这两种方法均返回 Task<TableResult>TableResult 实例包含 DDL 操作的状态,例如 TableState 和表方案。有关这些方法的更多详细信息,请参阅 Oracle NoSQL Dotnet SDK API Reference

Download the full code Indexes.cs from the examples here.
// Creates an index on a table
private static async Task createIndex(NoSQLClient client){
   var sql =
      $@"CREATE INDEX acct_episodes ON {TableName}(acct_data.contentStreamed[].seriesInfo[].episodes[]  AS ANYATOMIC)";
   var tableResult = await client.ExecuteTableDDLAsync(sql);
   // Wait for the operation completion
   await tableResult.WaitForCompletionAsync();
   Console.WriteLine(" Index acct_episodes is created on table Table {0}",
                tableResult.TableName);
}