簡體   English   中英

基本 SQL 設計問題:一個表有很多重復文本,還是兩個單獨的表?

[英]Basic SQL design question: One table with lots of repeated text, or two separate tables?

一個通用的數據庫設計問題:

假設我正在組合一個相當大的數據庫 - 比如說有數萬行,而不是很多列 - 我希望其中一列是一個識別“標簽”,最明顯的是一個 30-50 的長字符串人物。

並且不同標簽的數量將相當有限。

我讀過的關於 SQL 和數據庫設計的每一篇介紹性文章都表明,您應該小心為您的信息選擇正確的數據類型 - 如果您可以通過將其存儲為 integer 來獲得信息,請不要將其存儲為浮點數,不要如果您可以將信息存儲為數字,則將其存儲為文本 - 以防止您的數據庫變得不必要的大。 但我不確定這對性能有多大影響。

在我的特殊情況下,創建第二個表似乎很容易,我的所有英文“標簽”與一些任意的 integer “代碼”配對。 然后,我可以在我的主表中只存儲 integer 代碼,使其更小。 但是,然后,對於用戶從主表訪問他們想要的數據 - 在不知道 integer 代碼並且只知道英文標簽的情況下 - 每個查詢(我認為)必須看起來像:

"SELECT * FROM maintable WHERE code = (SELECT code FROM secondtable WHERE tag = 'English tag')"

這似乎可行,但是每個查詢實際上都是兩個查詢。

那么,通過將英文“標簽”替換為 integer“代碼”來縮小主表是否可能會帶來有意義的性能優勢? 而且,如果是這樣,這種性能改進是否可能超過將查詢數量翻倍的性能成本?

在這個問題上有經驗法則嗎? 一般首選的處理方式? 如果是這樣,我很想在走得太遠之前知道。

在我看來,就維護和可擴展性而言,最好有兩個帶有索引值的單獨表,因此您將有一個 TAG 表,每個表都有索引 ID,而主表僅引用這些 ID。 正如在這個問題中指出的那樣,最好使用 JOIN 來獲取這兩個值,所以你會做這樣的事情:

SELECT
    *
FROM maintable m
JOIN tagtable t ON t.id = m.tag //You can choose also LEFT JOIN
WHERE
    m.tag='english tag'

但最后,這一切都取決於您的具體需求。

讓我們舉一個具體的例子。 問題是考慮兩種方法:

Take1 :只有一個表,直接嵌入(和復制) tag字符串,類似於:

CREATE TABLE take1 (
    id   INTEGER NOT NULL PRIMARY KEY,
    tag  TEXT,
    foo1 INTEGER,
    foo2 INTEGER
);

Take2 :兩個表,其中主表通過 ID 引用tag字符串,類似於:

CREATE TABLE take2 (
    id     INTEGER NOT NULL PRIMARY KEY,
    tag_id INTEGER,
    foo1   INTEGER,
    foo2   INTEGER
);

CREATE TABLE tags (
    tag_id INTEGER NOT NULL PRIMARY KEY,
    tag    TEXT
);

第一條評論: take1使用了一個未標准化的表。 例如,假設您決定編輯其中一個標簽(例如拼寫錯誤)。 使用take1 ,您必須編輯嵌入給定標簽的所有行。 使用take2 ,您只需編輯 1 行。

我用 SQLite 寫了一個小程序來比較兩種方法的性能,假設主表有 80K 行,唯一標簽為 38。

從文件大小的角度來看:

  • take1在 200ms 內生成一個 4.7MB 的數據庫文件。
  • take2在 170ms 內生成一個 1.3MB 的數據庫文件。

從性能上看:

對於take1查詢

select count(*)
from take1
where tag = "consciousness as a paradox.";

需要 8ms

對於take2查詢

select count(*)
from take2, tags
where take2.tag_id = tags.tag_id and
tags.tag = "consciousness as a paradox.";

需要 10 毫秒

但是,想一想,我們也可以寫

select count(*)
from take2
where take2.tag_id = (
  select tag_id from tags where tag = "consciousness as a paradox."
);

而這個需要 5ms。

因此,根據這些微基准, take2不僅從設計角度來看更好,而且從性能角度來看(至少執行上述查詢)。

最后一點:我寫了一個程序,因為我還需要填寫表格,但是如果已經有表格可用,也可以直接使用 sqlite shell 和.timer on來獲取時序信息:

$ sqlite3 take2.db
SQLite version 3.28.0 2019-04-15 14:49:49
sqlite> .timer on
sqlite> select count(*)
  ...> from take2
  ...> where take2.tag_id = (
  ...>   select tag_id from tags where tag = "consciousness as a paradox."
  ...> );
2105
Run Time: real 0.005 user 0.004756 sys 0.000089

(2105來自80K/38)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM