PostgreSQL 9.5 的 GROUPING SETS 以及 CUBE 與 ROLLUP

Zite 上看到的「Postgres finally has CUBE / ROLLUP / GROUPING SETS !」。

直接看 PostgreSQL 的文件「7.2.4. GROUPING SETS, CUBE, and ROLLUP」就可以知道用法:

=> SELECT * FROM items_sold;
 brand | size | sales
-------+------+-------
 Foo   | L    |  10
 Foo   | M    |  20
 Bar   | M    |  15
 Bar   | L    |  5
(4 rows)

=> SELECT brand, size, sum(sales) FROM items_sold GROUP BY GROUPING SETS ((brand), (size), ());
 brand | size | sum
-------+------+-----
 Foo   |      |  30
 Bar   |      |  20
       | L    |  15
       | M    |  35
       |      |  50
(5 rows)

結果就是分次 GROUP BY 的聯集。而 CUBEROLLUP 則是提供列舉的方式。

ROLLUP 的部份:

ROLLUP ( e1, e2, e3, ... )

表示階層式的列舉:

GROUPING SETS (
    ( e1, e2, e3, ... ),
    ...
    ( e1, e2 )
    ( e1 )
    ( )
)

CUBE

CUBE ( a, b, c )

則是表示 power set (所有的組合):

GROUPING SETS (
    ( a, b, c ),
    ( a, b    ),
    ( a,    c ),
    ( a       ),
    (    b, c ),
    (    b    ),
    (       c ),
    (         ),
)

也有更複雜的 CUBE ( (a,b), (c,d) )GROUP BY a, CUBE(b,c), GROUPING SETS ((d), (e)) 可以用,參考文件裡的範例即可 :p

PostgreSQL 9.5 的 UPSERT

在「Upsert Lands in PostgreSQL 9.5 – a First Look」這邊提到了 PostgreSQL 9.5 支援的 UPSERT 操作。

UPSERT 的定義是:

(computing, database) An operation that inserts rows into a database table if they do not already exist, or updates them if they do.

如果不存在就 INSERT,如果存在就 UPDATE,然後取一部分的字變成 UPSERT。由於要偵測「存在」,只能用在有 primary key 或是有 unique 條件時的表格上。

作者給的範例講解了 PostgreSQL 9.5 上的語法:

INSERT INTO products (
    upc,
    title,
    description,
    link)
VALUES (
    123456789,
    ‘Figment #1 of 5’,
    ‘THE NEXT DISNEY ADVENTURE IS HERE - STARRING ONE OF DISNEY'S MOST POPULAR CHARACTERS! ’,
    ‘http://www.amazon.com/dp/B00KGJVRNE?tag=mypred-20'
    )
ON CONFLICT DO UPDATE SET description=excluded.description;

這邊用的是 ON CONLIFCT DO UPDATE SET,在 MySQL 則是用 ON DUPLICATE UPDATE

關於要不要使用 MySQL 這件事情...

前陣子 ant 將在 5xRuby 演講的投影片放出來:「技術演講:淺入淺出 MySQL & PostgreSQL」,另外在 kaif.io 上也有討論:「淺入淺出 MySQL & PostgreSQL // Speaker Deck」。

而國外剛好也有好幾篇文章都在討論 MySQL (InnoDB),其中「how innodb lost its advantage」這篇講到對 InnoDB 的壓縮感到悲觀...

另外 Pinterest 的「Learn to stop using shiny new things and love MySQL」這篇的時間點感覺上就是在回應上面某些想法。

下面是我的整理 (以及想法)。

MySQLPostgreSQL 都是很成熟的 RDBMS。

如果你對其中一種有經驗,那麼就用你熟悉的 RDBMS。如果你對兩者都有經驗,那麼你就憑自己的判斷選擇。

如果都沒有經驗呢?看你身邊的人用什麼就選什麼。

我在 5xRuby 時回答的比較輕鬆,但這是很實際的回答:你既然都不會用這些進階功能,那麼兩套其實對你都差不多。選一個可以問的到答案的就好。

反正真的夠大的時候,拿錢出來總是有方案可以解決問題。初期把力氣花在怎麼搞定產品吧,如果你不熟悉,這通常都不是你在這個時間應該花時間去研究的問題。

2014 年 Turing Award 得主:Michael Stonebraker

今年的 Turing Award 頒給了在資料庫領域上有重要貢獻的 Michael Stonebraker

For fundamental contributions to the concepts and practices underlying modern database systems.

ACM 的頁面上列了不少成就,比較熟的是 PostgreSQL,他是 PostgreSQL 發展重要的關鍵人物:(出自維基百科「PostgreSQL」條目)

PostgreSQL evolved from the Ingres project at the University of California, Berkeley. In 1982 the leader of the Ingres team, Michael Stonebraker, left Berkeley to make a proprietary version of Ingres. He returned to Berkeley in 1985 and started a post-Ingres project to address the problems with contemporary database systems that had become increasingly clear during the early 1980s. The new project, POSTGRES, aimed to add the fewest features needed to completely support types. These features included the ability to define types and to fully describe relationships – something used widely before but maintained entirely by the user.

PostgreSQL 的 command line 工具調整

在「Optimizing Your psql」這篇介紹了 PostgreSQL 的 command line 工具 psql 要怎麼調整讓他變好用。

這篇文章是入門的調整,藉由 $HOME/.psqlrc 裡的幾行設定,就可以讓系統變得好用很多,可以看到最後面給的 .psqlrc 也才八行...

資料結構、RDBMS、ORM

欠了很久的雜記。既然是雜記,只是把一些事情記錄下來,許多句子的主題會跳來跳去,請多見諒。

先解釋標題的三個詞彙。這邊要講的是三種存取資料的方式:

  • 資料結構:直接操作最底層的資料結構。
  • RDBMS (Relational Database Management System,關聯式資料庫):透過 RDBMS 存取資料的方式,在 open source 領域比較常遇到 MySQLPostgreSQL。由於與下面的 ORM 比較,這一條指的是透過 SQL query 去存取資料。
  • ORM (Object-Relational Mapping):透過程式語言的 object 以及 object 之間的關聯性存取資料。

彈性最高、效能也最好的是直接的資料存取,但寫起來也最複雜;而 ORM 大致上就是反過來。

現代的 RDBMS 大多都有實做 ACID,在自己操作資料結構時考慮這塊會比較辛苦。兩個層級之間有一些 library 試著解決這個問題 (像是 BerkeleyDB 或是 LevelDB),不過這篇文章暫時跳過。

MySQL 與其他的 RDBMS 比較起來欠了許多東西,但 High Availability 的成熟度以及效能而成為 open source 的第一選項。而也因為許多人使用,大家都知道 MySQL 的先天限制,也有許多 workaround 出現,所以大多數的狀況下這不是問題。

MySQL 的 InnoDB 其實寫的相當不錯,但 MySQL 的 SQL parser 一直都是 MySQL 的痛處,所以許多人使用 MySQL 時會儘量使用 simple query,而 ORM 的特性剛好可以搭上風。

使用 ORM 時最常見要避免的是 N+1 的問題,其他常見到的問題大多都不是 ORM 專有的。

先整理到這邊。

測試 GitLab 的方式...

除了在「doc/install/installation.md」有手動安裝的方式外,在「GitLab CE Downloads」有提供 Ubuntu 12.04 LTS 64bits 的 deb 檔可以安裝。

官方在「GitLab Omnibus project」提供的安裝方式是:

apt-get install openssh-server postfix
dpkg -i ooxx.deb
gitlab-ctl reconfigure

不過在 Docker 裡面會卡在設定 redis 的部份而無法成功,卡在:

Recipe: gitlab::redis
  * user[gitlab-redis] action create (up to date)
  * directory[/var/log/gitlab/redis] action create (up to date)
  * directory[/var/opt/gitlab/redis] action create (up to date)
  * template[/var/opt/gitlab/redis/redis.conf] action create (up to date)
  * directory[/opt/gitlab/sv/redis] action create (up to date)
  * directory[/opt/gitlab/sv/redis/log] action create (up to date)
  * directory[/opt/gitlab/sv/redis/log/main] action create (up to date)
  * template[/opt/gitlab/sv/redis/run] action create (up to date)
  * template[/opt/gitlab/sv/redis/log/run] action create (up to date)
  * file[/opt/gitlab/sv/redis/down] action delete (up to date)
  * link[/opt/gitlab/init/redis] action create (up to date)
  * link[/opt/gitlab/service/redis] action create (up to date)
  * ruby_block[supervise_redis_sleep] action run

後來是用 Vagrant (跑 VirtualBox) 才測試成功。就可以看到 PostgreSQLRedis 整包都被放到 /opt 下了...

提供給要玩 GitLab 的人參考看看...

UPSERT

維基百科對 UPSERT 的說明:(取自「Merge (SQL)」條目)

A relational database management system uses SQL MERGE (also called upsert) statements to INSERT new records or UPDATE existing records depending on whether or not a condition matches.

MySQL 裡的兩種語法其實就是在實做這個需求:

  • REPLACE INTO ...
  • INSERT INTO ... ON DUPLICATE KEY UPDATE ...

而前者其實是後者的一個特例 (當 INSERT 發現有 dupe key 時把現有的 record 改成與 INSERT 時相同的條件)。

而計數器是後者常見的 case 之一:當 record 不存在的時候塞一筆進去,並且將 counter 設為 1;當 record 存在的時候對 counter 加一更新。像是這樣的 SQL query:

INSERT INTO my_table SET id = ?, num = 1 ON DUPLICATE KEY UPDATE num = num + 1;

由於這是常見的需求,使得這個語法是目前少數 MySQL 比 PostgreSQL 好用的地方。

在「A Case for Upserts」這篇就看到抱怨 PostgreSQL 不實做這個功能...

不過我覺得作者寫得有點誇張,INSERT INTO ... ON DUPLICATE KEY UPDATE ... 應該是可以模擬出來的功能:當 INSERT 失敗後再跑 UPDATE。而 REPLACE INTO ... 是特例,也就當然可以模擬出來。