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

#はじめに
[前回](https://qiita.com/thimi0412/private/05cff32279973b0d5599)では、GoのDataFrameの基本的な扱い方を紹介しました。
今回はDB接続して、DataFrameに入れてみようと思います。
(SQLでやったらとか言う野暮なツッコミは無しで(^人^)ｵ･ﾈ･ｶﾞ･ｲ)

# LoadStructsでやってみる

一応[ハンズラボのアドベントカレンダー](https://qiita.com/advent-calendar/2018/handslab)なので、DBから東急ハンズの店舗情報を取得します。
`LoadStructs`が個人的に一番`DataFrame`を作りやすいのでやってみました。

```go: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. `dataframe`の`LoadStructs`にスライスを入れて読ませる

## 面倒な点

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

# Structを定義せずにDataFrameを作成する
`dataframe`の`LoadStructs`を使用しましたが、今回は`LoadRecords`を使用して`DataFrame`を作成します。
`LoadRecords`の詳細は[前回の記事](https://qiita.com/thimi0412/private/05cff32279973b0d5599)を見てください。

```go: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`で読み込めないので、例えば`id`が`001`だった場合`LoadRecords`で読み込むと上の出力結果のように`id`が`1`になってしまいます。　
型をしっかりとしてしたい場合は`LoadStruct`をしようした方がいいでしょう。

[ハンズラボ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/handslab) 明日8日目は@naokiurです！