やる気がストロングZERO

やる気のストロングスタイル

【Toggl track】と【todoist】と【Anki】で仕事を補助するとなかなか良い

最近仕事で以下ツールを使ってみている。

  • Toggl track: タイムトラッキングツール
  • todoist: Todoツール
  • Anki: 記憶補助ツール

まだ本格的に使いだして間があまりないが、今の所凄く良いように感じているのでそれについて書く。

  • toggl trackで自分が何に時間を使っていたかを記録する
  • todoistでやることを全部管理する
  • Anki で業務知識を覚える
  • まとめ
続きを読む

【テーブル設計】削除フラグを使わず削除テーブルを使うべき

データの削除機能において、何らかの理由でデータは残しておきたい場合には「削除フラグ」が使われがちだが、これは絶対にやめたい。
この場合は「削除テーブル」を用意してそちらにデータを移し、元テーブルからはレコード削除を行うようにするべきだと思っている。
それについて書く。

削除フラグを使うと事故りやすい

シチュエーション:販売終了商品が削除フラグ=1でテーブルに残っている

例えばある商品が販売されていたが、今は販売されていない商品が以下のようなテーブルとレコードで表現されているとする。

id 商品名 価格 削除フラグ
1 ガム 100 1
2 キャンディ 120 0

id: 1のガムは今はもう販売されていないので削除フラグが1(true)になっている。
しかし、商品購入履歴などで必要なのでデータはそのまま残っている。

商品テーブルに削除商品があることを常に意識してないとすぐ事故る

「取り扱い商品一覧データ」を取得する際[WHERE 削除フラグ != 1]の記載を忘れるともう事故になる。
これくらいだったら「そんな間違いをする方が悪い」のかもしれないが「常に意識しないといけない」というのが地味に難易度が高い。

  • 「外部の業者とのやりとりで必要なので商品データ一式をcsvでちょうだい」とかパッと言われてクエリを書く時
  • 新機能実装中、ユーザー毎の販売商品数が必要なので商品テーブルをjoinしようとした時
  • 新しくアサインされた新人が商品テーブルを使ったなんらかの集計値を算出する時

どんな状況でも、なにか商品テーブルに関する事を行う時には必ず削除フラグの事を意識しないといけない。
一度でも忘れると、表示されてはいけないデータが表示されたりして事故になる。
なかなか厳しいのではないかと思う。

こんなのがそこかしらのテーブル内に○○フラグ、××フラグといくつもあったりするともうお手上げ。

「削除商品テーブル」を用意して削除時はそちらにデータを移す

テーブル設計

テーブル定義はこんな感じ。頭文字をあわせることで関連したテーブルがテーブルリストで近なるようにしている。

商品全部テーブル(販売履歴など、商品とのリレーションはこのテーブルと行う)

id
1
2

商品販売中テーブル

id 商品名 価格
2 キャンディ 120

商品販売終了テーブル

id 商品名 価格
1 ガム 120

明示的に削除商品テーブルをJOINしない限り削除商品のデータが混じってしまうことはない

基本的に商品販売中テーブルを扱って作業するようになるので、特に気をつけないといけないことはない。
削除した商品のデータが必要な時は明示的に商品販売終了テーブルをJOINせねばならない。 直感的だと思う。

まとめ

そもそもリレーショナルモデルにて集合は「状態」を扱わない。
フラグを持たせて状態を管理しだすとリレーショナルモデルから外れるためDBがカバーしきれなくなりアプリケーション側で面倒を見てやらねばならなくなる、というのが今回の問題の原因である。

だから、削除フラグを使わず削除テーブルを使うべきである。

参考図書)

【Go】Gorpで[where in]をいい感じに書く方法

Gorpを使っていて以下のようなqueryを実行したい時がある。

select * from table where id in (1, 2, 4, 8);

"1,2,4,8"は動的に変えたいのでプレースホルダーにしたい。

select * from table where id in ($1, $2, $3, $4);

今回指定したidは4つだったけど、いつもそうとは限らないので渡す数も動的にしたい。

select * from table where id in ($1);

このqueryで以下のように実行してみるとエラーになる。

query := "select * from table where id in ($1);"
dbmap := &gorp.DbMap{
    Db:              db, // コネクション
    Dialect:         gorp.PostgresDialect{},
}
_, err := dbmap.Select(mapper, query, []int{1,2,4,8})

// エラーが出る
// sql: converting argument $1 type: unsupported type []int, a slice of int

方法:ExpandSliceArgsオプションを有効化してmap[string]interface{}型で値を渡す

以下のように書くと意図通りの挙動になる。

query := "select * from table where id in (:ids);"
dbmap := &gorp.DbMap{
    Db:              db,
    Dialect:         gorp.PostgresDialect{},
    ExpandSliceArgs: true, //スライス引数展開オプションを有効化する 

}
_, err := dbmap.Select(mapper, query, map[string]interface{}{"ids": []int{1,2,4,8}})

内部的には、query文字列を

select * from table where id in (:ids0, :ids1, :ids2, :ids3)

と展開して処理してくれている。

gorpのコードを読んでいてこの機能を知った。
この機能を知る前は、where in () の中を予めwhere in ($1, $2, $3, $4) となるように文字列をいじり、argsも[]interface{}型に変換してからdbMap.Select()に渡していたがめんどくさかった。。

システムドキュメントの管理方法のベストプラクティスを考える

システムドキュメントの管理って地味に難しい。
そもそもドキュメントが無かったり、どこにあるのか分からなかったり、あっても情報が古かったり、複数の場所や形式に分散していたりしていて、いざという時に全然使えないような状態になってしまってたりする。
そこで、これらの問題が起こりにくいドキュメント管理方法を考えてみた。

  • システムドキュメントはシステムのgitリポジトリで管理する
  • AsciiDocで記述する
  • 文書ファイルで管理する運用のデメリット:オリジナルがどれか分からなくなる
  • 外部サービスで管理する運用のデメリット:永続性に懸念がある
  • まとめ
続きを読む

プログラマー的厨二病の症状と処方箋

僕がそうだったのだが、プログラマーって厨二病を患っている時期があると思う。
厨二病の怖い所は、厨二病に侵されていると自分では気がつけない所だ。
だから、もしかしたら今も僕は厨二病なのかもしれないが、自分の過去を振り返って厨二病の症状とそうならないための対策を書く。

  • 発生時期
  • 症状
    • より新しい技術を善とし、古い技術を悪とする
    • 周りの意見(主にネット)を自分の意見とする
  • 原因
    • 周りが見えてない
    • 自分で考えた意見を持ってない
  • 対策
    • 常に自分が厨二病である可能性を疑う
    • 自分で考える

発生時期

いわゆる「完全に理解した」時期に発症しやすい。

続きを読む

【Golang】sql.Open(), Close()を呼ぶタイミング・場所について考えた

GoでDBアクセスする為のサンプルコードを探すとどこもこんな感じ

func main() {
    # dbコネクション(pool)を取得
    pool, err = sql.Open("driver-name", *dsn)
    if err != nil {
        // This will not be a connection error, but a DSN parse error or
        // another initialization error.
        log.Fatal("unable to use data source name", err)
    }
    defer pool.Close()

    # poolを使ってDBアクセスするコード
    ......
}

Openしたコネクションはアプリ終了時にdefer によってClose()処理されて終わる。理解した。

すべてmain()に処理があったり、数個の関数で構成されているアプリケーションだったら上記で良いけど、 クリーンアーキテクチャ的なレイヤー構造を持ったアプリケーションだったらどうすれば良いのか悩んだ。

続きを読む

アプリケーション要件に関わらずテーブルには事実を記録する

「このアプリケーションのこの機能は【年月日】までしか意識しない。【時分秒】はデータとして不要なのでテーブルにはdate型で【年月日】だけ持たせれば良い。」
と言われた事があったが例えアプリケーションにとって不要でもDBテーブルにはdatetimeとかtimestampで【時分秒】まで保持しておくべきだと思ったので書く。

  • DBはアプリケーション要件を意識しないほうがいい
  • 状況例
    • 【時分秒】を持たなかった場合
    • 【時分秒】を持っていた場合
  • まとめ
  • (補足)実際に【年月日】しかないデータはどう扱うか?

DBはアプリケーション要件を意識しないほうがいい

アプリケーションを意識しまくったテーブル設計はアプリケーション要件に依存してしまう。
だからアプリケーション要件が変わるとテーブル設計を変更しないといけなくなる。
この時、既存データとの整合性をとる必要性まで出てきて大変になる。

続きを読む