duckdb

duckdbを極める (2) ログ保存

duckdbを使うのならクエリに関するログ情報はしっかり保存しておきたいところ。ログ保存は法律やSOPで規定されているというのもありますが、想定通りの処理が実施できているか確認するためにも重要な要素です。

そこでduckdbのログ情報を保存する方法を検討しました。

duckdbのログ保存

duckdbにも一応ログ保存機能があります。ただし基本的に実行したクエリ、errorやwarningのみが出力されるため、例えば作成したテーブルの情報なんかは出力してくれないっぽいんですよね。そこまで欲しいならやはり自力でコンソール出力するしかなさそうです。

基本的にオープンソース言語はSASよりはログ情報が最低限しか出力してくれないため、SASと同等のログ情報が欲しいのならパッケージを探すか自力で何とかするしかありません。

duckdbの自作ログ出力関数

出力してくれないなら自作すればいい。ということでduckdbのクエリ結果に関する情報をコンソールに出力する関数を作成しました。

「crate or replace table as ..」で作成したテーブルのカラム数とレコード数、クエリに要した時間をコンソールに出力します。さらにconnectionペインの情報もアップデートします。

# create or replace tableで作成したテーブルのレコード数とカラム数をconsoleに出力する
# データベース接続は"con"の名称にする 
que <- function(query){

  start <- proc.time()
  dbExecute(con, query)
  end <- proc.time()

  time <-end - start

  tablenm <-str_match_all(query, "create\\sor\\sreplace\\stable\\s(\\S*)\\sas")[,2]

  for (no in 1:length(tablenm)) {
    no_record=dbGetQuery(con, paste("select count(*) from ", tablenm[no],";", sep=""))
    no_column=dbGetQuery(con, paste("select count(*) from information_schema.columns where table_name='", tablenm[no],"';", sep=""))

    cat("NOTE: the table '", tablenm[no], "' was created.", "\n", sep="")
    cat(
      "      ",
      no_record,
      " records, ",
      no_column,
      " columns",
      "\n")
  }

  cat('NOTE: Query process time', "\n")
  cat("      User CPU time:   ", round(time,2), "sec", "\n")
  cat("      system CPU time: ", round(time,2), "sec","\n")
  cat("      elapsed time:    ", round(time,2), "sec","\n") 
  cat("","\n")
  connections::connection_update(con)
}

 

使用方法は関数の引数にクエリの文字列を指定するだけです。実行するとRのコンソールに作成したテーブルのレコード数、カラム数、クエリ実行時間が表示されます。

コンソールに出力できればあとはRのlogrxパッケージを用いれば各種情報とセットにしてテキストとして保存できます。超便利

que("create or replace table test as select * from raw;" )
コンソール出力結果コンソール出力結果

 

ちなみにクエリ内で複数のテーブルを作成した場合、作成したテーブルすべての情報を出力できます。例えば2つテーブルを出力した場合、それぞれのテーブルの情報を出力してくれます。

複数のテーブルを作成するクエリを実行した場合、すべてのテーブルの情報を出力します。2つのテーブルを作成するクエリを実行した結果

 

注意点としてはあまりに長いクエリを実行した場合、errorが発生した時にエラーの文字数上限に引っかかってしまう可能性があります。options(warning.length)を8170に設定するか、クエリを一つにまとめずに分割して実行すると良いと思います。

私はテーブルを作成する毎にこの関数を実行することにしています。またこの関数だとサブクエリ内の情報は取得できないので、サブクエリも一つのテーブルとして作成したほうが良いかもしれません。