C# 在更新导致重复值时使用更新级联
在PostgreSQL中,我有一个主表ICD9,用于保存cicd9和cdesc的唯一组合,约束条件为:C# 在更新导致重复值时使用更新级联,c#,sql,database,postgresql,constraints,C#,Sql,Database,Postgresql,Constraints,在PostgreSQL中,我有一个主表ICD9,用于保存cicd9和cdesc的唯一组合,约束条件为: CONSTRAINT constraint_cdesc UNIQUE (cicd9, cdesc) 我有多个子表引用ICD9表,其中包含: CONSTRAINT fk_icd9 FOREIGN KEY (cicd9, cdesc) REFERENCES icd9 (cicd9, cdesc) MATCH SIMPLE ON UPDATE CASCADE ON DELETE RESTRICT
CONSTRAINT constraint_cdesc UNIQUE (cicd9, cdesc)
我有多个子表引用ICD9表,其中包含:
CONSTRAINT fk_icd9 FOREIGN KEY (cicd9, cdesc)
REFERENCES icd9 (cicd9, cdesc) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED
我可以更改任何需要更改的约束,但我的目标是使用主表来更正子表的所有拼写错误。我在网上找不到好的答案。问题在于,更新cdesc中的错误拼写或cicd9中的错误代码会导致主表中出现重复条目,从而引发错误
我认为做这件事的最好方法是什么比较常见的问题
注意:我在一个单独的程序中使用C Net 4.0来寻址PostgreSQL 9.1数据库服务器,我是该服务器的管理员。如果更新icd9中的条目会导致唯一的约束冲突,那么在我看来,在这种情况下,您实际需要做的是从icd9中删除条目,而不是更新它
如果删除条目会导致其他表中的FK约束冲突,那么我建议首先通过更新子表中的条目来消除该问题,以引用导致唯一约束冲突的条目,即已经存在的“正确”值,然后对“不正确”的值进行删除
如果您想在某种程度上实现自动化,您可能可以创建一个pl/pgSQL函数,该函数将:
检查是否会违反任何唯一约束
如果是,这是一个删除候选项
然后,检查子表以查看是否有任何引用删除候选项的条目
如果是这样,请将其更新为指向“正确”的行,即将导致唯一约束冲突的行
然后删除icd9表中的行
注意:您不希望在约束级联中使用DELETE,因为您不希望删除引用它的行,而是更新它们以指向icd9中的另一行
编辑:
要以编程方式确定表的FK或其他约束,可以查询目录中的pg_约束表。有关更多详细信息,请参阅
您可能希望将其与pg_类连接起来,从原始表名开始。有关更多信息,请参阅。我建议将此作为可能的解决方案。如果有人感兴趣的话,我最感兴趣的是这个优化版本。谢谢@Ken的指点。我不知道如何最好地格式化这个,所以,对不起
internal static void UpdateDxLibraryTx(PhysicalExam.DX newdx, PhysicalExam.DX olddx)
{
String Unique_Violation = "23505";
// nothing to change.
if (olddx.description == null) return;
PhysicalExam.DX oldDx = (PhysicalExam.DX)olddx;
// the server structure was created in MainWindow on initialization.
string connect = server.connection_string;
string update = " update icd9 set cicd9='" + newdx.icd9 + "', cdesc= '" + newdx.description.ToLower().Trim() + "'" +
" ,chronic ='" + newdx.chronic + "' ,modified='" + DateTime.Now + "'" +
" where cicd9 ='" + oldDx.icd9 + "'" +
" and trim(cdesc) ='" + oldDx.description.ToLower().Trim() + "' and chronic ='" + oldDx.chronic + "';";
string getchildtables = " select confrelid::regclass, af.attname as fcol, "+
" conrelid::regclass, a.attname as col "+
" from pg_attribute af, pg_attribute a, "+
" (select conrelid,confrelid,conkey[i] as conkey, confkey[i] as confkey "+
" from (select conrelid,confrelid,conkey,confkey, "+
" generate_series(1,array_upper(conkey,1)) as i "+
" from pg_constraint where contype = 'f') ss) ss2 "+
" where af.attnum = confkey and af.attrelid = confrelid and "+
" a.attnum = conkey and a.attrelid = conrelid "+
" AND confrelid::regclass = 'ICD9'::regclass AND ( af.attname = 'cicd9' OR af.attname ='cdesc');";
string sf = " update {0} set cicd9= '"+ newdx.icd9 +"' ,cdesc= '"+ newdx.description.ToLower().Trim() +"'," +
" chronic = '"+newdx.chronic+"', modified= '"+ DateTime.Now +"'" +
" where cicd9 = '"+ oldDx.icd9 +"' and trim(cdesc)= '"+ oldDx.description.ToLower().Trim() +"' and chronic ='"+ oldDx.chronic +"';";
string delete = "Delete from icd9 where trim(cicd9) = '" + oldDx.icd9.Trim() + "'" +
" and trim(cdesc)='" + oldDx.description.Trim() + "' and chronic = '"+ oldDx.chronic +"';";
// NpsqlConnection is unmanaged code...should use a using() block.
using (NpgsqlConnection pgConnection = new NpgsqlConnection(connect))
{
try
{
pgConnection.Open();
// Connection successful
// Create a new transaction
using (NpgsqlTransaction pgTransaction = (NpgsqlTransaction)pgConnection.BeginTransaction())
{
try
{
using (NpgsqlCommand updateCMD = new NpgsqlCommand(update, pgConnection, pgTransaction))
{
updateCMD.ExecuteNonQuery();
}
//Commit transaction. Hope it works! Will fail for unique key violation.
pgTransaction.Commit();
}
catch (NpgsqlException ex)
{
pgTransaction.Rollback();
if (ex.Code == Unique_Violation)
{
try
{
using (NpgsqlDataAdapter children = new NpgsqlDataAdapter(getchildtables,pgConnection))
{
DataSet ds = new DataSet();
children.Fill(ds);
DataView dv = new DataView();
dv.Table = ds.Tables[0];
foreach (DataRowView drv in dv)
{
string childtable = (string)drv[2];
string updatechild = String.Format(sf, childtable);
NpgsqlCommand updateCMD = new NpgsqlCommand(updatechild, pgConnection);
int k = updateCMD.ExecuteNonQuery();
}
/* all tables now point to new value. remove old value from parent table. */
NpgsqlCommand deleteCMD = new NpgsqlCommand(delete, pgConnection);
int j = deleteCMD.ExecuteNonQuery();
}
}
catch (NpgsqlException ex1)
{
string error = ex1.Message;
throw;
}
}
else
throw;
}
}
}
catch (NpgsqlException ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
catch (Exception ex)
{
string s = ex.Message;
throw;
}
}
}
有没有办法在PostgreSQL数据库中查询依赖于此约束的子表?显而易见的目标是发现要更改哪些表。Tom Lane确实发现了这一点:从pg_属性af中选择conrelid::regclass,af.attname作为fcol,conrelid::regclass,a.attname作为col,从pg_属性a中选择conrelid,conrelid,conkey[I]作为conkey,conkey[I]作为conkey,从select conrelid,conrelid,conkey,conkey,从pg_约束生成_series1,数组_upperconkey,1 as i,其中contype='f'ss ss2,其中af.attnum=confkey和af.attrelid=confrelid,a.attnum=conkey和a.attrelid=conrelid和confrelid::regclass='my_table'::regclass和af.attname='my_referenced_column';这似乎应该行得通。您可能还希望在整个查询中添加conname元素,这是实际约束的名称,尽管这取决于您处理问题的方式,如果您只想知道依赖表是什么,这可能并不重要。我认为您的做法是错误的。如果希望主表包含拼写,则应在主表中引入人工键标识或类似标识,并在关联表中将其用作FK。重复描述是要避免的。对于您的特定问题,如果在更新时出现重复值冲突,那么这是因为数据将导致一个错误,您可能会修复一个拼写错误,其中已经存在一个非拼写错误记录,然后您可能应该手动/通过触发器更新数据。@AllanS.Hansen是有意义的。我需要更多地研究触发器。举个例子会很有帮助。