Posts Tagged ‘PostgreSQL’

    はてなブックマーク - Postgres generate_series関数によるデータの生成
    このエントリーをはてなブックマークに追加

    はじめましてこんにちは、明日(12/4)結婚式の安元です。

    弊社期待の新人の活躍によりクジを引き当て、

    結婚式の前日にAdvent Calendar2010に寄稿することになりました。

    明日の結婚式でいきなりスピーチさせてやろうかと思います。。。。

    というわけで、本日はPostgres generate_series関数のご紹介です。

    集合を返す関数 generate_seriesについて

    さて、Postgres8系からgenerate_seriesという関数が使えるようになっております。
    何をする関数か。以下をごらんいただければ一目瞭然

    
    SELECT * FROM generate_series(1,5);
    
     generate_series
    -----------------
                   1
                   2
                   3
                   4
                   5
    (5 rows)
    


    このように連番を生成してくれます。

    第3引数を渡すことで、刻み(増分値)の変更も出来ます

    
     SELECT * FROM generate_series(1,10,2);
     generate_series
    -----------------
                   1
                   3
                   5
                   7
                   9
    

    日付計算を行うことで、カレンダーも生成出来ます

    
    SELECT
        date('2010-12-1') + a::int as day
    FROM
        generate_series(0,5) as a
        day
    ------------
     2010-12-01
     2010-12-02
     2010-12-03
     2010-12-04
     2010-12-05
     2010-12-06
    (6 rows)
    

    generate_seriesを利用した集計

    さて、「連番が作れるのはわかった。それの何がうれしいの?」 という方もいらっしゃるかと思います。
    DBは、データの集計は得意ですが、そこに存在しないデータは集計することは出来ません。
    しかし、generate_seriesを利用すし、集計のキーとなる値を生成することで、「0」という集計を行えるのです。

    以下のようなデータを日付ごとに合計します

    
     id |    days    | customer
    ----+------------+----------
      1 | 2010-12-01 |       10
      2 | 2010-12-01 |       15
      3 | 2010-12-03 |       14
      4 | 2010-12-03 |       13
      5 | 2010-12-03 |        8
      6 | 2010-12-04 |       12
    
    (5 rows)
    
    SELECT days , sum(customer) FROM customer GROUP BY days ORDER BY days;
        days    | sum
    ------------+-----
     2010-12-01 |  25
     2010-12-03 |  35
     2010-12-04 |  12
    (3 rows)
    

    GROUP BYを使うとそこに存在する日付だけが集計情報として表示されます。

    generate_seriesを使ってみましょう。

    
    SELECT
    
    	generate.days,
    	sum(customer) 
    FROM 
    (
    	SELECT
    		date('2010-12-1') + a::int as days
    	FROM
    		generate_series(0,4) as a
    ) as generate
    LEFT OUTER JOIN
    	customer 
    ON ( generate.days = customer.days)
    GROUP BY generate.days 
    ORDER BY generate.days;
    
    
        days    | sum
    ------------+-----
     2010-12-01 |  25
     2010-12-02 |
     2010-12-03 |  35
     2010-12-04 |  12
     2010-12-05 |
    (5 rows)
    
    

    このままでは、「0」がとれませんのでcoalesce関数でもう一工夫

    
    SELECT
    
    	generate.days,
    	coalesce(sum(customer) ,0) --←変更してのはココ
    FROM 
    (
    	SELECT
    		date('2010-12-1') + a::int as days
    	FROM
    		generate_series(0,4) as a
    ) as generate
    LEFT OUTER JOIN
    	customer 
    ON ( generate.days = customer.days)
    GROUP BY generate.days 
    ORDER BY generate.days;
    
    
        days    | sum
    ------------+-----
     2010-12-01 |  25
     2010-12-02 |  0
     2010-12-03 |  35
     2010-12-04 |  12
     2010-12-05 |  0
    (5 rows)
    

    これで日付ごとの集計がそろいました。

    プログラムで、IF分岐でも出来る処理ですが、ビジネスロジックが複雑な場合など
    集計処理の一環として「存在しないデータを作る」とコードがすっきりすることもありますよ。

      はてなブックマーク - PostgreSQL の VACUUM をなんとなくでするのはやめよう
      このエントリーをはてなブックマークに追加

      はじめての人もそうでない人もはじめまして。 河野と申します。

      いきなりすいません・・・。

      私の名前をさっそく覚えていただいた方には申し訳ないのですが、
      弊社にはもう一人河野というものがおり、そっちとは違う方と覚えて頂けると溜飲が下がります…。

      さて、今回 Fusic Advent Calendar の一番槍として最初に寄稿させて頂くことになりました。

      本日はお題の通り PostgreSQL の VACUUM をなんとなくでするのはやめようという提案を、全国 4,000万の VACUUM ファンの皆様にしたいと思います。

      尚、PostgreSQLの対応バージョンは 8.3 以降となります。

      PostgreSQL のメンテナンスと VACUUM

      データベースに PostgreSQL を採用している会社はどのくらいあるのでしょう?
      オープンソースのデータベースでは MySQL が多く採用されているイメージがありますが、弊社では以前より PostgreSQL を使用しシステム開発を行なっています。

      そんなこんなで PostgreSQL のメンテナンス業務などで VACUUM を実行する場面が私は何度かあるのですが、同じようにメンテナンスをされてる皆様は漫然と VACUUM を実行していませんでしょうか?

      VACUUM とは不要になった領域の回収を行うコマンドです。
      PostgreSQL は追記型のデータベースのためレコードの削除時はもちろん、更新時にも不要な領域が溜まっていきます。
      不要な領域が溜まっていくと、データベースのパフォーマンスが落ちていきます。
      そうなる前に PostgreSQL 好きの皆さんは VACUUM を実行してパフォーマンスをあげたいなと思いますよね?

      でも、ちょっと待って!
      あなたが VACUUM を実行するその時、それが VACUUM のタイミングなのでしょうか?

      VACUUM 前にチェック! 不要領域はどれぐらいあるの?

      PostgreSQL 8.3 からは、不要になった領域をSQLで確認することが可能となりました。
      ここでは pgAdmin を使って不要な領域、デッドタプルを確認したいと思います。

      データベースに接続して…テーブルを選んで…統計情報タブを選択する…以上。

      簡単ですね。 これで不要な行がどの程度あるのか確認することが可能です。
      23699行のデッドタプルがあることが分かりました。

      このテーブルの有効タプルが 454340行 そのうち 23699 行が不要なデッドタプルです。
      有効タプルに対して大体 5% が不要な領域として使用されている事がわかります。

      ちなみにSQLではこのように…。

      
      SELECT
          relname, n_live_tup, n_dead_tup, round(n_dead_tup*100/n_live_tup,2) AS ratio
      FROM
          pg_stat_user_tables WHERE relname = 'テーブル名';
      
       relname | n_live_tup | n_dead_tup | ratio
      ------------+------------+------------+-------
       テーブル名 |     454340 |      23699 |  5.00
      

      実際に確認してみますと、更新の多いテーブルほど n_dead_tup デッドタプルが多いことが分かるかと思います。
      そのデッドタプルが多く有効なレコードに対する割合の多いテーブルほど VACUUM すべきなのです!

      VACUUM を行ないましょう

      5%程でしたら VACUUM を実行するほどではないかもしれませんw
      (統計情報の更新や btree インデックスの不要領域の回収も行なうため実行した方がよいです。)
      むしろこのエントリーの本題は先ほどの”確認する”という部分なので、後はおまけみたいなものなのですが…
      VACUUM を実行してすっきりしたテーブルを見て心までまですっきりするために VACUUM を行なってみましょう。

      ここでも pgAdmin を利用して VACUUM を実行してみましょう。

      テーブルを右クリックしてメンテナンスを選択…VACUUMにラジオのチェックが入っているのを確認したらOKボタン…以上。

      SQLでは以下のようになります。

      VACUUM テーブル名;

      簡単ですね?
      デッドタプルが無くなり不要領域が回収されています。

      VACUUM をなんとなくでするのはやめよう

      如何でしたでしょうか?
      不要領域を回収するから、パフォーマンスが回復するからという理由で漫然と行なってきた VACUUM。

      これからは VACUUM を実行する前に、デッドタプルがあるか確認してください。
      そしてデッドタプルが多く、有効なレコードに対するデッドタプルの割合の多いテーブルに VACUUM をするようにしてみてください。

      このようなデータベースの状況の確認・把握するのは管理・メンテナンスを行なう者にとって重要なことです。
      なんとなく行なってきた VACUUM が明日からは ハッキリ とした理由で行なうことができます。

      VACUUM ファンの皆様なら分かってくれますよね?