2014-05-11 38 views
0

在PostgreSQL,我有一個主表,ICD9,這是爲了與約束持有cicd9和cdesc的獨特組合:使用ON UPDATE CASCADE時更新會導致重複值

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數據庫服務器(我是管理員)。

+0

我想你會以錯誤的方式去做。如果您打算讓「主」表包含拼寫,則應該在主表中引入一個仿真關鍵字(身份或類似關鍵字),並將其用作關聯表中的FK。重複描述是可以避免的。對於您的特定問題,如果在更新時遇到重複的值衝突,那是因爲數據會導致一個(您可能會修正拼寫錯誤,其中存在非拼寫錯誤記錄),然後您應該更新數據手動/通過觸發器。 –

+0

@ AllanS.Hansen有道理。我需要更多地研究觸發器。一個例子真的會有幫助。 –

回答

0

如果updatingicd9進入導致unique constraint違規,那麼在我看來,就像你真正想要在這種情況下,做的是deleteicd9項更新它代替。

如果deleting的條目會導致一個FK constraint違反其他表,那麼我會建議先得到通過更新子表的條目指的是導致unique constraint違反入境擺脫這一問題的(即'正確「的值已經存在),然後對」錯誤「值執行delete

如果你想這個自動在一定程度上,你可能會創建一個pl/pgSQL功能將:

  1. 檢查,看是否有唯一性約束將違背
  2. 如果是這樣,這是一個刪除候選
  3. 然後,檢查子表,看是否有參照刪除候選
  4. 如果是這樣,這些update指向是「正確的」行任何條目 - 即一個會導致獨特違反約束
  5. 然後做該行的deleteicd9表內

注:你不會希望有約束CASCADEDELETE,因爲你不想刪除行引用它,但更新它們以指向icd9中的另一行。

編輯: 以編程確定FK(或其他)約束的表有,可以查詢pg_constraint表其爲catalog的一部分。有關更多詳細信息,請參閱http://www.postgresql.org/docs/9.3/static/catalog-pg-constraint.html

您可能想加入pg_class以使用原始表名稱開始。有關更多信息,請參閱http://www.postgresql.org/docs/9.3/static/catalog-pg-class.html

+0

有沒有辦法查詢PostgreSQL數據庫中的依賴於這個約束的子表?顯而易見的目標是發現哪些表要改變。 –

+0

是的。我會用一些添加細節來編輯我的答案。 – khampson

+0

我做的湯裏找到這樣的:選擇confrelid :: regclass的,af.attname爲FCOL, conrelid :: regclass的,a.attname從pg_attribute裏AF山坳,pg_attribute裏一,(選擇conrelid,confrelid,康基爲[I] 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和af.attrelid = confrelid和 a.attnum =康基和a.attrelid = conrelid AND confrelid :: regclass的= 'MY_TABLE' :: regclass的AND af.attname = 'my_referenced_column'; –

0

我會建議這是一個可能的解決方案。如果有人感興趣,我會對這個優化版本感興趣。感謝@Ken的指針。 (我不知道如何爲SO格式化這個格式,對不起)。

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; 
      } 
     } 
    } 
相關問題