基于 SQL 视图创建图表

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

准备工作

如需创建图表,您必须执行以下操作:

  1. 确保已设置 Spanner Graph 环境。

  2. 熟悉 Spanner Graph 架构的运作方式

使用视图创建图表

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

  1. 为图表定义视图。请确保您的视图遵循必需的视图模式之一。 如需了解详情,请参阅创建视图

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

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

示例:使用视图创建图表

此示例在 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 步:创建视图

接下来,创建视图以转换或过滤表中的数据。这些视图会过滤表格,使其仅包含亚洲的客户和账号。用于创建图表元素的视图必须确保视图中的行是唯一的

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

后续步骤