查询状态数据库

本主题包含的信息可帮助您了解如何查询存储区块链分类账当前状态数据的状态数据库。

什么是国家数据库?

区块链分类账的当前状态数据存储在状态数据库中。

开发 Oracle Blockchain Platform 链代码时,您可以通过执行富查询从状态数据库中提取数据。Oracle Blockchain Platform 使用 SQL 富查询语法和 CouchDB 查找表达式支持富查询。请参阅 SQL Rich Query SyntaxCouchDB Rich Query Syntax

Hyperledger Fabric 不支持 SQL 丰富的查询。如果您的 Oracle Blockchain Platform 网络包含 Hyperledger Fabric 参与者,则您需要确保执行以下操作:

  • 如果链代码包含 SQL 丰富的查询语法,则这些链代码仅安装在使用 Oracle Blockchain Platform 的成员对等节点上。

  • 如果需要在 Oracle Blockchain Platform 和 Hyperledger Fabric 对等节点上安装链代码,请在链代码中使用 CouchDB 语法,并确认 Hyperledger Fabric 对等节点已设置为使用 CouchDB 作为其状态数据库系统信息库。Oracle Blockchain Platform 可以处理 CouchDB。

Oracle Blockchain Platform 如何与 Berkeley DB 协同工作?

Oracle Blockchain Platform 使用 Oracle Berkeley DB 作为状态数据库。Oracle Blockchain Platform 基于 SQLite 扩展在 Berkeley DB 中创建关系表。此体系结构提供了一种强大的高性能方法来验证 SQL 丰富的查询。

对于每个渠道链代码,Oracle Blockchain Platform 会创建一个 Berkeley DB 表。此表存储状态信息数据,并至少包含一个名为 key 的键列和一个名为 valuevalueJson 的值列,具体取决于您是否使用 JSON 格式数据。

Column Name(列名) 类型 说明
key TEXT 状态表的关键字列。
value TEXT 状态表中的值列。
valueJson TEXT 状态表的 JSON 格式值列。

请注意,valueJsonvalue 列是互斥的。因此,如果链代码为键分配 JSON 值,则 valueJson 列将存储该值,并且值列将设置为 null。如果链代码为键分配非 JSON 值,则 valueJson 列将设置为 null,并且值列将保存该值。

状态数据库的示例

以下是汽车经销商示例状态数据库中键及其值的示例:

value valueJson
abg1234 null {"docType":"vehiclePart","serialNumber":"abg1234",“汇编程序”:"panama-parts","assemblyDate":1502688979,"name":"airbag 2020","owner":"Detroit Auto","recall":false,"recallDate":1502688979}
abg1235 null {"docType":"vehiclePart","serialNumber":"abg1235",“汇编程序”:"panama-parts","assemblyDate":1502688979,"name":"airbag 4050","owner":"Detroit Auto","recall":false,"recallDate":1502688979}
ser1236 null {"docType":"vehiclePart","serialNumber":"ser1236",“汇编程序”:"panama-parts","assemblyDate":1502688979,"name":"seatbelt 10020","owner":"Detroit Auto","recall":false,"recallDate":1502688979}
bra1238 null {"docType":"vehiclePart","serialNumber":"bra1238",“汇编程序”:"bobs-bits","assemblyDate":1502688979,"name":"brakepad 4200","owner":"Detroit Auto","recall":false,"recallDate":150268979}
dtrt10001 null {"docType":"vehicle", "chassisNumber": "dtrt10001", "Manufacturer": "Detroit Auto", "model": "a coupe", "assemblyDate": 1502688979, "airbagSerialNumber": "abg1235", "owner": "Sam Dealer", "recall": false, "recallDate": 1502688979

控制台中的丰富查询

管理员可以从控制台运行和分析丰富的查询。

  1. 转到控制台并选择渠道选项卡。
  2. 在“渠道”表中,找到要运行查询的渠道,单击渠道 More Actions(更多操作)按钮,然后单击 Analyze Rich Queries(分析富查询)。此时将显示 Analyze Rich Queries 对话框。
  3. 要对状态数据库运行富查询,请选择查询执行
    1. 对于链代码,请选择部署到要查询的渠道的链代码。
    2. 对于对等点,选择要查询的对等点。
      只有当前组织中运行所选链代码的对等节点可用。
    3. 对于富查询,输入要运行和分析的丰富查询。
      查询格式必须遵循丰富的查询语法。有关富查询语法的更多信息,请参见 Supported Rich Query Syntax
    4. 对于结果行限制,请将滑块移动到要提取的最大结果行数。最多可以提取 50 行结果。
  4. 要获取查询的执行计划,请选择查询计划说明。查询执行计划是为运行查询而执行的操作序列。
    1. 对于链代码,请选择部署到要查询的渠道的链代码。
    2. 对于对等点,选择要查询的对等点。
    3. 对于集合,选择状态数据库或专用数据集合。
    4. 对于富查询,输入富查询。
      此查询不需要 explain 关键字。
      例如:select * from <state>
  5. 单击执行结果字段显示查询结果表或执行计划。要将结果表导出为 .csv 文件,请单击导出
    结果表大小限制为 1 MB。您可能需要细化查询以避免超出此限制。

支持的富查询语法

Oracle Blockchain Platform 支持两种类型的丰富查询语法,可用于查询状态数据库:SQL 丰富查询和 CouchDB 丰富查询。

SQL 丰富查询语法

Berkeley DB JSON 扩展采用 SQL 函数的形式。

使用须知

请注意以下信息:

  • 您只能访问正在执行查询的渠道链代码 (<STATE>)。
  • 仅支持 SELECT 语句。
  • 您无法修改状态数据库表。
  • 多信息查询表达式只能有一个 SELECT 语句。
  • 本主题中的示例只是您可以编写丰富查询的几种方式。您可以访问通常的完整 SQL 语法来查询 SQL 数据库。
  • 您可以访问 JSON1 扩展(SQLite 扩展)。请参阅 JSON1 扩展SQL As Understood by SQLite

如果您需要有关编写和测试链代码的更多信息,请参见 Develop Chaincodes

如何在查询中引用状态数据库

状态数据库表名由 Oracle Blockchain Platform 在内部管理,因此在编写链代码时,您无需知道状态数据库的物理名称。

而是必须使用 <STATE> 别名来引用表名。例如:select key, value from <STATE>

请注意,<STATE> 别名区分大小写,因此您可以使用 <state><STATE> 或类似 <StAtE> 的内容。

检索所有密钥

使用以下语法:

SELECT key FROM <STATE>

例如,如果您使用此语法查询“汽车经销商”示例,则会得到以下键列表:

abg1234

abg1235

ser1236

bra1238

dtrt10001

检索按键按字母顺序排序的所有键和值

使用以下语法:

SELECT key AS serialNumber, valueJson AS details FROM  <state> ORDER BY key

例如,如果使用此语法查询“汽车经销商”示例,则会得到以下结果:

serialNumber 详细信息
abg1234 {"docType":"vehiclePart","serialNumber":"abg1234",“汇编程序”:"panama-parts","assemblyDate":1502688979,"name":"airbag 2020","owner":"Detroit Auto","recall":false,"recallDate":1502688979}
abg1235 {"docType":"vehiclePart","serialNumber":"abg1235",“汇编程序”:"panama-parts","assemblyDate":1502688979,"name":"airbag 4050","owner":"Detroit Auto","recall":false,"recallDate":1502688979}
bra1238 {"docType":"vehiclePart","serialNumber":"bra1238",“汇编程序”:"bobs-bits","assemblyDate":1502688979,"name":"brakepad 4200","owner":"Detroit Auto","recall":false,"recallDate":150268979}
dtrt10001 {"docType":"vehicle", "chassisNumber": "dtrt10001", "Manufacturer": "Detroit Auto", "model": "a coupe", "assemblyDate": 1502688979, "airbagSerialNumber": "abg1235", "owner": "Sam Dealer", "recall": false, "recallDate": 1502688979
ser1236 {"docType":"vehiclePart","serialNumber":"ser1236",“汇编程序”:"panama-parts","assemblyDate":1502688979,"name":"seatbelt 10020","owner":"Detroit Auto","recall":false,"recallDate":1502688979}

检索以“abg”开头的所有键和值

使用以下语法:

SELECT key AS serialNumber, valueJson AS details FROM <state> WHERE key LIKE 'abg%'SELECT key, value FROM <STATE>

例如,如果使用此语法查询“汽车经销商”示例,则会得到以下结果:

serialNumber 详细信息
abg1234 {"docType":"vehiclePart","serialNumber":"abg1234",“汇编程序”:"panama-parts","assemblyDate":"1502688979","name":"airbag 2020","owner":"Detroit Auto","recall":"false","recallDate":"1502688979"}
abg1235 {"docType":"vehiclePart","serialNumber":"abg1235",“汇编程序”:"panama-parts","assemblyDate":"1502688979","name":"airbag 4050","owner":"Detroit Auto","recall":"false","recallDate":"1502688979"}

检索包含由“底特律汽车”拥有的车辆部件的值的所有关键字

使用以下语法:

SELECT key FROM <state> WHERE json_extract(valueJson, '$.docType') = 'vehiclePart' AND json_extract(valueJson, '$.owner') = 'Detroit Auto'

例如,如果您使用此语法查询“汽车经销商”示例,则会得到以下键列表:

abg1234

abg1235

ser1236

bra1238

检索“Sam 经销商”拥有的所有汽车的型号和制造商

使用以下语法:

SELECT json_extract(valueJson, '$.model') AS model, json_extract(valueJson, '$.manufacturer') AS manufacturer FROM <state> WHERE json_extract(valueJson, '$.docType') = 'vehicle' AND json_extract(valueJson, '$.owner') = 'Sam Dealer'

例如,如果使用此语法查询“汽车经销商”示例,则会得到以下结果:

模型 生产厂家
轿车 底特律自动

如果状态值为 JSON 数组,则可以使用此语法检索“Sam Dealer”拥有的所有汽车的型号和制造商:

SELECT json_extract(j.value, '$.model') AS model, json_extract(j.value, '$.manufacturer') AS manufacturer FROM <state> s, json_each(json_extract(s.valueJson,'$')) j WHERE json_valid(j.value) AND json_extract(j.value, '$.owner') = 'Sam Dealer'

CouchDB 富查询语法

如果您要将包含 CouchDB 语法的链代码迁移到 Oracle Blockchain Platform ,或者需要编写链代码以安装在参与 Oracle Blockchain Platform 网络的超级账本架构对等节点上,请使用本主题中的信息。

如果您正在编写新的链代码,Oracle 建议您使用 SQL 丰富的查询来利用 Oracle Blockchain Platform 和 Berkeley DB 提供的性能优势。

如果您需要有关编写和测试链代码的更多信息,请参见 Develop Chaincodes

不支持的查询参数和选择器语法

Oracle Blockchain Platform 不支持 use_index 参数。如果使用,Oracle Blockchain Platform 将忽略此参数,它将自动选取在相关 StateDB 上定义的索引。

Parameter (参数) 类型 说明
use_index json 指示查询使用特定索引。

检索所有车型、制造商和车主,并按车主排序

使用此表达式:

{ 
  "fields": ["model", "manufacturer", "owner"], 
  "sort": [   
    "owner" 
   ]
}

检索“Sam 经销商”拥有的所有汽车的型号和制造商

使用此表达式:

{ 
  "fields": ["model", "manufacturer"], 
  "selector": {   
    "docType"  : "vehicle",
     "owner" : "Sam Dealer" 
  }
}

状态数据库索引

状态数据库可以包含大量数据。在这种情况下,Oracle Blockchain Platform 使用索引来改进数据访问。

默认指数

部署链代码时,Oracle Blockchain Platform 会创建两个索引。

  • 键索引:在键列上创建。

  • 值索引:在值列上创建。

定制索引

在某些情况下,您可能需要创建定制索引。可以使用可在状态表的上下文中解析的任何表达式来定义这些索引。针对 Berkeley DB 创建的定制索引依赖于 SQLite 语法,但是它们遵循 Hyperledger Fabric 提供的相同 CouchDB 实现。

可以使用定制索引显著提高 WHERE 语句和 ORDER BY 语句对大型数据集的性能。由于使用定制索引会减慢数据插入的速度,因此请谨慎使用它们。

每个定制索引都定义为一个表达式数组,该数组支持复合索引,在一个文件中表示为 JSON 文档。每个文件有一个索引。必须在以下目录结构中将此文件与名为 indexes 的文件夹中的链代码打包:statedb/relationaldb/indexes。有关更多信息,请参见 How to add CouchDB indexes during chaincode installation

定制索引示例

本节中的自定义索引示例使用汽车经销商示例。

示例 1 :此示例为 WHERE 和 ORDER BY 表达式的上下文中使用 json_extract 表达式编制索引。

{"indexExpressions": ["json_extract(valueJson, '$.owner')"]}

例如:

SELECT … FROM … ORDER BY json_extract(valueJson, '$.owner')

示例 2 :此示例为 WHERE 和 ORDER BY 表达式上下文中两个 json_extract 表达式的复合用法编制索引。

{"indexExpressions": ["json_extract(valueJson, '$.docType')", "json_extract(valueJson, '$.owner')"]}

例如:

SELECT … FROM … WHERE json_extract(valueJson, '$.docType') = 'vehiclePart' AND json_extract(valueJson, '$.owner') = 'Detroit Auto'

示例 3 :此示例将创建示例 1 中描述的索引和示例 2 中描述的索引。每个 JSON 结构必须包含在单独的文件中。每个文件都描述一个索引:一个简单的索引(如示例 1)或一个复合索引(如示例 2)。

索引 1:{"indexExpressions": ["json_extract(valueJson, '$.owner')"]}

索引 2:{"indexExpressions": ["json_extract(valueJson, '$owner')", "json_extract(valueJson, '$.docType')"]}

在以下示例中,索引 2 应用于查询的 WHERE 部分中的 AND 表达式,而索引 1 应用于 ORDER BY 表达式:

SELECT … FROM … WHERE json_extract(valueJson, '$.docType') = 'vehiclePart' AND json_extract(valueJson, '$.owner') = 'Detroit Auto' ORDER BY json_extract(valueJson, '$.owner')

JSON 文档格式

JSON 文档必须采用以下格式:

{"indexExpressions": [expr1, ..., exprN]}

例如:

{"indexExpressions": ["json_extract(valueJson, '$.owner')"]}

富查询验证中的差异

在某些情况下,具有 CouchDB 丰富查询的标准超级账本架构和 Oracle Berkeley DB 丰富查询的行为有所不同。

在带有 CouchDB 的标准 Hyperledger Fabric 中,查询返回的每个键和值对将添加到事务的读取集,并在验证时进行验证,而无需重新执行查询。在 Berkeley DB 中,返回的键和值对不会添加到读取集,但是富查询的结果在 Merkle 树中进行散列,并在验证时针对查询的重新执行进行验证。

原生超级账本架构无法为丰富的查询提供数据保护。但是,Berkeley DB 包含通过将 Merkle 树散列值添加到读取集、重新执行富查询以及在验证阶段重新计算 Merkle 树值来保护和验证富查询的功能。请注意,由于 Oracle Blockchain Platform 与 Berkeley DB 的验证更加准确,因此有时会标记链代码调用以更频繁地进行虚拟读取。