边缘约束

适用于: SQL Server 2019 (15.x) 及更高版本 Azure SQL 数据库Azure SQL 托管实例

边缘约束可用于对 SQL Server 图形数据库中的边缘表强制执行数据完整性和特定语义。

边缘约束

默认情况下,边缘表不会为边缘的端点强制执行任何操作。 也就是说,无论何种类型,图形数据库中的边缘都可以将任何节点连接到任何其他节点。

SQL Graph 支持边缘约束,使用户能够向其边缘表添加约束,从而强制执行特定语义,同时保持数据完整性。 将新的边缘添加到具有边缘约束的边缘表时,数据库引擎强制让边缘尝试连接的节点存在于正确的节点表中。 如果有任何边缘引用该节点,则还确保了不会删除该节点。

边缘约束子句

单个边缘约束都由一个或多个边缘约束子句组成。

CONSTRAINT constraint_name CONNECTION (cause1[, clause2...])
  • 边缘约束子句是一对节点表名称,由 TO 关键字分隔。
  • 边缘约束子句中的第一个表名是边缘关系的 FROM 节点表的名称。
  • 边缘约束子句中的第二个表名是边缘关系的 TO 节点表的名称。
  • 因此,表名称对指示边缘关系的方向。
  • 如前文所述,边缘约束可以包含一个或多个边缘约束子句。

多个约束和子句

  • 为同一个边缘表定义的多个边缘约束与 AND 运算符一起使用。
  • 在同一个边缘约束中定义的多个边缘约束子句与 OR 运算符一起使用。

请考虑图形中的 SupplierCustomer 节点。 每个节点可通过单个共享边缘表与 Product 节点关联:boughtbought 边缘表支持 Customer-(bought)->ProductSupplier-(bought)->Product 关系类型。 可以通过使用具有多个边缘约束子句的单个边缘约束来完成此操作。

示例
CONSTRAINT EC_BOUGHT CONNECTION (Customer TO Product)

上面的示例显示了一个边缘约束,其中包含一个边缘约束子句。 此约束支持 Customer-(bought)->Product。 也就是说,将允许插入从 CustomerProductbought 边缘关系。 插入任何其他节点组合(如 Supplier-(bought)->Product),即使它可能描述现实世界中的有效关系,也会失败。

CONSTRAINT EC_BOUGHT CONNECTION (Supplier TO Product, Customer TO Product)

上面的示例定义了一个边缘约束,其中包含两个边缘约束子句。 这些约束子句允许 bought 边缘包含 Supplier-(bought)->ProductCustomer-(bought)->Product 关系。 在 bought 表中插入任何其他类型的边缘关系会失败。

CONSTRAINT EC_BOUGHT1 CONNECTION (Supplier TO Product)
CONSTRAINT EC_BOUGHT2 CONNECTION (Customer TO Product)

上面的示例显示同一边缘表上的两个约束,每个边界约束指定一个约束子句。 在这种情况下,SQL 只允许同时满足这两个边缘约束子句的插入。 这不可能。 没有可满足这两个边缘约束子句的节点对。 此边缘约束组合使边缘表不可用。

有关可在实际应用场景中使用的多个边缘约束的详细说明,请参阅本页后面的“使用新边缘约束子句在现有边缘表上创建新边缘约束”的示例。

边缘约束的索引

创建边缘约束不会自动在边缘表中的 $from_id$to_id 列上创建相应的索引。 如果拥有点查找查询或 OLTP 工作负载,建议在“$from_id$to_id对上手动创建索引。

ON 删除边缘约束上的引用操作

使用边缘约束上的级联操作,用户可以定义当用户删除给定边缘连接到的节点时,数据库引擎所采取的操作。 可以定义以下引用操作:NO ACTION 在尝试删除具有连接边缘的节点时,数据库引擎引发错误。

CASCADE 从数据库中删除某个节点时,会删除连接边缘。

使用边缘约束

可以使用 Transact-SQL 定义 SQL Server 中的边缘约束。 边缘约束只能在图形边缘表上进行定义。 若要创建、删除或修改边缘约束,必须对表拥有 ALTER 权限。

创建边缘约束

下面几个示例展示了如何对新表或现有表创建边缘约束。

在新边缘表上创建边缘约束

下面的示例对 bought 边缘表创建边缘约束。

-- CREATE node and edge tables
CREATE TABLE Customer
   (
      ID INTEGER PRIMARY KEY
      ,CustomerName VARCHAR(100)
   )
AS NODE;
GO
CREATE TABLE Product
   (
      ID INTEGER PRIMARY KEY
      ,ProductName VARCHAR(100)
   )
AS NODE;
GO
CREATE TABLE bought
   (
      PurchaseCount INT
         ,CONSTRAINT EC_BOUGHT CONNECTION (Customer TO Product) ON DELETE NO ACTION
   )
   AS EDGE;

定义对新边缘表的引用操作

以下示例对 bought 边缘表创建边缘约束并定义删除级联引用操作。

-- CREATE node and edge tables
CREATE TABLE Customer
   (
      ID INTEGER PRIMARY KEY
      ,CustomerName VARCHAR(100)
   )
AS NODE;
GO
CREATE TABLE Product
   (
      ID INTEGER PRIMARY KEY
      ,ProductName VARCHAR(100)
   )
AS NODE;
GO
CREATE TABLE bought
   (
      PurchaseCount INT
         ,CONSTRAINT EC_BOUGHT CONNECTION (Customer TO Product) ON DELETE CASCADE
   )
   AS EDGE;

将边缘约束添加到现有边缘表

以下示例使用 ALTER TABLE 将边缘约束添加到 bought 边缘表。

-- CREATE node and edge tables
CREATE TABLE Customer
   (
      ID INTEGER PRIMARY KEY
      , CustomerName VARCHAR(100)
   )
   AS NODE;
CREATE TABLE Product
   (
      ID INTEGER PRIMARY KEY
      , ProductName VARCHAR(100)
   )
   AS NODE;
GO
CREATE TABLE bought
   (
      PurchaseCount INT
   )
   AS EDGE;
GO
ALTER TABLE bought ADD CONSTRAINT EC_BOUGHT1 CONNECTION (Customer TO Product);

对现有边缘表新建边缘约束(其中包含附加边缘约束子句)

以下示例使用 ALTER TABLE 命令,将包含附加边缘约束子句的新边缘约束添加到 bought 边缘表。

-- CREATE node and edge tables
CREATE TABLE Customer
   (
      ID INTEGER PRIMARY KEY
      , CustomerName VARCHAR(100)
   )
   AS NODE;
GO
CREATE TABLE Supplier
   (
      ID INTEGER PRIMARY KEY
      , SupplierName VARCHAR(100)
   )
   AS NODE;
GO
CREATE TABLE Product
   (
      ID INTEGER PRIMARY KEY
      , ProductName VARCHAR(100)
   )
   AS NODE;
GO
CREATE TABLE bought
   (
      PurchaseCount INT
      , CONSTRAINT EC_BOUGHT CONNECTION (Customer TO Product)
   )
   AS EDGE;
-- Drop the existing edge constraint first and then create a new one.
ALTER TABLE bought DROP CONSTRAINT EC_BOUGHT;
GO
-- User ALTER TABLE to create a new edge constraint.
ALTER TABLE bought ADD CONSTRAINT EC_BOUGHT1 CONNECTION (Customer TO Product, Supplier TO Product);

在以上示例中,EC_BOUGHT1 约束中有两个边缘约束子句,一个用于将 Customer 连接到 Product,另一个用于将 Supplier 连接到 Product。 这两个子句都可应用于析取。 即,给定的边缘必须满足这两个子句之一,才能在边缘表中使用。

对现有边缘表创建新的边缘约束(其中包含新边缘约束子句)

以下示例使用 ALTER TABLE 命令,将包含新边缘约束子句的新边缘约束添加到 bought 边缘表。

-- CREATE node and edge tables
CREATE TABLE Customer
  (
     ID INTEGER PRIMARY KEY
     , CustomerName VARCHAR(100)
  )
  AS NODE;
GO
CREATE TABLE Supplier
  (
     ID INTEGER PRIMARY KEY
     , SupplierName VARCHAR(100)
  )
  AS NODE;
GO
CREATE TABLE Product
  (
     ID INTEGER PRIMARY KEY
     , ProductName VARCHAR(100)
  )
  AS NODE;
GO
CREATE TABLE bought
  (
     PurchaseCount INT,
        CONSTRAINT EC_BOUGHT CONNECTION (Customer TO Product)
  )
  AS EDGE;
GO

在前面的示例中,假设现在还需要通过 bought 边缘表包含 SupplierProduct 的关系。 可以尝试添加新的边缘约束:

ALTER TABLE bought ADD CONSTRAINT EC_BOUGHT1 CONNECTION (Supplier TO Product);

但是,添加新的边缘约束并不是正确的解决方案。 我们在 bought 边缘表上创建了两个单独的边缘约束:EC_BOUGHTEC_BOUGHT1。 这两个边缘约束都具有不同的边缘约束子句。 如果一个边缘表在其上具有多个边缘约束,则给定的边缘表必须满足所有 边缘约束,才能在边缘表中使用它。 由于此处没有任何边缘能够同时满足 EC_BOUGHTEC_BOUGHT1,因此如果 ALTER TABLE 边缘表中存在任何行,则上述 bought 语句将失败。

要成功创建此边缘约束,需要按照本示例中所示的以下顺序来操作:

-- First, add the desired ("super-set") constraint:
ALTER TABLE bought ADD CONSTRAINT EC_BOUGHT_NEW CONNECTION (Customer TO Product, Supplier TO Product);
GO

-- Then, drop the older edge constraint:
ALTER TABLE bought DROP CONSTRAINT EC_BOUGHT;
GO

-- If needed, you can rename the new edge constraint to match the original name:
EXECUTE sp_rename '[dbo].[EC_BOUGHT_NEW]', '[dbo].[EC_BOUGHT]';

先添加新的“超集”约束,同时不删除之前的约束,这样可使操作变为仅元数据的操作 – 即无需检查 bought 表中的所有现有数据,因为其包含现有约束。

这样,为了能够在 bought 边缘中使用给定的边缘,其必须满足 EC_BOUGHT_NEW 约束中的边缘约束子句之一。 因此,可允许任何尝试将有效的 Customer 连接到 Product 或将 Supplier 连接到 Product 的节点。

删除边缘约束

下面的示例先标识边缘约束名称,再删除边缘约束。

-- CREATE node and edge tables
CREATE TABLE Customer
   (
      ID INTEGER PRIMARY KEY
      , CustomerName VARCHAR(100)
   )
   AS NODE;
GO
CREATE TABLE Product
   (
      ID INTEGER PRIMARY KEY
      , ProductName VARCHAR(100)
   ) AS NODE;
GO
CREATE TABLE bought
   (
      PurchaseCount INT
      , CONSTRAINT EC_BOUGHT CONNECTION (Customer TO Product)
    )
    AS EDGE;
GO

-- Return the name of edge constraint.
SELECT name
   FROM sys.edge_constraints
   WHERE type = 'EC' AND parent_object_id = OBJECT_ID('bought');
GO

-- Delete the primary key constraint.
ALTER TABLE bought
DROP CONSTRAINT EC_BOUGHT;

修改边缘约束

要使用 Transact-SQL 修改边缘约束,必须首先删除现有的边缘约束,然后用新定义重新创建。

查看边缘约束

目录视图中仅显示用户拥有的安全对象的元数据,或用户对其拥有某些权限的安全对象的元数据。 有关详细信息,请参阅 Metadata Visibility Configuration

此实例返回 tempdb 数据库中边缘表 bought 的所有边缘约束及其属性。

-- CREATE node and edge tables
CREATE TABLE Customer
   (
      ID INTEGER PRIMARY KEY
      , CustomerName VARCHAR(100)
   )
   AS NODE;
GO
CREATE TABLE Supplier
   (
      ID INTEGER PRIMARY KEY
      , SupplierName VARCHAR(100)
   )
   AS NODE;
   GO
CREATE TABLE Product
   (
      ID INTEGER PRIMARY KEY
      , ProductName VARCHAR(100)
   )
   AS NODE;

-- CREATE edge table with edge constraints.
CREATE TABLE bought
   (
      PurchaseCount INT
      , CONSTRAINT EC_BOUGHT CONNECTION (Customer TO Product, Supplier TO Product)
   )
   AS EDGE;

-- Query sys.edge_constraints and sys.edge_constraint_clauses to view
-- edge constraint properties.
SELECT
   EC.name AS edge_constraint_name
   , OBJECT_NAME(EC.parent_object_id) AS edge_table_name
   , OBJECT_NAME(ECC.from_object_id) AS from_node_table_name
   , OBJECT_NAME(ECC.to_object_id) AS to_node_table_name
   , is_disabled
   , is_not_trusted
FROM sys.edge_constraints EC
   INNER JOIN sys.edge_constraint_clauses ECC
   ON EC.object_id = ECC.object_id
WHERE EC.parent_object_id = object_id('bought');

后续步骤