當第三個 NF 不是最佳解決方案時?(表現)
我有 3 張桌子:
Measurements ('id', 'name') Records ('id', 'measurement_id', 'time', 'number') - Measurement has many records Parameters ('id', 'record_id', 'param', 'value') - Record has many parameters
我遇到的問題是我需要允許最終使用者通過網頁將數據導入數據庫,目前由於:
- 內聯文件 (MySQL)
- 大數據文件 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
為 MySQLDATETIME
(orTIMESTAMP
orINT
);文件中有一些很好的例子。但是,如果日期格式錯誤怎麼辦?那你想做什麼?在這種情況下,最好將數據放入
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
/DOUBLE
。FLOAT
需要 4 個字節(而 8 個字節DOUBLE
)並提供大約 7 個有效數字,可能足以滿足您的測量要求?提示(根據經驗):從長遠來看,您很可能會更喜歡單列
DATETIME
而不是兩列(DATE
和TIME
)的笨拙。一個字節的 ENUM 被一個 4 字節的 INT 取代?標準化小列沒有優勢。2 值列上的索引很可能永遠不會被使用。
請詳細說明使用
params
; 它聞起來像“過度標準化”。