[SQL] 為何應該要使用 “IS NULL" 而不是 “= NULL"?

當我們想要找出某些欄位是 NULL 的資料…

做為資料工作者,在處理資料的時候經常會遇到所謂的空值「NULL」,也常看到這樣的值被存在資料庫的欄位中,當我們使用 SQL 想要找出某些欄位中是空值的資料,就會使用像下方這樣的 WHERE 去做篩選,但這樣其實無法得到我們想要的結果。

SELECT 
    column_1,
    column_2
FROM `my_table`
WHERE column_1 = NULL
閱讀更多»

【MongoDB 最佳實踐規範】❶ Data Modeling 篇: 資料的儲存該如何設計

  1. MongoDB 和一般關聯式資料庫 (RDBMS) 的差異
  2. MongoDB Best Practices 最佳實踐規範
  3. Data Modeling
    1. 1. 會一起被使用的數據就放在一起
    2. 2. 避免儲存較大的陣列
    3. 3. 某些情況下,建議將數據放在不同文件
    4. 4. 跨文件存放相同的數據可以提高效能
  4. 參考資料

MongoDB 和一般關聯式資料庫 (RDBMS) 的差異

MongoDB 屬於一種非關聯式資料庫,與關聯式資料庫的差異其實網路上已經有非常多的資料,個人覺得主要是 Schema 的彈性不同:MongoDB 可以不用事先定義資料欄位和型態,但像是 MySQL 就得先 Create Table 並定義固定的 schema,若後續有想要更改或是新增欄位就會比較麻煩。

所以如果你的資料隨時間變動的可能性較大、具有較高不確定性的話,MongoDB 就會是很好的選擇!

關於差異也可以參考 AWS 所做的表格

MongoDBMySQL
資料模型將資料儲存在 JSON 文件中
然後將其組織到集合中
將資料儲存在資料欄和資料列中
資料儲存採用表格式和關聯式方式
可擴展性使用複寫和碎片化來水平擴展使用垂直擴展和僅供讀取複本來大規模提升效能
查詢語言使用 MongoDB 查詢語言使用 SQL
效能擅長 INSERT 或 UPDATE 大量記錄SELECT 大量記錄時,MySQL 的速度更快
靈活性沒有結構描述,這點提供更多靈活性
使其能處理非結構化、半結構化和結構化資料
具有剛性結構描述,能有效處理結構化資料
安全性使用 Kerberos、X.509 和 LDAP 憑證來驗證使用者使用內建身分驗證方法
MongoDB vs. MySQL

MongoDB Best Practices 最佳實踐規範

基於以上所述的一些差異,在使用 MongoDB 時的操作就會有很多要注意的地方,這次藉由官方最新發布的 MongoDB Best Practices Guide 來整理一些建議的 Tips。

預計會分成四個主題,包含

  1. Data Modeling
  2. Working With Data
  3. Optimizing Data Access Patterns
  4. Availability and Scaling

Data Modeling

數據建模要考慮的,就是最後將會如何使用這些數據、如何訪問這些數據,來決定怎麼設計資料儲存的形式。

1. 會一起被使用的數據就放在一起

把可能會一起使用的數據放在同一個 document 中,可以大幅提升查詢效能,因為等於直接省去了很多類似關聯式資料庫的 Join。

如下方的 document,user 的 alerts 是直接寫在每個 user 的資料底下,而不是像 MySQL 那樣另外寫一張叫做 alerts 的資料表,這樣在查詢的時候只要查詢 users 這個 collection 就可以直接取得各自的 alerts,完全不需要再另外 Join。

像這樣的 embedded data (subdocuments) 在 MongoDB 是比較建議的做法,因為也可以讓 UPDATE 資料能在單一一個操作中完成。

// db.users
{
     _id: "abc",
     email: "xyz@example.com",
     preferences: {
     alerts:[
         { name: "morning", frequency: "daily", time: { h: 6, m: 0 } }
     ],
     colors: { bg: "#cccccc" }
     }
}

2. 避免儲存較大的陣列

由於 MongoDB 每個 document 的最大儲存上限是 16 MB,所以像是餐廳商店的評論,資料的則數和字數都是未知的,用陣列儲存在同一個餐廳商店資料階層底下,有可能就會占用很大的儲存空間。

因此這類的數據就建議每個評論分成不同的 document,並帶有餐廳商店的 ID 做參照 (如下方範例)

// businesses
{
     _id: "100",
     name: "Bake and Go",
     addresses: [
         { street: "40 Elm", state: "NY" },
         { street: "101 Main St", state: "VT" }
     ]
}

 // businessReviews
{
     _id: "61e706e2450fac1271c9b27a",
     storeId: "100",
     rating: 5,
     comment: "Best bagels in town, a must try if you are in the area."
}
{
     _id: "61e706e2450fac1271c9c522",
     storeId: "100",
     rating: 5,
     comment: "Yummy desserts and a nice place to sit and eat."
}

3. 某些情況下,建議將數據放在不同文件

其實並非所有的 1:1 或 1:many 關聯都可以直接無腦放在同一個文件中,如果有以下狀況的話,就建議將數據放在不同的文件:

  • 在很常需要讀取的文件中,包含了部分幾乎不會用到的數據
  • 文件中部份數據會隨時間更新和成長,但另外一部分卻相對固定不變
  • 文件大小可能會超過 16 MB 上限 (如同前述的第2點)

下方以一個汽車製造商的資料庫為例,汽車車款 (models) 是陣列的形式,但由於車款的資料通常都是個別顯示 (比較不會有同時需要取用多個車款的情況),所以這樣儲存陣列的話,查詢數據後就會需要進行額外的資料處理。

這是錯誤示範:

// manufacturers
{
     _id: "200",
     name: "Swaab Automotive",
     type: "auto",
     models: [
         { name: "Swaab Model X", year: 2022, sku: "SWA-X-22Z"},
         { name: "Swaab Model Y", year: 2022, sku: "SWA-Y-22Z"},
         //...
     ]
}

因此這種情況下,就比較建議將汽車 models 另外獨立成一個 collection,並且帶有製造商 ID 做參照。

而且通常 models 資料的使用也會較獨立,也可能更新比較頻繁,所以把它們拉出來做為另一個 collection 會比較方便讀取和寫入。

正確示範如下:

// models
{
     _id: "1201",
     name: "Swaab Model X",
     year: 2022,
     sku: "SWA-X-22Z",
     manufacturer_id: "200"
}
{
     _id: "1202",
     name: "Swaab Model Y",
     year: 2022,
     sku: "SWA-Y-22Z",
     manufacturer_id: "200"
}

4. 跨文件存放相同的數據可以提高效能

為了提高效能,就要避免額外的資料 JOIN 操作,所以建議可以在不同的文件中存放相同的數據。

如下方範例,在 users, posts 兩個 collections 中都存放相同的使用者 Email 數據:

// users
{
     _id: "1",
     name: { first: "Jane", last: "Doe" }
     email: "jdoe@example.com"
}

// posts
{
     _id: "1055",
     title: "My First Post",
     text: "Hello World",
     userId: "1",
     userEmail: "jdoe@example.com"
}

雖然資料看起來會比較複雜,但 MongoDB 提供了 change streamstriggers,來確保跨文件的重複存放數據都能保持同步更新和一致性。

參考資料

[ ClickHouse ] uniq 和 uniqExact 的比較

建議使用 uniq()

在 ClickHouse 中很常用又很相像的兩個 Aggregate Function: uniq()uniqExact()

其實 uniqExact(x) 就等同 COUNT(DISTINCT x),若是使用 COUNT(DISTINCT x) 會看到欄位名稱直接被轉成 uniqExact(x)

官方也有提醒:「除非一定要最精確的數字,否則建議使用 uniq」,因為 uniq 可以最佳化 memory 的使用。


以下是官方文件的描述:

uniq(x [, …])

Calculates the approximate number of different values of the argument.
計算輸入參數的不重複的大約數量。

參數可以是

  • Tuple
  • Array
  • Date
  • DateTime
  • String
  • numeric types.

Returned value


uniqExact(x [, …])

Calculates the exact number of different argument values.
計算輸入參數的不重複的精確數量。

可接受的參數和回傳的值跟 uniq 是一樣的。


參考資料


歡迎追蹤我的 IG 和 Facebook