Database-Design

將 CSV 列拆分為單獨的表(強制執行 1NF)是否不必要的複雜性?

  • May 14, 2018

我已經非常仔細地詢問了表格設計者為什麼他選擇在多個表格中創建 CSV 列。

設計師的回答是,將 CSV 列拆分為單獨的表格是“不必要的複雜”。

一個 CSV 列用於隱式連接,以計算 CSV 列中的多個值中的某個值已被使用的次數。

綁定兩個表的where原因使用了一個like % | table_b.csv_column | %操作。

我閱讀並了解到,如果有充分的理由不需要標準化,則並不總是需要標準化。

但是,我不知道不規範化的充分理由是什麼。

這是(避免“複雜化”)不強制執行第一範式(1NF)的一個很好的理由嗎?

桌子:TB_DISASTER_CAUSE_CATE

CATEGORY_ID   CATEGORY_N CATEGORY_L L
------------- ---------- ---------- -
DC006001002   ****                3 Y
DC006001003   ****                3 Y
DC006002001   ****                3 Y
DC007001001   ****                3 Y
DC007002001   ****                3 Y
DC007002002   ****                3 Y
DC007003001   ****                3 Y
DC007003002   ****                3 Y
DC007003003   ****                3 Y

桌子:TB_DISASTER_HISTORY

SEQ DISASTER_TYPE DISASTER_CAUSE                                             
----- ------------- ------------------------------------------------------------
  32 DT001003002   DC001001004|DC002001002|DC003001001|DC007002001             
  33 DT002003007   DC001001004|DC007002001                                     
  34 DT002003009   DC001001003|DC003001002|DC007002001                         
  16 DT002003001   DC001001004|DC002001002|DC005001003|DC007002001             
  17 DT002003001   DC001001004|DC002001002|DC005001003|DC006001003|DC007002001 
  18 DT002003007   DC001001003|DC001002002|DC002001001|DC002002002|DC004001001|
                   DC007002001                                                 

  19 DT002003007   DC001001003|DC001001004|DC003001002|DC007002001             
  20 DT002003007   DC001001003|DC001001004|DC007002001                         
  21 DT002003007   DC001001003|DC003004001|DC007002001                         

 SEQ DISASTER_TYPE DISASTER_CAUSE                                             
----- ------------- ------------------------------------------------------------
  22 DT002003007   DC001001003|DC001001004|DC002001002|DC007002001  

詢問

select 
         x.category_id as category_id
         ,x.cnt as ccnt
         ,y.category_nm as category_nm
       from(

       select category_id , count(*) cnt from 
           TB_DISASTER_CAUSE_CATE  a , 
           tb_disaster_history b 

          where last_yn  = 'Y'
            and b.disaster_cause like '%'||a.category_id||'%'
            and b.disaster_type=#disaster_type#
        group by category_id  ) x

        inner join (
        select * from tb_disaster_cause_cate
        where last_yn='Y') y
        on x.category_id = y.category_id;

這個問題的答案和這個問題的答案是一樣的。兩者都是關於第一範式(1NF)。連結的問題與由通過破折號“-”連接的不同數據元素組成的鍵列的理由有關。這個問題與非鍵列的理由有關,該列由通過管道“|”填充的同一數據元素的不同值組成。要回答這個問題,我們必須首先評估這樣的設計是否違反了 1NF 的意圖,其次,如果是,那麼在什麼時候違反 1NF 是合理的。

背景

首先,快速回顧一下什麼是 1NF。一個表是一個關係(R-Table)表,因此如果在其設計中遵循確保:

  • 不同的、無序的行
  • 唯一命名的無序列
  • 每列包含來自相應域的單個值

注意要求 3 意味著任何列都不能有缺失值(或 NULL 標記)。讓我們暫時假設滿足要求 1 和 2,以及要求 3 的“無缺失值”部分。我說“假設”是因為如果不查看表中的所有行來驗證沒有缺失值、沒有重複行以及在列或行的排序中沒有編碼的隱式資訊,我們真的無法評估合規性。現在,如果模式包含完整性約束,這些約束實現了概念模型中的業務規則以保證沒有重複行和缺失值,我們就不必檢查所有數據來驗證是否符合這些要求,因為我們知道 DBMS 將強制與這些規則保持一致每一個案例。但是,我們仍然需要檢查所有數據,以確保不使用行或列排序來隱式編碼附加資訊。

鑑於剛剛提出的假設,我們需要確定將多個災難原因程式碼放置到由管道分隔的單個列中是否違反了單值列的要求 3。在我對上一個關於鍵列的問題的回答中,該鍵列具有多個數據元素,由破折號分隔,我說這樣的設計確實違反了該要求,並且在任何情況下都是不合理的。我的答案是基於對Fabian PascalChris Date對 1NF 的最新和最明確的思考的研究(我將在答案末尾提供參考)。Fabian 本人對我的回答進行了更正,為回答這個問題做出了貢獻。他說我的斷言太嚴格了,而且:

如果該組合僅用於視覺目的,並且可以保證DBMS 始終將其作為單個值訪問(這裡強調我的)。問題是這樣的保證是不穩定的。

Fabian 的修正觸及了單值列要求的微妙之處。如果該列的域是關係值域(RVD),並且因此包含在該列中的單個表,則它不會違反1NF。在我們為現在能夠證明您的“高級”開發人員在創建具有由破折號或管道或其他任何東西分隔的多個數據元素的列的做法感到高興之前,我想首先指出沒有 SQL DBMS 實際上支持 RVD,從而使操作在列上作為單個值很難。其次,Date 和 Pascal 都指出,雖然 RVD 不違反 1NF,但它確實引入了額外的複雜性和不對稱性,而沒有(除了在少數極端情況下)任何額外的好處。因此,雖然它可能是允許的,但它可能不是一個好主意。詳情請參閱參考資料。

評估設計

有了 1NF 的背景,讓我們開始討論這種情況。我要送出的有問題的列(災難原因)不是單值列**,因為您給出了一個範例查詢,其中訪問了較大字元串的各個值。因此該表未標準化。鑑於此,僅僅因為規範化設計需要兩個表並因此被認為是“不必要的複雜” ,以使其不是R-Table 的方式設計該表是否仍然合理?我會說絕對不是有兩個原因。首先,愛因斯坦說事情應該盡可能簡單,但不能簡單。這種非規範化設計是“過於簡單”的一個例子,因為如果表格不是 R-Table,則無法獲得 R-Table 的任何好處。參考資料詳細介紹了這一點,但要點是失去了對每個數據元素的保證邏輯訪問。一個更微妙的問題是,保持災難原因的完整性要困難得多。如果災難原因是子表,則通過鍵約束保證每個災難原因程式碼只能分配一次。作為災難歷史表中的多值列,應用程序很容易意外地將相同的原因程式碼連接兩次。即使考慮如何編寫在多值列上實現此約束所必需的程序觸發器,這也讓我頭疼。最後,請注意,我只討論了邏輯**設計。我敢肯定,在各種 SQL DBMS 的物理實現方面,有很多專家會對將要執行的查詢的優化結果感到畏縮,就像提供的查詢一樣,以訪問這些數據。

最後,我認為即使是帶有兩個表的規範化設計“不必要地複雜”的理由也是錯誤的。相反,我認為單表中的多值列“不必要地複雜”。您提供的查詢,它試圖計算給定災難原因導致災難的次數,很多如果設計只是有一個災難表和一個子災難原因表,編寫起來會更複雜。嵌套將是不必要的,並且連接的目的將簡單明了。第一次查看架構的人可以很容易地理解,甚至無需查看數據,我們擁有有關災難的資訊,並且每個災難都可能有很多原因。使用多值列時,這個簡單的結構會變得模糊,使用者第一次必須深入研究數據值才能發現實際上災難可能有很多原因。

參考

Fabian Pascal 的實用數據庫基礎系列。論文 #5 涉及 1NF 和 RVD。如果您購買所有論文(您確實應該購買,因為它們是解決所有基礎知識的補充集)可以獲得另外兩篇專門針對 1NF 的論文 - CJ Date 的“第一範式的真正含義”和“什麼是第一範式形式意味著不”,Fabian Pascal 著。接下來,CJ Date 撰寫了大量有關關係理論的文章。關於規範化的最新參考資料是Database Design and Relational Theory: Normal Forms and All That Jazz。最後,Fabian Pascal最近寫了一篇關於 1NF的部落格,這些部落格非常適合閱讀。

引用自:https://dba.stackexchange.com/questions/123155