pagetakaBlog

最近リフォームと鳥取県日南町の記事多め。写真、PC、ネット等の話題も

Go言語…爺の暴走:SQLひとつだけ値取得、Pythonのfetchone()みたいな…一値強力

Go言語、久しぶりに勉強してます。Pythonで実現したスクリプトをGoでもやってみようというタイミングです。で、MySQLにあるデータの取り出しの課題でつまずき、強力な相談相手、Microsoft:Copilotに教えてもらいました。

課題は、Pythonのfetchone()と同様のことをGo言語でやってみようと暴走した顛末です。

こんな感じだとスクリプト動作、確認できるかも。

  • MySQL:フツーの設定で、日付データを持っている
  • VSCode:Go言語が動く
  • スクリプト:MySQLとの接続設定はご自身の環境に変えてください

急所は「db.QueryRow(query, 1, 10).Scan(&a_date)」

Copilotに尋ねたら、Pythonのfetchone()に直接該当する機能はないそうです。「db.QueryRow(query, 1, 10).Scan(&a_date)」とSQL文工夫との合わせ技がピンポイントで値を取得するカギになるそうです。

スクリプトをGo参照、いや御参照くださいませ。

日付は単純に計算可能…でも、東証開場日の日付は、土日休日を除外しなくては。

そもそもですが、単純にN日前の日付を求める、というなら簡単な話のはずです。しかし、東京証券取引所は、土曜、日曜、休日は仕舞ってます。

爺は、朝、東京証券取引所日報(PDF)をDLし解析後MySQLにUPしデータ保管してます。そこに、日付データがあります。

前市場日データが公開された午前8時半過ぎにDLしたあと、同9時に市場が始まるまでに何らかの判断をしたい、という忙しさがあります。すると、全データをあれこれするというのもアレなので、ピンポイントの日付=N日前(市場日)が必要になりました。

import:MySQL関係がPythonとは違う感じ…。

色ついてるところがPythonとの違いを感じてる爺の今日この頃です。

import (
"fmt"
"database/sql"
_ "github.com/go-sql-driver/mysql"

)

importができたら、MySQL使える前提条件を得たことに…。
それで、接続のための書き方もPythonとは違う感じです。
あ~、「db_name」のところは、ご自身のデータベース名に書き換えてくださいね~。

mysqlRoot := "root:@tcp(127.0.0.1:3306)/db_name"
db, err := sql.Open("mysql", mysqlRoot)

急所+α:プレースホルダーで安全に。

Copilotにたびたび大切な箇所だと指摘されたのが、セキュリティ上の問題を抱えないように、というようなことでした。その結果、プレースホルダーを安全に使う、という流れにしました…爺基準で。

具体的には、次の箇所です。

query := "SELECT DISTINCT date FROM `table_name` ORDER BY date DESC LIMIT ? OFFSET ?;"
var a_date string
err := db.QueryRow(query, 1, 10).Scan(&a_date)

LIMIT ? は「db.QueryRow(query, 1, 10)」の「1」が代入されます。つまり、一個だけピックアップする…という限定ができました。このままでは、「どこの一個」ということになるので、「OFFSET ?」のところに「db.QueryRow(query, 1, 10)」の「10」が代入され、先頭から10日遡った位置、という条件が加わりました。この結果、10日前の日付(一個)が取得できる、というコトになりました。

それ以外では、「SELECT DISTINCT date」の「DISTINCT」が仕事してくれてます。他の抽出要件との整合性を保ちつつ、日付を選び出す際の大切な要素条件になりました。東証日報から取得したデータは、日付と銘柄、その他が一行になって爺のMySQLのテーブルに収まっています。結果、同一日付がテーブルにいっぱいあります。それで重複削除「 DISTINCT」が必要になりました。

テーブルに日付を持たせず、他のテーブルに置いて結合参照するなら、単純な日付計算で目的の日付を得ることができますね。爺は、RDBが壊れたときに面倒なので、ひとつのテーブルに全部詰め込んでます…ええ…。

スクリプト、自己責任で。

ということで、N日前の日付を抽出するGo言語で取得するスクリプトが以下の通りです。
ただし、このままでは動きません。一般的な設定にしてますので、ご自身の環境に合わせてください。

package main

import (
	"fmt"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
)

func main() {
	mysqlRoot := "root:@tcp(127.0.0.1:3306)/db_name"
	db, err := sql.Open("mysql", mysqlRoot)
	if err != nil {
		panic(err.Error())
	}
	defer db.Close()	
	a_date, err := get_a_date(db)
	if err != nil {
		panic(err.Error())
	}
	fmt.Println("10市場日前の日付:", a_date)
}

func get_a_date(db *sql.DB) (string, error) {
	query := "SELECT DISTINCT date FROM `table_name` ORDER BY date DESC LIMIT ? OFFSET ?;" 
	var a_date string
	err := db.QueryRow(query, 1, 10).Scan(&a_date)
	if err != nil {
		return "", err
	}
	return a_date, nil
}

一致協力とはならず「一値強力」で処理。

日付データをMySQLから全部取り込んでから、Go言語でN日前の日付を得る方法もありそうです。爺は、今回、SQL文にその設定を埋め込んだ、という作り方にしました。前者が「一致協力」的で、後者が「一値強力」的なのかと…。

どちらの書き方が適しているか爺にはよくわかりませぬ。諸賢はいかがお考えでしょうか。優し目に教えてくださいませ。