我有一个ID为ID和ParentAccountID的Account表。这是重现步骤的脚本。
如果ParentAccountID为NULL,则将其视为顶级帐户。 每个帐户最终都应以顶级帐户结尾,即ParentAccountID为NULL
Declare @Accounts table (ID INT, ParentAccountID INT )
INSERT INTO @Accounts values (1,NULL), (2,1), (3,2) ,(4,3), (5,4), (6,5)
select * from @Accounts
-- Request to update ParentAccountID to 6 for the ID 3
update @Accounts
set ParentAccountID = 6
where ID = 3
-- Now the above update will cause circular reference
select * from @Accounts
当请求要更新一个帐户的ParentAccountID时,如果这导致循环引用,则在更新之前需要对其进行识别。
任何有想法的人!
答案 0 :(得分:3)
似乎您已经为表定义了一些业务规则:
您有两种方法可以执行此操作。
您可以在数据库中创建一个trigger,然后检查触发器中的逻辑。这具有在数据库内部运行的优势,因此它适用于每个事务,而与客户端无关。但是,数据库触发器并不总是很流行。我将它们视为side effect,它们可能很难调试。触发器作为SQL的一部分运行,因此,如果触发器运行缓慢,则SQL将会运行缓慢。
另一种方法是在应用程序层中强制执行此逻辑-无论与数据库进行通信。这更易于调试,并使您的业务逻辑对新开发人员明确-但它不在数据库中运行,因此如果您有多个客户端应用程序,最终可能会复制逻辑。
答案 1 :(得分:2)
这里是一个示例,您可以将其用作实现数据库约束的基础,该约束应防止在单行更新中使用循环引用;我认为,如果更新了多行,这不会阻止循环引用。
/*
ALTER TABLE dbo.Test DROP CONSTRAINT chkTest_PreventCircularRef
GO
DROP FUNCTION dbo.Test_PreventCircularRef
GO
DROP TABLE dbo.Test
GO
*/
CREATE TABLE dbo.Test (TestID INT PRIMARY KEY,TestID_Parent INT)
INSERT INTO dbo.Test(TestID,TestID_Parent) SELECT 1 AS TestID,NULL AS TestID_Parent
INSERT INTO dbo.Test(TestID,TestID_Parent) SELECT 2 AS TestID,1 AS TestID_Parent
INSERT INTO dbo.Test(TestID,TestID_Parent) SELECT 3 AS TestID,2 AS TestID_Parent
INSERT INTO dbo.Test(TestID,TestID_Parent) SELECT 4 AS TestID,3 AS TestID_Parent
INSERT INTO dbo.Test(TestID,TestID_Parent) SELECT 5 AS TestID,4 AS TestID_Parent
GO
GO
CREATE FUNCTION dbo.Test_PreventCircularRef (@TestID INT,@TestID_Parent INT)
RETURNS INT
BEGIN
--FOR TESTING:
--SELECT * FROM dbo.Test;DECLARE @TestID INT=3,@TestID_Parent INT=4
DECLARE @ParentID INT=@TestID
DECLARE @ChildID INT=NULL
DECLARE @RetVal INT=0
DECLARE @Ancestors TABLE(TestID INT)
DECLARE @Descendants TABLE(TestID INT)
--Get all descendants
INSERT INTO @Descendants(TestID) SELECT TestID FROM dbo.Test WHERE TestID_Parent=@TestID
WHILE (@@ROWCOUNT>0)
BEGIN
INSERT INTO @Descendants(TestID)
SELECT t1.TestID
FROM dbo.Test t1
LEFT JOIN @Descendants relID ON relID.TestID=t1.TestID
WHERE relID.TestID IS NULL
AND t1.TestID_Parent IN (SELECT TestID FROM @Descendants)
END
--Get all ancestors
--INSERT INTO @Ancestors(TestID) SELECT TestID_Parent FROM dbo.Test WHERE TestID=@TestID
--WHILE (@@ROWCOUNT>0)
--BEGIN
-- INSERT INTO @Ancestors(TestID)
-- SELECT t1.TestID_Parent
-- FROM dbo.Test t1
-- LEFT JOIN @Ancestors relID ON relID.TestID=t1.TestID_Parent
-- WHERE relID.TestID IS NULL
-- AND t1.TestID_Parent IS NOT NULL
-- AND t1.TestID IN (SELECT TestID FROM @Ancestors)
--END
--FOR TESTING:
--SELECT TestID AS [Ancestors] FROM @Ancestors;SELECT TestID AS [Descendants] FROM @Descendants;
IF EXISTS (
SELECT *
FROM @Descendants
WHERE TestID=@TestID_Parent
)
BEGIN
SET @RetVal=1
END
RETURN @RetVal
END
GO
ALTER TABLE dbo.Test
ADD CONSTRAINT chkTest_PreventCircularRef
CHECK (dbo.Test_PreventCircularRef(TestID,TestID_Parent) = 0);
GO
SELECT * FROM dbo.Test
--This is problematic as it creates a circular reference between TestID 3 and 4; it is now prevented
UPDATE dbo.Test SET TestID_Parent=4 WHERE TestID=3
答案 2 :(得分:1)
在SQL中使用自引用表/递归关系进行处理并不容易。我想这可以通过以下事实得到证明:多个人仅检查一次深度循环就无法解决问题。
要使用表约束实施此操作,您将需要基于递归查询的检查约束。最好的情况是特定于DBMS的支持,并且如果必须在每个更新上运行它,它可能无法很好地运行。
我的建议是让包含UPDATE语句的代码强制执行此操作。这可能有几种形式。无论如何,如果需要严格执行它,可能需要将对表的UPDATE访问限制为存储的proc或外部服务使用的服务帐户。
使用存储过程的方式与CHECK约束类似,不同之处在于您可以在执行更新之前使用过程(迭代)逻辑来查找周期。但是,在存储的proc中放置太多逻辑已经变得不受欢迎,并且是否应该执行这种类型的检查是团队之间/团队之间/组织之间进行判断的依据。
同样,使用基于服务的方法将使您可以使用过程逻辑来查找周期,并且可以用更适合此类逻辑的语言来编写它。这里的问题是,如果服务不是您的体系结构的一部分,那么引入一个全新的层会很繁重。但是,服务层(至少目前)可能被认为比通过存储的过程集中更新更现代/更受欢迎。
牢记这些方法-并且理解数据库中的过程和递归语法都是特定于DBMS的-确实有太多可能的语法选项可供使用。但想法是:
答案 3 :(得分:0)
最后,我在一些失败后创建了脚本,对我来说运行正常。
import React from 'react';
import {StyleSheet} from 'react-native';
import HTMLView from 'react-native-htmlview';
class App extends React.Component {
render() {
const htmlContent = `<p><a href="http://jsdf.co">♥ nice job!</a></p>`;
return (
<HTMLView
value={htmlContent}
stylesheet={styles}
/>
);
}
}
const styles = StyleSheet.create({
a: {
fontWeight: '300',
color: '#FF3366', // make links coloured pink
},
});