4
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

Goでもpandasっぽいことできる! DB接続編

はじめに

前回では、GoのDataFrameの基本的な扱い方を紹介しました。
今回はDB接続して、DataFrameに入れてみようと思います。
(SQLでやったらとか言う野暮なツッコミは無しで(^人^)オ・ネ・ガ・イ)

LoadStructsでやってみる

一応ハンズラボのアドベントカレンダーなので、DBから東急ハンズの店舗情報を取得します。
LoadStructsが個人的に一番DataFrameを作りやすいのでやってみました。

main.go
package main

import (
    "database/sql"
    "fmt"

    _ "github.com/go-sql-driver/mysql"
    "github.com/kniren/gota/dataframe"
)

// DBから取得するカラムを定義
type Store struct {
    ID      int
    Name    string
    Address string
}

func main() {
    db, err := sql.Open("mysql", "<user>:<password>@tcp(<host>:3306)/<db>")
    if err != nil {
        panic(err.Error())
    }
    defer db.Close()

    rows, err := db.Query("SELECT id, name, address FROM store")
    if err != nil {
        panic(err.Error())
    }
    defer rows.Close()

    user := []Store{}

    for rows.Next() {
        var store Store
        err := rows.Scan(&(store.ID), &(store.Name), &(store.Address))
        if err != nil {
            panic(err.Error())
        }
        user = append(user, store)
    }

    if err := rows.Err(); err != nil {
        panic(err.Error())
    }

    df := dataframe.LoadStructs(user)

    fmt.Println(df)
}
[84x3] DataFrame

    ID    Name     Address
 0: 1     渋谷店      渋谷区宇田川町12-18
 1: 4     江坂店      吹田市豊津町9-40
 2: 5     町田店      町田市原町田6-4-1東急ツインズ イースト 6F・7F
 3: 6     池袋店      豊島区東池袋1-28-10
 4: 7     三宮店      神戸市中央区下山手通2-10-1
 5: 8     横浜店      横浜市西区南幸1-3-1横浜モアーズ 5F〜7F
 6: 9     広島店      広島市中区八丁堀16-10
 7: 10    新宿店      渋谷区千駄ヶ谷5-24-2タイムズスクエアビル 2F〜8F
 8: 11    札幌店      札幌市中央区南一条西6-4-1
 9: 12    心斎橋店     大阪市中央区南船場3-4-12
    ...   ...      ...
    <int> <string> <string>

やっていることはこのような流れ

  1. Structで取得してくるカラムを定義する
  2. DB接続して、クエリを実行しデータを取得(Struct)
  3. データをforで回してスライスにStructを詰める
  4. dataframeLoadStructsにスライスを入れて読ませる

面倒な点

  • Structで取得してくるカラムを定義しないといけない
  • Structを変更したらクエリも変更しないといけない

Structを定義せずにDataFrameを作成する

dataframeLoadStructsを使用しましたが、今回はLoadRecordsを使用してDataFrameを作成します。
LoadRecordsの詳細は前回の記事を見てください。

main.go
package main

import (
    "database/sql"
    "fmt"

    _ "github.com/go-sql-driver/mysql"
    "github.com/thimi0412/gota/dataframe"
)

func main() {
    stores := [][]string{}
    db, err := sql.Open("mysql", "<user>:password@tcp(<host>:3306)/<db>")
    if err != nil {
        panic(err.Error())
    }
    defer db.Close()

        // 追加でurlも選択します
    rows, err := db.Query("SELECT id, name, address, url FROM store")
    if err != nil {
        panic(err.Error())
    }

    // カラムを取得してstoresに詰める
    columns, err := rows.Columns()
    if err != nil {
        panic(err.Error())
    }
    // 一つ目の要素がカラム名となる
    stores = append(stores, columns)

    values := make([]sql.RawBytes, len(columns))
    scanArgs := make([]interface{}, len(values))
    for i := range values {
        scanArgs[i] = &values[i]
    }

    for rows.Next() {
        err = rows.Scan(scanArgs...)
        if err != nil {
            panic(err.Error())
        }

        var value string

        // 取得した値をスライスに詰める
        store := []string{}
        for _, col := range values {
            if col == nil {
                value = "NULL"
            } else {
                value = string(col)
            }
            store = append(store, value)
        }
        // 作成したスライスをスライスに詰める
        stores = append(stores, store)
    }

    df := dataframe.LoadRecords(stores)
    fmt.Println(df)
}
[84x4] DataFrame

    id    name     address         url
 0: 1     渋谷店      渋谷区宇田川町12-18    https://shibuya.tokyu-hands.co.jp/
 1: 4     江坂店      吹田市豊津町9-40      https://esaka.tokyu-hands.co.jp/
 2: 5     町田店      町田市原町田6-4-1東急ツインズ イースト 6F・7F https://machida.tokyu-hands.co.jp/
 3: 6     池袋店      豊島区東池袋1-28-10   https://ikebukuro.tokyu-hands.co.jp/
 4: 7     三宮店      神戸市中央区下山手通2-10-1 https://sannomiya.tokyu-hands.co.jp/
 5: 8     横浜店      横浜市西区南幸1-3-1横浜モアーズ 5F〜7F https://yokohama.tokyu-hands.co.jp/
 6: 9     広島店      広島市中区八丁堀16-10   https://hiroshima.tokyu-hands.co.jp/
 7: 10    新宿店      渋谷区千駄ヶ谷5-24-2タイムズスクエアビル 2F〜8F https://shinjuku.tokyu-hands.co.jp/
 8: 11    札幌店      札幌市中央区南一条西6-4-1 https://sapporo.tokyu-hands.co.jp/
 9: 12    心斎橋店     大阪市中央区南船場3-4-12 https://shinsaibashi.tokyu-hands.co.jp/
    ...   ...      ...             ...
    <int> <string> <string>        <string>

コードが長くなりましたがやっていることは
1. DB接続して、クエリを実行しデータを取得
2. スライスを作成
3. カラムを取得してスライスに入れる(こいつがヘッダ)
4. 選択したカラムの値を入れたスライスを2.で作成したスライスに入れる
5. LoadRecordsに読ませる

手が届かないところ

LoadRecordsは型を指定してDataFrameで読み込めないので、例えばid001だった場合LoadRecordsで読み込むと上の出力結果のようにid1になってしまいます。 
型をしっかりとしてしたい場合はLoadStructをしようした方がいいでしょう。

ハンズラボ Advent Calendar 2018 明日8日目は@naokiurです!

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
4
Help us understand the problem. What are the problem?