基于 SQL 视图创建图表

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

准备工作

如需创建图表,您必须:

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

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

使用视图创建图

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

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

  2. 在 CREATE PROPERTY GRAPH 语句的 NODE TABLES 和 EDGE 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 查询)。

  5. 可选:如果您的视图调用远程函数或具有其他易失性列,请将 OPTIONS (allow_view_with_volatile_column = true) 子句添加到 CREATE PROPERTY GRAPH 语句中:

    CREATE PROPERTY GRAPH MyGraph
      NODE TABLES (
        AccountRemoteUdfView KEY(account_id)
      ) OPTIONS (allow_view_with_volatile_column = true);
    

    如需了解详情,请参阅在视图使用远程函数时启用易失性列。

示例:使用视图创建图表

此示例在 Customer 和 Account 表上创建以下视图:AsiaCustomer、AsiaBankAccount 和 AsiaAccountsOwnership。然后,该示例使用这些视图在图表中创建以下内容:

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

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

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

第 1 步:创建表

首先,创建数据表。以下代码会创建 Customer 和 Account 表。

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 步:创建属性图

现在,使用您创建的视图创建 AsiaFinGraph。CREATE 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)
  );

示例:模型派生的图元素

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

取消数组嵌套以对边进行建模

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

在以下供应链架构示例中,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)
  );

使用远程函数计算属性

您还可以在视图中使用远程函数,通过在 CREATE PROPERTY GRAPH 语句的 OPTIONS 子句中设置 allow_view_with_volatile_column = true 来计算图元素的派生属性。如需了解详情,请参阅在视图使用远程函数时启用易失性列。

-- Define a remote function.
CREATE FUNCTION rudf_identity_func(x INT64)
  RETURNS INT64 NOT DETERMINISTIC LANGUAGE REMOTE
  OPTIONS (
    endpoint = 'https://us-central1-project-id.cloudfunctions.net/my-func'
  );

-- Base table for accounts.
CREATE TABLE Account (
  account_id INT64 NOT NULL,
  balance INT64
) PRIMARY KEY (account_id);

-- View that computes a property using the remote function.
CREATE VIEW AccountRemoteUdfView SQL SECURITY INVOKER AS
  SELECT
    t.account_id,
    t.balance,
    rudf_identity_func(t.account_id) AS view_computed_prop
  FROM Account AS t;

-- Graph using the view requires allow_view_with_volatile_column = true.
CREATE PROPERTY GRAPH AccountViewRemoteUdfGraph
  NODE TABLES (
    AccountRemoteUdfView KEY (account_id)
      PROPERTIES (account_id, balance, view_computed_prop)
  )
  OPTIONS (allow_view_with_volatile_column = true);

示例:无架构数据转换

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

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

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

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

对于某些查询(例如,具有多种边缘类型的量化路径模式),无架构图比形式化图提供更好的性能。如果形式化元数据对您的应用场景很重要,您可以使用视图从无架构图过渡到类型化架构。您还可以选择在某些用例中使用无架构图,在其他用例中使用类型化架构图。如需了解详情,请参阅根据图表查询选择架构设计。

以下示例演示了通过四个步骤从无架构图过渡到形式化图的工作流:

  1. 为无架构模型定义规范的 GraphNode 和 GraphEdge 表。

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

  3. 定义类型化视图(Person、Company、WorksFor),用于从无架构表中提取数据并将其形式化。

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

后续步骤