Restricciones perimetrales

Se aplica a: SQL Server 2019 (15.x) y versiones posteriores Azure SQL DatabaseAzure SQL Managed Instance

Las restricciones perimetrales se pueden usar para aplicar la integridad de datos y una semántica específica en las tablas perimetrales de una base de datos de grafos de SQL Server.

Restricciones perimetrales

De forma predeterminada, las tablas perimetrales no aplican nada a los puntos de conexión del perímetro. Es decir, los perímetros de una base de datos de gráficos no podían conectar ningún nodo con ninguno otro, independientemente del tipo.

SQL Graph admite restricciones perimetrales, con las que los usuarios pueden agregar restricciones a sus tablas perimetrales, de manera que pueden aplicar una semántica específica y mantener la integridad de datos. Al agregar un perímetro nuevo a una tabla perimetral que tiene restricciones perimetrales, el Motor de base de datos exige que los nodos que está intentando conectar el perímetro existan en las tablas de nodo adecuadas. También se garantiza que no se pueda quitar un nodo si algún perímetro hace referencia a él.

Cláusulas de restricciones perimetrales

Una sola restricción perimetral consta de una o varias cláusulas de restricción perimetral.

CONSTRAINT constraint_name CONNECTION (cause1[, clause2...])
  • Una cláusula de restricción perimetral es un par de nombres de tabla de nodo, separados por la palabra clave TO.
  • El primer nombre de tabla de la cláusula de restricción perimetral es el nombre de la tabla de nodo FROM para la relación perimetral.
  • El segundo nombre de tabla de la cláusula de restricción perimetral es el nombre de la tabla de nodo TO para la relación perimetral.
  • Por tanto, el par de nombres de tabla indica la dirección de la relación perimetral.
  • Como hemos mencionado antes, una restricción perimetral puede contener una o varias cláusulas de restricción perimetral.

Varias restricciones y cláusulas

  • Para aplicar varias restricciones perimetrales, definidas para la misma tabla perimetral, se utiliza un operador AND.
  • Para aplicar varias cláusulas de restricción perimetral, definidas en la misma restricción perimetral, se utiliza un operador OR.

Observe los nodos Supplier y Customer del gráfico. Cada uno puede estar relacionado con el nodo Product por medio de una única tabla perimetral compartida: bought. La tabla perimetral bought admite los tipos de relación Customer-(bought)->Product y Supplier-(bought)->Product. Esto se puede lograr usando una restricción perimetral única con varias cláusulas de restricción perimetral.

Ejemplos
CONSTRAINT EC_BOUGHT CONNECTION (Customer TO Product)

En el ejemplo anterior, se muestra una restricción perimetral con una cláusula de restricción perimetral. Esta restricción admite Customer-(bought)->Product. Es decir, se permitiría insertar una relación perimetral bought que fuese de Customer a Product. Si se intenta insertar cualquier otra combinación de nodos, como Supplier-(bought)->Product, aunque pueda describir una relación válida en el mundo real, produciría un error.

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

En el ejemplo anterior se define una restricción perimetral con dos cláusulas de restricción perimetral. Estas cláusulas de restricción permiten que el perímetro bought contenga relaciones Supplier-(bought)->Product o Customer-(bought)->Product. La inserción de cualquier otro tipo de relaciones perimetrales en la tabla bought produciría un error.

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

En el ejemplo anterior, se muestran dos restricciones en la misma tabla perimetral, donde cada restricción perimetral especifica una cláusula de restricción. En esta situación, SQL solo permitiría inserciones que cumplieran LAS DOS cláusulas de restricción perimetral simultáneamente. Esto no es posible. No hay ningún par de nodos que pueda cumplir las dos cláusulas de restricción perimetral. Esta combinación de restricciones perimetrales hace que la tabla perimetral sea inutilizable.

Para obtener una explicación detallada de dónde se pueden usar varias restricciones perimetrales en una situación real, vea más adelante en esta página el ejemplo "Creación de una restricción perimetral en una tabla perimetral existente con una cláusula de restricción perimetral nueva".

Índices en las restricciones perimetrales

La creación de una restricción perimetral no crea automáticamente un índice en las columnas $from_id y $to_id de la tabla perimetral. Se recomienda crear manualmente un índice en un par de columnas $from_id y $to_id si tiene consultas de búsqueda de puntos o una carga de trabajo OLTP.

Acciones referenciales ON DELETE en restricciones perimetrales

Las acciones en cascada en una restricción perimetral permiten a los usuarios definir las acciones que toma el motor de base de datos cuando un usuario elimina los nodos, a los que se conecta el perímetro especificado. Se pueden definir las siguientes acciones referenciales: NO ACTION: el motor de base de datos genera un error al intentar eliminar un nodo que tiene perímetros conectados.

CASCADE: cuando se elimina un nodo de la base de datos, se eliminan los perímetros conectados.

Trabajo con restricciones perimetrales

Puede definir una restricción perimetral en SQL Server mediante Transact-SQL. Las restricciones perimetrales solo se pueden definir en tablas perimetrales de gráficos. Para crear, eliminar o modificar una restricción perimetral, debe contar con el permiso ALTER en la tabla.

Creación de restricciones perimetrales

En los ejemplos siguientes se muestra cómo crear restricciones perimetrales en tablas nuevas o existentes.

Crear una restricción perimetral en una nueva tabla perimetral

En el ejemplo siguiente se crea una restricción perimetral en la tabla perimetral 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;

Definir acciones referenciales en una tabla perimetral nueva

En el ejemplo siguiente se crea una restricción perimetral en la tabla perimetral bought y se define la acción referencial ON DELETE CASCADE.

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

Agregar una restricción perimetral a una tabla perimetral existente

En el ejemplo siguiente se usa ALTER TABLE para agregar una restricción perimetral a la tabla perimetral 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);

Creación de una restricción perimetral en una tabla perimetral existente con cláusulas de restricción perimetral adicionales

En el ejemplo siguiente se usa el comando ALTER TABLE para agregar una restricción perimetral nueva con cláusulas de restricción perimetral adicionales en la tabla perimetral 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);

En el ejemplo siguiente, hay dos cláusulas de restricción perimetral en la restricción EC_BOUGHT1, una que conecta Customer con Product y otra que conecta Supplier con Product. Estas dos cláusulas se aplican como disyunción; es decir, un perímetro determinado tiene que cumplir cualquiera de estas dos cláusulas para poder estar permitido en la tabla perimetral.

Crear una restricción perimetral en una tabla perimetral existente con una cláusula de restricción perimetral nueva

En el ejemplo siguiente se usa el comando ALTER TABLE para agregar una restricción perimetral nueva con una cláusula de restricción perimetral nueva en la tabla perimetral 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

Partiendo del ejemplo anterior, imagine que ahora tenemos que incluir también la relación de Supplier con Product mediante la tabla perimetral bought. Puede intentar agregar una nueva restricción perimetral:

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

Sin embargo, agregar una nueva restricción perimetral no es la solución correcta. Hemos creado dos restricciones perimetrales independientes en la tabla perimetral bought: EC_BOUGHT y EC_BOUGHT1. Ambas restricciones perimetrales tienen cláusulas de restricción perimetral diferentes. Si una tabla perimetral contiene más de una restricción perimetral, un perímetro concreto tendrá que cumplir TODAS las restricciones perimetrales para poder estar permitido en la tabla perimetral. Puesto que en este caso ningún perímetro puede cumplir EC_BOUGHT y EC_BOUGHT1, se produce un error en la anterior instrucción ALTER TABLE si hay filas en la tabla perimetral bought.

Para que esta restricción perimetral se cree correctamente, el método prescrito consiste en seguir una secuencia como se muestra en este ejemplo:

-- 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]';

El hecho de que agregáramos primero la nueva restricción de "superconjunto" sin quitar la anterior permite que la operación sea solo de metadatos; no necesita comprobar todos los datos de la tabla bought, ya que abarca la restricción existente.

De este modo, para que se permita un perímetro concreto en la tabla perimetral bought, tiene que cumplir cualquiera de las cláusulas de restricción perimetral de la restricción EC_BOUGHT_NEW. Por lo tanto, se permite cualquier perímetro que intente conectar nodos válidos Customer a Product o Supplier a Product.

Eliminación de restricciones perimetrales

En el ejemplo siguiente se identifica primero el nombre de la restricción perimetral y, luego, se elimina.

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

Modificación de restricciones perimetrales

Para modificar una restricción perimetral mediante Transact-SQL, deberá eliminar la restricción perimetral existente y, a continuación, volver a crearla con la nueva definición.

Visualización de restricciones perimetrales

La visibilidad de los metadatos en las vistas de catálogo se limita a los elementos protegibles y que son propiedad de un usuario o sobre los que el usuario ha recibido algún permiso. Para obtener más información, consulte Metadata Visibility Configuration.

El ejemplo devuelve todas las restricciones perimetrales y sus propiedades para la tabla perimetral bought en la base de datos tempdb.

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

Pasos siguientes