Mysql

當第三個 NF 不是最佳解決方案時?(表現)

  • March 13, 2018

我有 3 張桌子:

Measurements ('id', 'name')
Records ('id', 'measurement_id', 'time', 'number') - Measurement has many records
Parameters ('id', 'record_id', 'param', 'value') - Record has many parameters

我遇到的問題是我需要允許最終使用者通過網頁將數據導入數據庫,目前由於:

  1. 內聯文件 (MySQL)
  2. 大數據文件 500MB+

我必須先導入測量和記錄,這需要總解析(導入)時間的 50%;之後我可以填充參數,這需要另外 50% 的時間。

另一方面,如果我沒有完全規範化的數據庫形式,我將能夠一次性解析數據並節省 50% 的解析時間。順便說一句,目前解析器大約需要 7 分鐘來解析(導入)500MB 的數據,節省 50% 的時間,我們正在尋找 3-4 分鐘才能完全完成導入!

所以表格看起來像:

Measurements ('id', 'name')
Records ('id', 'measurement_id', 'time', 'param', 'value') - Measurement has many records

當然,這樣做的缺點是我不得不在“記錄”表的“時間”列中重複數據,從長遠來看,這將花費我的數據庫空間。

ps 我可能不需要日期部分,因為我知道測量何時開始和結束,因此我可以在需要時使用程式語言計算日期;但是,對我來說,在數據庫中有“時間”會更方便,這樣我就可以用它來查詢數據庫


最終使用者文件中的範例數據行

24/01/2017,17:06,0.123,0.43,1.23,4,4.43,0.12

數字是參數,我將它們映射到導入器中以更正參數名稱。

目前,由於 INLINE 無法處理外鍵,我指的是“參數”表中的“record_id”,我必須有兩個循環來導入文件,如上文所述。

1\ 我會在 PHP 中創建測量值並獲取 ID 號。2\我將遍歷數據文件並創建一個文件以通過INLINE為“記錄”表導入;每行還將包含一個帶有參數的 json 數據數組。3\ 我將遍歷“記錄”表並創建新文件以通過 INLINE 導入“參數”表中的參數。


現有表的縮短版本

CREATE TABLE `measurements` (
   `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,  
   `measurement` ENUM('1','2') NOT NULL COLLATE 'utf8_unicode_ci',
)

CREATE TABLE `records` (
   `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
   `measurement_id` INT(10) UNSIGNED NOT NULL,
   `no` INT(11) NOT NULL,
   `date` DATE NOT NULL,
   `time` TIME NOT NULL, 
   `params_temp` TEXT NOT NULL COLLATE 'utf8_unicode_ci',
   PRIMARY KEY (`id`), 
   INDEX `records_measurement_id_index` (`measurement_id`), 
   CONSTRAINT `records_measurement_id_foreign` FOREIGN KEY (`measurement_id`) REFERENCES `measurements` (`id`) ON DELETE CASCADE, 
)

CREATE TABLE `params` (
   `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
   `record_id` INT(10) UNSIGNED NOT NULL,
   `parameter` VARCHAR(255) NOT NULL COLLATE 'utf8_unicode_ci',
   `value` DOUBLE(8,2) NOT NULL, 
   PRIMARY KEY (`id`),
   INDEX `params_record_id_index` (`record_id`), 
   CONSTRAINT `params_record_id_foreign` FOREIGN KEY (`record_id`) REFERENCES `records` (`id`) ON DELETE CASCADE
) 

在記錄表中,“params_temp”包含稍後將導入“params”表的參數。這通過遍歷“records”表中新添加的記錄並使用“records.id”和“records.params_temp”創建新文件到 INLINE 來實現。“records.params_temp”將包含帶有參數數組的 json 數據。

json數據範例:

[{"parameter":"x","value":"27.33"},{"parameter":"y","value":"57.78"},{"parameter":"z","value":"5.105639E-06"}]

我使用 PHP 生成的文件範例以通過 INLINE 導入

對於記錄:

對於參數(record_id、參數、值):

"19578952","x","-39.338954925537";
"19578954","y","-39.187381744385";
"19578956","z","-39.261814117432";

現有表的更長版本

`measurements`:
- id int(10)
- file_id int(10)
- band enum(with 3 options)
- interval enum(with 2 options)
- frequency enum(with 3 options)
- weight enum(with 3 options)
- measurment_number enum(with 2 options)
- created_at (timestamp) (thinking to remove this)
- updated_at (timestamp) (thinking to remove this)
- owner_id int(10) (storing owner id for quick export/import of data belonging to the owner) 

`records`
- id int(10)
- measurement_id int(10)
- no int(11)
- date date
- time time
- ident varchar(255)
- created_at (timestamp) (thinking to remove this)
- updated_at (timestamp) (thinking to remove this)
- owner_id int(10) (storing owner id for quick export/import of data belonging to the owner)
- params_titles (text) (used to populate params tables)
- params_temp (text) (used to populate params tables)

`params`
- id int(10)
- record_id int(10)
- parameter varchar(255)
- value double(8,2)
- frequency varchar(255)
- created_at (thinking to remove this)
- updated_at (thinking to remove this)
- owner_id  (storing owner id for quick export/import of data belonging to the owner)

甚至在LOAD DATA語句中也有可能轉換24/01/2017,17:06為 MySQL DATETIME(or TIMESTAMPor INT);文件中有一些很好的例子。但是,如果日期格式錯誤怎麼辦?那你想做什麼?

在這種情況下,最好將數據放入VARCHAR; 將有效行移動到目標表,然後發送一封關於其餘行的電子郵件?

那麼,你想0.123,0.43,1.23,4,4.43,0.12變成行嗎?每次測量一行?這將需要在“粉碎”之前將數據放入 tmp talbe;LOAD DATA無法處理。這可能是,也可能不是最好的舉措。後續SELECTs會是什麼?

由於您談論的是大型數據集,因此您需要保持較小的磁碟佔用空間(以提高速度)。我質疑id許多桌子上的必要性。你有“自然”PRIMARY KEY嗎?如果是這樣,請考慮放棄id.

我認為真正的問題是關於終極性能,而不是關於教科書 NF。(當然,有一些重疊。)

安排提示

不要在DOUBLE: 上使用 (m,n) DOUBLE(8,2)。這導致第二次舍入。切換到DECIMAL(8,2)或使用FLOAT/ DOUBLEFLOAT需要 4 個字節(而 8 個字節DOUBLE)並提供大約 7 個有效數字,可能足以滿足您的測量要求?

提示(根據經驗):從長遠來看,您很可能會更喜歡單列DATETIME而不是兩列(DATETIME)的笨拙。

一個字節的 ENUM 被一個 4 字節的 INT 取代?標準化小列沒有優勢。2 值列上的索引很可能永遠不會被使用。

請詳細說明使用params; 它聞起來像“過度標準化”。

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