基于 SQL 视图创建图表

了解如何使用 SQL 视图 创建图表。本文档提供了有关定义视图以及使用视图定义节点表和边表的逐步说明和代码示例。探索 示例,其中包含示例代码,演示了使用视图创建图表的用例 。如需详细了解如何使用视图创建属性图表(包括优势和注意事项),请参阅使用 SQL 视图创建的图表概览

准备工作

如需创建图表,您必须:

  1. 确保已设置 Spanner Graph 环境

  2. 熟悉 Spanner Graph 架构的工作原理

使用视图创建图表

如需使用视图创建图表,请执行以下操作:

  1. 为图表定义视图。默认情况下,您的视图必须遵循其中一种 受支持的视图模式 ,以确保元素的唯一性。如果您的视图使用的查询不遵循这些模式,您可以在第 4 步中停用键唯一性验证。如需了解 详情,请参阅创建视图

  2. CREATE PROPERTY GRAPH 语句的 NODE TABLESEDGE TABLES 子句中使用视图来创建图表。

  3. CREATE PROPERTY GRAPH 语句中添加 KEY 子句。KEY 子句用于指定源视图中的列,这些列可唯一标识每个图表元素。

  4. 可选:如果您的视图使用不遵循受支持的唯一性模式的任意 SQL 查询,请将 OPTIONS (validate_element_key_uniqueness = false) 子句添加到 CREATE PROPERTY GRAPH 语句中。确保 KEY 子句列为每个节点或边行生成唯一值:

    CREATE PROPERTY GRAPH MyGraph
      NODE TABLES (
        CustomerViewTrusted KEY(id)
      ) OPTIONS (validate_element_key_uniqueness = false);
    

    如需了解详情,请参阅 停用键验证(任何 SQL 查询)

示例:使用视图创建图表

此示例在 CustomerAccount 表上创建以下视图: AsiaCustomerAsiaBankAccountAsiaAccountsOwnership。然后,该示例使用这些视图在图表中创建以下内容:

  • 使用 AsiaCustomer 视图创建 Customer 节点表。

  • 使用 AsiaBankAccount 视图创建 Account 节点表。

  • 使用 AsiaAccountsOwnership 视图创建 Owns 边表。此边将 Customer 节点与 Account 节点连接起来。

第 1 步:创建表

首先,创建数据表。以下代码会创建 CustomerAccount 表。

CREATE TABLE Customer (
  customer_id INT64 NOT NULL,
  name STRING(MAX),
  address_continent STRING(MAX),
  address_country STRING(MAX),
) PRIMARY KEY(customer_id);

CREATE TABLE Account (
  account_id INT64 NOT NULL,
  customer_id INT64 NOT NULL,
  account_type STRING(MAX),
  balance INT64,
  create_time TIMESTAMP,
  address_continent STRING(MAX),
  address_country STRING(MAX),
  CONSTRAINT FK_CustomerId FOREIGN KEY (customer_id)
    REFERENCES Customer (customer_id)
) PRIMARY KEY(account_id);

第 2 步:创建视图

接下来,创建视图以转换或过滤表中的数据。这些视图会过滤表,以仅包含亚洲的客户和账号。除非将 validate_element_key_uniqueness设置为false,否则用于创建图表 元素的视图必须 遵循受支持的模式,以确保视图中的行是唯一的

-- View for 'Customer' nodes, filtered for Asia
CREATE VIEW AsiaCustomer
  SQL SECURITY INVOKER AS
    SELECT customer.customer_id, customer.name
    FROM Customer customer
    WHERE LOWER(customer.address_continent) = "asia";

-- View for 'Account' nodes, filtered for Asia.
CREATE VIEW AsiaBankAccount
  SQL SECURITY INVOKER AS
    SELECT account.account_id, account.balance, account.account_type, account.create_time
    FROM Account account
    WHERE LOWER(account.address_continent) = "asia";

-- View for 'Owns' edges, connecting customers to accounts in Asia.
CREATE VIEW AsiaAccountsOwnership
  SQL SECURITY INVOKER AS
    SELECT account.customer_id, account.account_id
    FROM Account account
    WHERE LOWER(account.address_continent) = "asia";

第 3 步:创建属性图表

现在,使用您创建的视图创建 AsiaFinGraphCREATE PROPERTY GRAPH 语句包含每个图表元素定义的 KEY 子句,用于指定可唯一标识图表元素的列。

CREATE PROPERTY GRAPH AsiaFinGraph
  NODE TABLES (
    AsiaCustomer AS Customer KEY(customer_id),
    AsiaBankAccount AS Account KEY(account_id)
  )
  EDGE TABLES (
    AsiaAccountsOwnership AS Owns
      KEY(customer_id, account_id)
      SOURCE KEY (customer_id) REFERENCES Customer (customer_id)
      DESTINATION KEY (account_id) REFERENCES Account (account_id)
  );

用例示例

与使用表作为属性图表元素相比,SQL 视图具有优势。以下示例演示了使用视图(而不是表)定义图表元素的几个用例。

示例:强制执行精细的图表数据访问权限控制

如需对图表数据强制执行行级安全性,请使用 定义者的权限视图定义节点表或边表 。该视图向图表公开了底层数据的允许子集

例如,如需将图表访问权限限制为仅供工程成本中心的员工访问,您可以创建 EngineerEmployeeView 视图,并使用 GRANT 子句向 engineering_data_reader 角色授予对该视图的 SELECT 权限。

当您使用此视图定义图表节点表时,以 engineering_data_reader 角色运行图表查询的用户只能看到视图过滤的行,其中包括工程员工。

-- The table containing all employee data.
CREATE TABLE Employee (
  id INT64 NOT NULL,
  cost_center STRING(MAX),
  job_title STRING(MAX),
  office STRING(MAX)
) PRIMARY KEY (id);

-- The definer's rights view that filters for engineering employees.
CREATE VIEW EngineerEmployeeView SQL SECURITY DEFINER AS
  SELECT e.id, e.cost_center, e.job_title, e.office
  FROM Employee e
  WHERE LOWER(e.cost_center) = "engineering";

-- The role that is granted to read the view.
CREATE ROLE engineering_data_reader;
GRANT SELECT ON VIEW EngineerEmployeeView TO ROLE engineering_data_reader;

-- The graph that uses definer's rights view.
CREATE PROPERTY GRAPH EngineeringGraph
  NODE TABLES (
    EngineerEmployeeView KEY(id)
  );

示例:对派生的图表元素进行建模

您可以使用视图来定义需要进行数据转换的图表元素。一个主要优势是,视图定义了转换,因此您无需为派生数据维护单独的表。

例如,您可以从 ARRAY 列(或 JSON 列中的数组字段)UNNEST 数据,以对单行中的多个边关系进行建模。

在以下供应链架构示例中,Parts 表会在 dependent_parts 数组中存储子组件列表。视图可以使用 UNNEST 运算符将该数组的每个元素转换为不同的行。然后,此视图可以用作边表,让您对 PartDependsOnPart 边进行建模,以表示部件之间的依赖关系。

-- Parts table with an ARRAY of dependent parts.
CREATE TABLE Parts (
  part_id INT64 NOT NULL,
  dependent_parts ARRAY<INT64>
) PRIMARY KEY (part_id);

-- A view that unnests the dependent_parts array.
-- GROUP BY ensures uniqueness for the graph element KEY.
CREATE VIEW PartDependsOnPart SQL SECURITY INVOKER AS
  SELECT p.part_id, dependent_part_id
  FROM Parts AS p,
    UNNEST(p.dependent_parts) AS dependent_part_id
  GROUP BY p.part_id, dependent_part_id;

-- Graph modeling the part dependency relationship.
CREATE PROPERTY GRAPH SupplyChainGraph
  NODE TABLES (
    Parts
  )
  EDGE TABLES (
    PartDependsOnPart KEY (part_id, dependent_part_id)
      SOURCE KEY (part_id) REFERENCES Parts(part_id)
      DESTINATION KEY (dependent_part_id) REFERENCES Parts(part_id)
  );

示例:无架构数据转换

无架构数据管理让 您创建灵活的图表定义,而无需预定义节点和边类型。 虽然无架构数据管理提供了灵活性,但随着数据的定义越来越明确,您可能需要转换为更正式的结构。更正式的结构会在架构中公开图表的节点和边关系、标签和属性,从而减少了手动探索数据以了解图表架构的需求。

您可以使用视图来规范节点和边类型,而无需迁移底层数据。例如,您可以从使用规范 GraphNodeGraphEdge 表的典型无架构模型进行转换。为此,您可以创建从无架构表中提取数据的视图:

  1. 为您要规范的每个节点和边类型(例如 PersonWorksFor)定义一个视图。在该视图中,按标签过滤数据(例如 WHERE n_label = "person"),并将属性从 JSON 列转换为特定数据类型(例如STRING(prop.name) AS name)。

  2. 定义新的属性图表,其中 NODE TABLESEDGE TABLES 引用您刚刚创建的类型化视图。

对于某些查询(例如,具有多种边类型的量化路径模式),无架构图表的性能优于正式图表。如果正式元数据对您的用例很重要,那么您可以使用视图从无架构图表转换为类型化架构。您还可以选择对某些用例使用无架构图表,而对其他用例使用类型化架构图表。如需了解详情,请参阅 根据图表查询选择架构设计

以下示例演示了从无架构图表转换为正式图表的工作流,共分为四个步骤:

  1. 为无架构模型定义规范 GraphNodeGraphEdge 表。

  2. 在这些无架构表上创建初始的灵活图表。

  3. 定义类型化视图(PersonCompanyWorksFor),这些视图从无架构表中提取数据并将其规范化。

  4. 创建最终的强类型图表,该图表使用这些视图作为其节点表和边表。

-- 1. Create the canonical tables for a schemaless model.
CREATE TABLE GraphNode (
  id INT64 NOT NULL,
  label STRING(MAX) NOT NULL,
  properties JSON
) PRIMARY KEY (id);

CREATE TABLE GraphEdge (
  id INT64 NOT NULL,
  dest_id INT64 NOT NULL,
  edge_id INT64 NOT NULL,
  label STRING(MAX) NOT NULL,
  properties JSON
) PRIMARY KEY (id, dest_id, edge_id),
  INTERLEAVE IN PARENT GraphNode;

-- 2. Define a schemaless graph.
CREATE PROPERTY GRAPH FinGraph
  NODE TABLES (
    GraphNode
      DYNAMIC LABEL (label)
      DYNAMIC PROPERTIES (properties)
  )
  EDGE TABLES (
    GraphEdge
      SOURCE KEY (id) REFERENCES GraphNode(id)
      DESTINATION KEY (dest_id) REFERENCES GraphNode(id)
      DYNAMIC LABEL (label)
      DYNAMIC PROPERTIES (properties)
  );

-- 3. Define typed views that extract and formalize the data.
--    Convert JSON fields to primitive types (for example, INT64, STRING) to
--    ensure type safety.
CREATE VIEW Person SQL SECURITY INVOKER AS
  SELECT n.id, STRING(n.properties.name) AS name, INT64(n.properties.age) AS age
  FROM GraphNode n WHERE n.label = "person";

CREATE VIEW Company SQL SECURITY INVOKER AS
  SELECT n.id, STRING(n.properties.name) AS company_name, BOOL(n.properties.is_public) AS is_public
  FROM GraphNode n WHERE n.label = "company";

CREATE VIEW WorksFor SQL SECURITY INVOKER AS
  SELECT e.id AS person_id, e.dest_id AS company_id, e.edge_id AS edge_id, STRING(e.properties.since) AS since
  FROM GraphEdge e
  WHERE e.label = "worksfor";

-- 4. Create the final, formalized graph from the typed views.
CREATE PROPERTY GRAPH typed_formalized_graph
  NODE TABLES (
    Person KEY(id)
      PROPERTIES (name, age),
    Company KEY(id)
      PROPERTIES (company_name, is_public)
  )
  EDGE TABLES(
    WorksFor KEY(person_id, company_id, edge_id)
      SOURCE KEY (person_id) REFERENCES Person(id)
      DESTINATION KEY (company_id) REFERENCES Company(id)
      PROPERTIES (since)
  );

后续步骤