使用 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 原子数据类型之一。为此,您可以使用 JSON 字段中每个索引路径旁边的 AS 关键字声明数据类型。

示例 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 表中 info 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)

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

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

使用 NO NULLS 子句创建索引

可以使用可选的 WITH NO NULLS 子句创建索引。在这种情况下,索引字段上具有 NULL 和/或 EMPTY 值的行将不会被索引。

CREATE INDEX nonull_phone ON baggageInfo (contactPhone) WITH NO NULLS

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

可以创建每个行属性具有唯一键的索引。

CREATE INDEX idx_showid ON
stream_acct(acct_data.contentStreamed[].showId AS INTEGER)
WITH UNIQUE KEYS PER ROW

在上面的查询中,索引是在 showId 上创建的,单个 contentStreamed 数组不能有重复的 showId。这会通知查询处理器,对于任何流处理用户,contentStreamed 数组不能包含具有相同显示 ID 的两个或更多显示。限制是必要的,因为如果存在重复的 show 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 类用于在表上创建索引。此请求指定的操作的执行是异步的。这些操作可能是长时间运行的操作。TableResult 是从 TableRequest 操作返回的,它将封装表的状态。有关 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')

TableRequest 类用于在表上创建索引。TableRequest 指定的操作的执行是异步的。这些操作可能是长时间运行的操作。此请求用作 Client.DoTableRequest() 操作的输入,该操作将返回可用于轮询的 TableResult,直到表达到所需状态。有关 TableRequest 类的各种方法的更多详细信息,请参阅 Oracle NoSQL Go SDK API Reference

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 of 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);
}