選擇適合的資料庫-Choosing The Right Database

這篇文章很棒, 仔細地介紹了資料庫的種類與特性, 給在選擇應用資料庫的朋友們, 有個較為完整的概念與取捨:

https://towardsdatascience.com/choosing-the-right-database-c45cd3a28f77

簡單整理一下重點:

CAP: https://zh.wikipedia.org/wiki/CAP%E5%AE%9A%E7%90%86

在分散式的系統架構下, 不能同時滿足這三個條件的 CAP定理:

  1. Consistency: 一致性, 所有分散的節點都能有一致的資料
  2. Availability: 可用性, 每次查詢皆可取得正確(但不一定最新)的結果.
  3. Partition tolerance: 分區容錯性, 部分節點異常或資料異常, 系統仍能保持運作.

(也請參考這篇: https://ithelp.ithome.com.tw/articles/10158554)

而傳統資料庫管統系統需要能滿足 ACID 的部分如下:

https://zh.wikipedia.org/wiki/ACID

  1. Atomicity: 原子性, 在一個交易內的異動是不會被分割的.
  2. Consistency: 一致性.
  3. Isolation: 獨立性, 在各交易操作的狀況下, 是不會互相影響的.
  4. Durability: 持續性, 在未異動的資料, 是能持續保持不變的.

傳統 ACID 在現代資料庫是無法同時滿足的, 所以在分散架構下的 CAP 是另一個檢視要件, 主要應用在 NoSQL 環境.

另外各式資料庫的特性如同原文介紹, 有 key-value, document, graph 不同的資料存放型態.

而其中最重要的部分是應用情境的問題:

  • How many relationships are in your data?
  • What is the level of complexity in your data?
  • How often do the data change?
  • How often does your application query the data?
  • How often does your application query the relationship underlying the data?
  • How often do your users update the data?
  • How often do your users update the logic in the data?
  • How critical is your Application in a disaster scenario?

若是能妥善理解與處理這些可能的問題, 就能找到適合的資料庫, 而實務上也是有可能互相混搭應用的, 區分為不同的資料庫應用方式, 但更重要的是適當的搭配與選擇.

繼續閱讀:
https://blog.longwin.com.tw/2013/03/nosql-db-choose-cap-theorem-2013/

SQL Fiddle好用線上資料庫語法測試工具

線上的前端有好用的 jsfiddle (https://jsfiddle.net/), CodePen (http://codepen.io/), 今天發現了一個很棒的資料庫語法測試工具, SQL Fiddle, 網址如下:

http://sqlfiddle.com/

這個好用的工具分為左右兩側, 分別是 DDL,DML 與 SQL 兩塊, 左側的 DDL, DML 可以直接定義資料表與資料內容, 當然, 還是用標準的資料庫語法. 右邊則是於 DDL, DML 執行之後, 才能使用的 SQL 語法查詢與測試用.

這樣不僅能快速定義資料表供應用外, 最重要的是可以測試 SQL 的查詢語法. 從左上角 Menu 可以看到支援的資料庫有:

MySQL 5.6
MySQL 5.5
Oracle 11g R2
PostgreSQL 9.3
SQLite (WebSQL)
SQLite (SQL.js)
MS SQL Server 2014
MS SQL Server 2008

8種不同資料庫與版本, 十分方便. 另外於 DDL/DML的部分, 也可以直接用上方 Text To DDL 進行設定, 更加方便, 讓不會使用 DDL/DML 的人員, 也能無痛建立資料表, 例如:

table name: t_staff,
data:
name, age, title
Mary, 25, manager
Charlie, 18, Clerk
Fiona, 19, Clerk

(第一列為欄位名, 之後為資料)

就會生出以下語法:

接下來, 就可以在右側進行查詢, 如:

select * from t_staff where age > 20

就可以查出對應的資料了, 十分方便, 尤其對於要查找資料時的不同資料庫語法間的應用, 都有很大的幫助. 另外已完成的結果, 還能快速分享, 對於查找問題, 有更多可以協同作業的可能, 上面的範例如下:

http://sqlfiddle.com/#!9/18a360/1

SQL Join語法圖示

這篇主要目的是介紹集合, 使用 SQL Join 指令時的語法與集合的關係, 資料可以參考這篇: http://www.codeproject.com/Articles/33052/Visual-Representation-of-SQL-Joins

(以上圖片引用自: http://www.codeproject.com/Articles/33052/Visual-Representation-of-SQL-Joins )

在資料庫查詢時, 使用 Join 語法是用來查詢多個資料表間的相關資料狀況用的, 區分為 inner join 及 outer join 兩種, 不指定時預設為 inner join, 也就是在兩邊都存在的資料, 使用 inner join 查詢, 例如 select a.sid from table_a a inner join table_b b on a.sid=b.sid 時, 會出現同時在 a 與 b 的 sid 都存在的資料.

例如 table_a 存在 1, 2, 3, 5, 6, 7, 10, 11 共八筆資料, 而 table_b 存在 2, 3, 4, 5, 6, 7, 8 共七筆資料時, 使用 inner join 則取出 2, 3, 5, 6, 7 共五筆資料.

而 left join (也就是 left outer join), 則是取出以左邊為主, 不管右邊是否存在的資料, (不存在的資料會用 null 值補齊欄位), 如:

select a.sid from table_a a left join table_b b on a.sid=b.sid

則會取出 1, 2, 3, 4, 5, 7, 10 , 11 共八筆資料,

而 right join (也就是 right outer join), 則是取出以右邊為主, 不管左邊是否存在的資料, 如:

select a.sid from table_a a right join table_b b on a.sid=b.sid

則會取出 2, 3, 4, 5, 6, 7, 8 共七筆資料.

另外還有 full join (也就是 full outer join)則是兩邊都取出來, 如:

select a.sid, b.sid from table_a a full join table_b b on a.sid=b.sid

則會取出兩個表全部的內容, 1, 2, 3, 5, 6, 7, 10, 11, 4, 8 共十筆資料,

其他若是需要做其他的集合, 只需要再加上 where 中的 is null 就可以產生差集這樣的方式.

繼續閱讀:
http://www.codeproject.com/Articles/33052/Visual-Representation-of-SQL-Joins
http://blog.wu-boy.com/2009/01/mysqlleft-right-inner-outer-join-%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95/

[MSSQL]文字民國日期轉西元日期

剛朋友問到有關存在 MSSQL 中的文字資料是民國日期, 例如 67/3/12 這樣的格式, 要轉出西元日期, 其實還蠻單純的, 只需要找出第一個 ‘/’ 在什麼位置, 就很容易了, 查找 ‘/’ 的函數使用 charindex, 如下:

declare @a varchar(20)
select @a = '67/6/7'
select charindex('/', @a)

這樣取出的值為 3, 也就是第 3個字元, 而下一步就是取出年的部份, 如下:

declare @a varchar(20)
select @a = '67/6/7'
select substring(@a, 1, charindex('/', @a)-1)

這樣取出的值為 67, 於是要轉出西元年其實就是先轉成 int 後, 加上 1911 再轉回字串後, 加回原來的月/日的部分, 如下:

declare @a varchar(20)
select @a = '67/6/7'
select convert(varchar, convert(int, substring(@a, 1, charindex('/', @a)-1))+1911)+ substring(@a, charindex('/', @a), 10)

其中最後一個 substring 中的取出長度 hardcode 寫 10 是一個小技巧, 不用真的算出取出長度, 而固定給一個大於此字串的長度, 就可以取出完整字串, 所以上面的結果就會是 1978/6/7, 而 substring 最後一個參數可以參考線上說明:

http://msdn.microsoft.com/en-us/library/ms187748.aspx

其中的 length_expression 參數的說明, 其中有提到

If the sum of start_expression and length_expression is greater than the number of characters in value_expression, the whole value expression beginning at start_expression is returned.

如此一來, 便可以一次在 sql command 中將原存在資料庫中的民國年日期轉出為西元年, 例如 table:

userid birthday
—————
1 67/3/12
2 79/2/11

語法如下:

select userid, birthday, convert(varchar, convert(int, substring(birthday, 1, charindex('/', birthday)-1))+1911)+ substring(birthday, charindex('/', birthday), 10) as birthday2 from table1

會取出:

userid birthday birthday2
————————
1 67/3/12 1978/3/12
2 79/2/11 1990/2/11

給大家參考!

使用TABLESAMPLE方式取樣

先弄清楚, 這個 TABLESAMPLE 是用來做以”頁(page)”為單位的取樣方式, 和所謂的隨機不同, 不過在超大型資料表上, 若要快速地取出一部分的資料, 可以應用這樣的方式來達成.

支援 MS SQL 2005 以上的版本, 使用方式很單純, 只需要在查詢指令的 table 之後加上 TABLESAMPLE 子句即可, 如下:

TABLESAMPLE [SYSTEM] (sample_number [ PERCENT | ROWS ] )
[ REPEATABLE (repeat_seed) ]

範例如下:
使用 percent百分比:

USE AdventureWorks2008R2 ;
GO
SELECT FirstName, LastName
FROM Person.Person
TABLESAMPLE (10 PERCENT) ;

或是指定筆數:

USE AdventureWorks2008R2 ;
GO
SELECT FirstName, LastName
FROM Person.Person
TABLESAMPLE (100 ROWS) ;

要特別注意的是, 由於是以 page 為單位, 所以取出的資料只是約略的 page 內容物組合而成的, 不過若是對於超大內容的 table 來說, 比較如下兩個指令:

SELECT TOP 100 * FROM TABLE1 ORDER BY NEWID()

SELECT TOP 100 * FROM TABLE1 TABLESAMPLE(1 PERCENT) ORDER BY NEWID()

效率上會有很大的差異(當然是後者會快很多), 不過若是有 WHERE 條件下, 就不適合使用 TABLESAMPLE 了, 因為是無法以指定的條件來取出資料, 但仍可以下達 WHERE 子句, 是指取出的 TABLESAMPLE 內容, 再過濾的資料, 如下:

SELECT * FROM TABLE1 TABLESAMPLE(1 PERCENT) WHERE FIELD1 > 5000

記得是先取出 sample data 後再過濾的行為, 所以極有可能沒有資料發生, 使用上要特別留意.

官方資料: http://technet.microsoft.com/zh-tw/library/ms189108.aspx
繼續閱讀: http://sharedderrick.blogspot.com/2011/04/tablesample.html

當identity到達該欄位上限時…

今天在整理資料庫時, 發現有個 table 的 identity 欄位, 即將到達上限囉. 於是便來著手研究一下會發生什麼問題.

LAB 狀況 Microsoft SQL Server 2005, 某 table 的 identity 欄位為 tinyint, 並進行 insert 測試.

狀況 1, tinyint 為 (0~255), 若於資料已屆 255 時, 再 insert 資料會發生下面錯誤 (exception):

“轉換 IDENTITY 到資料類型 tinyint 時發生算術溢位錯誤。
發生算術溢位。”

狀況 2, 先將前面 < 100 的資料刪除, 再利用 dbcc checkident(‘table_name’, RESEED, 20) 指令執行後, 重置 identity 值 (下一筆會由 21 開始), 再進行 insert, 則可以正常 insert (dbcc checkindent 資料可以看這篇文章: SQL Server的Identity欄位使用/複製/重設 )

狀況 3, 繼續再進行 insert, 直到 99 時, 再繼續 insert 會發生 primary key violation 的錯誤.

由以上狀況可知, 一旦發生 identity 到該欄位上限時, 將會發生無法寫入資料的狀況, 而且是以算術溢位錯誤的方式發生, 一旦造成, 會有極嚴重的問題, 一定得在發生之前規劃好, 以免造成問題!

SQL 2005的HashBytes function (MD5, SHA1)

在 SQL 2005版本中, 已內建了 HashBytes function 用來做 Hash 的功能, 可以參考該 function 的用法: http://msdn.microsoft.com/en-us/library/ms174415.aspx. 不過這個功能在 SQL 2000 的版本內並無內建, 若有需要可以參考 code project 上的 MD5 Hash SQL Server Extended Stored Procedure, 或是使用純 T-SQL 的方式的 MD5 : MD5算法的T-SQL實現(FOR SQL2000) .

使用方式很簡單, 直接呼叫 HashBytes 函數並指定演算法即可, 如下:

select HashBytes('MD5', '1234')

不過傳回的是 varbinary , 若是要取回為字串的型態, 得再用 sys.fn_VarBinToHexStr() 這個 function 來轉換, 如下:

select sys.fn_VarBinToHexStr(HashBytes('MD5', '1234'))

參考文章:
好用的 MS SQL 2005 欄位字串加密應用 ( MD5 與 SHA1 )
SQL Server 数据库中的 MD5 和 SHA1

另外若是要作密碼加密使用的話, 在 SQL 2000 中有內建 PWDCOMPARE, PWDENCRYPT 這些函數可供使用, 可以參考 BlueShop 討論.

如何選出隨機資料

利用 sql command 可以查詢指定的資料, 有時因為功能上的需要, 希望能在 web 頁面上呈現價格最低, 或是到訪最高的資料, 這種取出固定的資料順序, 往往少了一些活發性.

我們可以利用資料庫的函數特性, 讓查詢出來的結果能有不同的順序, 可以增加訪客訪問的興趣. 不過導入又十分容易的方法囉.

MS SQL Server

select * from mydata order by NEWID()

其中 NEWID() 函數, 是取出 GUID, 不過我們是利用這個函數來進行排序的打散. 如此一來, 每次查詢, 就會有不同的隨機效果

用CTE來取出指定筆數 – SQL2005

SQL2005中有個CTE (Common Table Expression)功能十分強大, 可以用來做遞迴式的參考, 藉以達成複雜的查詢邏輯並簡化查詢指令, 是 SQL2005的一個新的重要功能.

在使用 MySQL 時, 可以容易地利用 limit m, n 的語法進行指定筆數的取得, 然而在 SQL Server這裡一直沒有這種功能. 這樣的需求通常是應用在分頁上會用到. 不過SQL Server可以利用 ADO存取方式內的分頁方法來進行, 也就避開了這個問題. 但無論如何, 仍是將所有的資料帶到查詢端(如客戶端或是中間層)後, 再進行分頁, 效能仍然不佳.

方便好用的Database Mail – SQL2005

有在用 SQL 2000的使用者還記得 SQL Mail吧. SQL Mail是利用MAPI的方式進行mail的傳遞, 在實務上往往會使用outlook配合exchange server來進行環境的建立, 也造成在使用SQL Mail前, 要準備好這些環境. 但在一般的應用上, 要使用SQL Mail, 又要有這些環境, 不見得很容易實現. 另外再加上SQL Mail在cluster環境時, 也要注意兩台的MAPI profile的設定, 才不會發生發不出mail的問題.

是的, 你的不方便, MS都知道, 到 SQL 2005後, 你會發現多了一個 Database Mail 的功能, 本篇文章就是來介紹這個方便又好用的 SQL Server發信方式. Database Mail 是基於 SMTP的方式來寄送信件, 所以很容易設定, 不太需要像 MAPI 需要 outlook 介面及 exchange server 配合寄送, 當然還是要建立 profile, 好讓 SQL Server 明白你要發信的帳號. 不過因為是走 SMTP, 所以設定也就容易多了. 而且 Database Mail 是能完全支援 cluster 的(因為是用 SQL Server 的機制, 而非外部的資源).