Từ SQL Server 2017 và Azure SQL DB, bạn có thể sử dụng các khả năng cơ sở dữ liệu đồ thị mới và mệnh đề MATCH mới để mô hình hóa loại mối quan hệ này. Nhìn không có giá trị! Một tập lệnh mẫu:
USE tempdb
GO
IF NOT EXISTS ( SELECT * FROM sys.schemas WHERE name = 'Tasks' )
EXEC('CREATE SCHEMA Tasks')
GO
IF NOT EXISTS ( SELECT * FROM sys.schemas WHERE name = 'graph' )
EXEC('CREATE SCHEMA graph')
GO
DROP TABLE IF EXISTS [Tasks].[TaskDetail]
DROP TABLE IF EXISTS graph.taskDetail
DROP TABLE IF EXISTS graph.isParentOf
DROP TABLE IF EXISTS graph.isChildOf
GO
CREATE TABLE [Tasks].[TaskDetail]
(
[TaskDetailID] [int] IDENTITY(1,1) NOT NULL,
[TaskName] [varchar](50) NOT NULL,
[TaskDescription] [varchar](250) NULL,
[IsActive] [bit] NOT NULL CONSTRAINT [DF_TaskDetail_IsActive] DEFAULT ((1)),
[ParentID] [int] NULL,
CONSTRAINT [PK_TaskDetail_TaskDetailID] PRIMARY KEY CLUSTERED ([TaskDetailID] ASC),
CONSTRAINT [FK_TaskDetail_ParentID] FOREIGN KEY([ParentID]) REFERENCES [Tasks].[TaskDetail]([TaskDetailID])
);
GO
SET IDENTITY_INSERT [Tasks].[TaskDetail] ON
GO
INSERT INTO [Tasks].[TaskDetail] ( TaskDetailID, TaskName, TaskDescription, IsActive, ParentID )
VALUES
( 2, 'Cash Receipt 1', 'Fund Account', 1, NULL ),
( 3, 'Cash Receipt 2', 'Check the ...', 1, 2 ),
( 4, 'Non Trade', 'Income & Expense', 1, NULL ),
( 5, 'Income Verified', 'Income Verified', 1, 4 ),
( 6, 'Expense Verified', 'Expense Verified', 1, 4 ),
( 7, 'Pricing', 'Pricing Verified', 1, NULL ),
( 8, 'Manual Pricing', 'Manual Pricing', 1, 7 ),
( 9, 'Missing Pricing', 'Missing Pricing', 1, 7 )
GO
SET IDENTITY_INSERT [Tasks].[TaskDetail] OFF
GO
-- Create graph tables
CREATE TABLE graph.taskDetail (
taskDetailId INT PRIMARY KEY,
taskName VARCHAR(50) NOT NULL,
taskDescription VARCHAR(250) NULL,
isActive BIT NOT NULL
) AS NODE;
CREATE TABLE graph.isParentOf AS EDGE;
CREATE TABLE graph.isChildOf AS EDGE;
GO
-- !!TODO add indexes
-- Add the node data
INSERT INTO graph.taskDetail ( taskDetailId, taskName, taskDescription, isActive )
SELECT taskDetailId, taskName, taskDescription, isActive
FROM Tasks.TaskDetail
-- Add the edge data
INSERT INTO graph.isParentOf ( $from_id, $to_id )
SELECT p.$node_id, c.$node_id
FROM Tasks.TaskDetail td
INNER JOIN graph.taskDetail c ON td.TaskDetailId = c.taskDetailId
INNER JOIN graph.taskDetail p ON td.ParentID = p.taskDetailId
-- Add inverse relationship
INSERT INTO graph.isChildOf ( $from_id, $to_id )
SELECT $to_id, $from_id
FROM graph.isParentOf
GO
-- Now run the graph queries
SELECT
FORMATMESSAGE( 'Task [%s](%i) is the parent of [%s](%i)', p.taskName, p.taskDetailId, c.taskName, c.taskDetailId )
FROM graph.taskDetail p, graph.isParentOf isParentOf, graph.taskDetail c
WHERE MATCH ( p-(isParentOf)->c )
ORDER BY 1;
-- Tasks with same parent
-- Tasks 5 and 6 have the same parent 4
-- Tasks 8 and 9 have the same parent 7
SELECT
FORMATMESSAGE( 'Tasks %i and %i have the same parent %i', t1.taskDetailId, t3.taskDetailId, t2.taskDetailId )
FROM graph.taskDetail t1, graph.isChildOf c1, graph.taskDetail t2, graph.isChildOf c2, graph.taskDetail t3
WHERE MATCH ( t1-(c1)->t2<-(c2)-t3 )
AND t1.$node_id < t3.$node_id
ORDER BY 1;
-- Find tasks with no parents?
SELECT
FORMATMESSAGE( 'Task [%s](%i) has no parents.', p.taskName, p.taskDetailId )
FROM graph.taskDetail p
WHERE NOT EXISTS
(
SELECT *
FROM graph.isParentOf isParentOf
WHERE p.$node_id = isParentOf.$from_id
)
Kết quả của tôi: