基于 SQL 视图创建的图表概览

通过本文档,您可以了解使用 SQL 视图创建图表的优势。本文档介绍了使用视图创建图表的优势、要求和注意事项,可帮助您决定是应使用表还是视图来创建图表。

如需详细了解如何通过视图创建图,请参阅通过 SQL 视图创建属性图。

使用视图(而非表)创建图表的优势

SQL 视图是由 SQL 查询定义的虚拟表。在 Spanner 中,每次执行引用视图的查询时,都会执行定义视图的查询。Spanner 视图不是物化视图,因为它们不会将定义视图的查询结果存储为数据存储区中的实际表。如需了解详情,请参阅视图概览。您可以基于 SQL 视图创建图表元素,但无法创建查询图表的视图。

作为表和图表架构之间的抽象层,视图具有多种优势,而这些优势在使用表创建图表时无法获得。

使用视图创建图表的要求

使用视图创建图表元素时,您必须遵循以下要求:

指定图表元素时使用 KEY 子句

使用视图创建节点或边元素时,您必须明确定义用于唯一标识图表元素的列。为此,请在节点或边缘元素定义中使用 KEY 子句。如需了解如何在创建图表元素时使用 KEY 子句,请参阅本文档和通过 SQL 视图创建 Spanner Graph 中的代码示例。

确保节点和边缘键的唯一性

属性图中的每个节点和边都必须具有唯一的键。使用视图定义图表元素时,您可以选择如何验证元素键的唯一性:

  • 严格键验证(默认):Spanner 会验证视图是否遵循支持的查询句式,以确保键的唯一性。
  • 停用键验证(任何 SQL 查询):如果您设置了 validate_element_key_uniqueness = false 图表选项,则可以使用任何 SQL 查询。在此模式下,您需要负责确保元素键是唯一的。

严格密钥验证(默认)

默认情况下,Spanner 会验证定义节点表或边缘表的视图是否遵循以下模式之一,以确保每个节点或边缘都是唯一的:

  • 模式 1:视图使用单个表的主键。

  • 模式 2:视图使用 GROUP BY 或 SELECT DISTINCT 子句。

您可以将其他 SQL 运算符(例如 WHERE、HAVING、ORDER BY、LIMIT 和 TABLESAMPLE)与这些模式结合使用。这些运算符会过滤或排序结果,但不会更改模式提供的底层唯一性保证。

模式 1:使用单个表的主键

在此模式中,视图从单个表中进行选择,并且图定义中的 KEY 子句与基表的主键列匹配。因此,视图生成的每个节点或边缘行都是唯一的。

例如,以下代码从 Account 表中选择一部分行。 图表 KEY(account_id) 与 Account 表的主键匹配,这可确保视图生成的每一行都是唯一的。

-- Table has PRIMARY KEY(account_id).
CREATE TABLE Account (
  account_id INT64 NOT NULL,
  customer_id INT64 NOT NULL,
  account_type STRING(MAX),
  balance INT64
) PRIMARY KEY(account_id);

-- Pattern 1: View uses the primary key from a single table.
CREATE VIEW SavingAccount
  SQL SECURITY INVOKER AS
    SELECT accnt.account_id, accnt.customer_id, accnt.balance
    FROM Account accnt
    WHERE accnt.account_type = 'saving';

CREATE PROPERTY GRAPH SavingAccountGraph
  NODE TABLES (
    -- The element KEY(account_id) matches the table's primary key.
    SavingAccount KEY(account_id)
  );

模式 2:使用 GROUP BY 或 SELECT DISTINCT 子句

在此模式中,视图的查询使用 GROUP BY 或 SELECT DISTINCT 子句。KEY 子句中的列必须与这些子句用于定义唯一性的列匹配:

  • 对于 GROUP BY:KEY 子句列必须与 GROUP BY 子句中的所有列匹配。

  • 对于 SELECT DISTINCT:KEY 子句列必须与 SELECT DISTINCT 列表中的列匹配。

GROUP BY 示例:

CREATE TABLE Customer (
  customer_id INT64,
  name STRING(MAX)
) PRIMARY KEY (customer_id);

CREATE TABLE SaleOrder (
  order_id INT64,
  customer_id INT64,
  amount INT64
) PRIMARY KEY (order_id);

CREATE VIEW CustomerOrder
  SQL SECURITY INVOKER AS
    SELECT
      s.order_id,
      ANY_VALUE(c.customer_id) AS customer_id,
      ANY_VALUE(c.name) AS customer_name
    FROM Customer c JOIN SaleOrder s ON c.customer_id = s.customer_id
    GROUP BY s.order_id;

CREATE PROPERTY GRAPH OrderGraph
  NODE TABLES (
    -- The KEY(order_id) matches the GROUP BY column in view definition.
    CustomerOrder KEY(order_id)
  );

SELECT DISTINCT 示例:

CREATE TABLE SaleOrder (
  order_id INT64,
  customer_id INT64,
  amount INT64
) PRIMARY KEY (order_id);

CREATE VIEW KeyCustomer SQL SECURITY INVOKER AS
  SELECT DISTINCT s.customer_id, s.amount
  FROM SaleOrder s
  WHERE s.amount > 1000;

CREATE PROPERTY GRAPH KeyCustomersGraph
  NODE TABLES (
    -- The KEY(customer_id, amount) matches the DISTINCT columns.
    KeyCustomer KEY(customer_id, amount)
  );

已停用键验证(任何 SQL 查询)

如果您的视图使用的查询不遵循模式 1 或模式 2(例如,在没有 GROUP BY 或 SELECT DISTINCT 子句的情况下联接表的查询),您可以通过设置图表选项 validate_element_key_uniqueness = false 来停用元素键验证。

默认情况下,validate_element_key_uniqueness 为 true。设置为 false 时,Spanner 不会在创建架构时验证视图的查询定义。您可以在视图中使用任何 SQL 查询,但必须确保 KEY 子句中指定的列对于每个节点或边缘行都包含唯一值。

以下示例定义了一个名为 CustomerOrderTrusted 的视图,该视图在没有 GROUP BY 子句的情况下联接 Customer 和 SaleOrder 表。由于每个销售订单都是唯一的,因此将 SaleOrder 与 Customer 联接可保留视图结果中 order_id 的唯一性。但是,由于查询使用 JOIN 且不遵循模式 1 或模式 2,因此 Spanner 无法在语法上验证唯一性。

通过在 OPTIONS 子句中指定 KEY(order_id) 并设置 validate_element_key_uniqueness = false,您可以定义属性图,而无需修改视图查询:

-- View uses a JOIN without GROUP BY or DISTINCT.
CREATE VIEW CustomerOrderTrusted
  SQL SECURITY INVOKER AS
    SELECT
      s.order_id,
      c.customer_id,
      c.name AS customer_name
    FROM Customer c JOIN SaleOrder s ON c.customer_id = s.customer_id;

-- Property graph disables element key uniqueness validation.
CREATE PROPERTY GRAPH OrderGraphTrusted
  NODE TABLES (
    CustomerOrderTrusted
      KEY(order_id)
      LABEL CustomerOrder PROPERTIES(
        order_id,
        customer_id,
        customer_name)
  ) OPTIONS (validate_element_key_uniqueness = false);

在视图使用远程函数时启用易失列

您可以在调用远程函数 (LANGUAGE REMOTE) 的视图上定义属性图,方法是在 CREATE PROPERTY GRAPH 语句的 OPTIONS 子句中设置 allow_view_with_volatile_column = true。

默认情况下,Spanner 不允许您在具有易失性列 (allow_view_with_volatile_column = false) 的视图上定义属性图。如果视图列的表达式调用了易失性函数,则该列是易失性的。如果函数的返回值即使在具有相同参数的单个语句执行中也可能发生变化,则该函数是易失性函数,例如远程函数 (NOT DETERMINISTIC)。视图定义正文中的任何易失性(例如在 JOIN 条件或 WHERE 子句中)也会使视图中的所有投影列都具有易失性。如需查看代码示例,请参阅使用远程函数计算属性。

使用视图时的注意事项

当您使用视图定义图表元素时,以下内容可以帮助您设计和实现有效的图表:

属性图查询性能

在执行数据转换(例如 GROUP BY、UNNEST 或 JOIN 操作)的视图上定义图表元素时,请仔细评估用例的查询性能。请注意,每当查询执行元素模式匹配时,Spanner 都会执行视图的查询定义。

图表架构优化

使用视图定义图表元素时,某些图表架构优化的效果可能不如使用表定义图表元素。

投影单个表的主键的视图

如果视图是单个基表的投影,则对该底层表的任何优化都会对图查询保持有效。例如,对基表应用以下方法,可为基于这些视图定义的图元素提供类似的性能优势:

使用 GROUP BY 或 DISTINCT 子句定义的视图

执行聚合(例如 GROUP BY、SELECT DISTINCT 或其他复杂转换)的视图会失去与底层表结构的直接关系。因此,对基表进行架构优化可能无法为对视图执行操作的图查询带来相同的性能优势。当视图执行复杂的聚合时,请仔细评估用例的查询性能。

使用基于视图的图表进行数据修改

视图未具体化,这意味着它们不会将定义视图的查询结果存储为数据存储区中的表,并且它们是只读的。因此,如需在基于视图创建的图表中插入、更新或删除节点或边缘,您需要修改用于创建视图的表中的数据。

图表错误处理,以强制执行数据完整性

使用视图定义图表元素时,请对底层基表强制执行数据完整性(例如,强制执行数据类型)。否则,基表中的数据可能无效,并导致在运行时对基于视图的图表执行的查询失败。

例如,当您从无架构过渡到形式化图时,请使用 CHECK 约束条件来验证基表(GraphNode 和 GraphEdge)中的数据。以下代码在 JSON 属性中应用了这些约束条件,以确保源端的数据完整性并防止运行时查询错误。

-- Enforce that the 'name' property exists for nodes with the 'person' label.
ALTER TABLE GraphNode
ADD CONSTRAINT NameMustExistForPersonConstraint
CHECK (IF(label = 'person', properties.name IS NOT NULL, TRUE));

-- Enforce that the 'name' property is a string for nodes with the 'person' label.
ALTER TABLE GraphNode
ADD CONSTRAINT PersonNameMustBeStringTypeConstraint
CHECK (IF(label = 'person', JSON_TYPE(properties.name) = 'string', TRUE));

后续步骤