我必须在从Sql server表中删除记录时执行以下操作。
当我尝试从表中删除记录时(TableA)如果该记录未被任何表引用,那么只有该记录必须被删除,否则如果任何其他表记录引用该记录,我必须更新列必须更新表中的Obsolete(tableA)。
对于这种情况,我想检查该记录是否被同一数据库中的任何其他表引用。
答案 0 :(得分:0)
请尝试使用下面给出的代码
Create Table Parent
(
ID INT Identity primary key,
val varchar(10),
IsObsolete bit not null default(0)
)
create table child
(
CID INT Identity,
ID INT CONSTRAINT [FK_Parent_ID] REFERENCES Parent(ID),
)
INSERT INTO Parent(val) values('a'),('b'),('c')
insert into child(id) values (1),(2)
-- NOW SUPPOSE I WANT TO DELETE RECORD B
IF EXISTS (SELECT * FROM child WHERE id = (select id from parent where val='b')) -- this exists then update
BEGIN
UPDATE Parent
Set IsObsolete = 1
Where val='b'
END
ELSE
BEGIN
DELETE FROM Parent
Where val='b'
END
答案 1 :(得分:0)
我使用以下选择进行了查看:
SELECT FK_Table, FK_Column, PK_Table, PK_Column, PK_Alias = CASE WHEN PrevVal = PK_Table THEN PK_Table + ltrim(str(RowNumber)) ELSE PK_Table END
FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY FK_Table) AS RowNumber, lag(PK_Table) OVER (ORDER BY FK_Table) PrevVal, LEAD(PK_Table) OVER (ORDER
BY FK_Table) NextVal
FROM (SELECT TOP (100) PERCENT FK.TABLE_NAME AS FK_Table, CU.COLUMN_NAME AS FK_Column, PK.TABLE_NAME AS PK_Table,
PT.COLUMN_NAME AS PK_Column
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS C INNER JOIN
INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS FK ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME INNER JOIN
INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS PK ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME INNER JOIN
INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS CU ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME INNER JOIN
(SELECT i1.TABLE_NAME, i2.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS i1 INNER JOIN
INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS i2 ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME
WHERE (i1.CONSTRAINT_TYPE = 'PRIMARY KEY')) AS PT ON PT.TABLE_NAME = PK.TABLE_NAME) x) p
此视图将为我提供所有外键。当我必须多次连接同一张表时(出于其他目的,不在这里),我使用PK_Alias进行联合联接。
现在,我使用这个小的存储过程来返回一个数据集,该数据集包含所有表和行数,其中每个表都有PK引用:
已编辑: 约束 代表视图
create procedure mvp_CountRowsForFKs
@PKColumnName varchar(100),@ColumnValue sysname
as
begin
set nocount on
declare cCurs cursor fast_forward for
select FK_Table,FK_Column
from Constraints
where PK_Column=@PKColumnName
declare @Result table (table_name varchar(100),Counted int)
declare @TN varchar(100),@CN varchar(100),@Command varchar(300)
open cCurs
fetch next from cCurs into @TN,@CN
while @@FETCH_STATUS = 0
begin
set @Command = 'SELECT '''+@TN+''',Count(*) as Counted from '+@TN+' where '+@CN+'='+str(@ColumnValue)
insert @Result exec(@Command)
fetch next from cCurs into @TN,@CN
end
close cCurs
deallocate cCurs
select table_name,sum(Counted) Counted from @Result where Counted<>0 group by Table_Name
end
当您在两个或多个FK中使用相同的PK时,该视图将多次为您提供同一FK表,因此,根据FK中记录的存在,@ Result变量表将多次存储同一表。列:)
PS:我想PK列的类型是Integer
使用示例:
exec mvp_CountRowsForFKs 'IdUser',34
它将返回如下内容:
答案 2 :(得分:0)
这是一个老歌,但我遇到了它并认为我会添加到组合中......
如果您应用了适当的外键约束(并且您没有使用 ON DELETE CASCADE
),那么如果您尝试删除该行,则会出现异常。现在使用异常来控制正常的执行流程被认为是不好的做法,因为异常很昂贵 - 但数据库命令也是如此!我想不出更好的方法。所以这是我基于 C# 的方法:
try {
// code to execute the delete here
}
catch (SqlException ex) {
if (!SqlErrors.IsForeignKeyConstraintError(ex)) {
throw;
}
// could not delete, do something else...
}
为了区分实际的 FK 异常和其他一些 SQL 异常,这是我的类:
using System.Data.SqlClient;
using System.Linq;
namespace My.Funky.Namespace {
public static class SqlErrors {
// From https://msdn.microsoft.com/en-us/library/cc645611.aspx
// also: select * from sys.messages where language_id=1033 and severity between 11 and 16
private enum Errors {
None = 0,
ForeignKeyConstraint = 547,
RaiseError = 50000
// ...there are 12K+ messages - so not putting them all here!
}
public static bool IsForeignKeyConstraintError(SqlException ex) {
return ex != null && (from object err in ex.Errors select err as SqlError).Any(err =>
err.Number == (int)Errors.ForeignKeyConstraint ||
err.Number == (int)Errors.RaiseError
);
}
}
}
诚然,海报没有提到 C#,但很可能最初的问题现在完全没有实际意义 - 其他人可能会发现它很有用! :o)
(我希望在 T-SQL 中实现类似的东西会很容易)