P学習帳

書いておぼえるブログ

【MySQL】日付を操作したい

状況

指定範囲の期間でレコードを絞り込みたい。たとえば、きのう、先週、先月。

解決

日付操作用の関数を組み合わせる。 DATE_SUBYEARWEEKDATE_FORMATなどなど。

コード

CREATE TABLE articles (
  id int,
  title varchar,
  created_at date
)

本日から8〜14日の間に作成されたレコードを取得する。

SELECT *
FROM articles
WHERE date = BETWEEN DATE_SUB( CURRENT_DATE(), interval 14day ) AND DATE_SUB( CURRENT_DATE(), interval 8 day ); 

あるいは週番号を西暦につなげて返すYEARWEEKをつかえばもっと簡単だ。

SELECT *
FROM articles
WHERE YEARWEEK(date) = YEARWEEK( CURRENT_DATE()) -1; 

このときYEARWEEKは直に引き算できる。

SELECT YEARWEEK(CURRENT_DATE())-1;
-- 201826 (2018/07/12時点)

ハマりポイント

除算できない形式に対して除算してしまう。返り値がおかしくなるかエラーになる。

例:yyyy-mm-dd形式の文字列を除算する

SELECT DATE_FORMAT( CURRENT_DATE(), '%Y-%m-%d') -1;
-- 2018-07-12 ではなく 2017が返ってくる!!!

だが次の場合は期待する結果が得られる。日付フォーマットからハイフンの区切りを省いた。

SELECT DATE_FORMAT( CURRENT_DATE(), '%Y%m%d') -1;
-- 20180712 - 1 => 20180711  (^^)

さらに年月も計算できる。

除算

SELECT DATE_FORMAT( CURRENT_DATE(), '%Y%m') -1;
-- 201807 - 1 => 201806  (^^)

加算

SELECT DATE_FORMAT( CURRENT_DATE(), '%Y%m') +1;
-- 201807 + 1 => 201807

雑感

うっかり文字列の日付を直に演算してバグらせてしまった。何がおかしいのかしばらくわからなかった。正しいと思っているコードのおかしい箇所に気づくのはむずかしい。

【Ruby on Rails】安全に検索語を指定してレコードを取得したい

状況  

Active RecordsのWhereに動的に文字列を渡してレコードを取得したい。クエリエラーになる文字列が含まれるかもしれないので、エスケープしてから渡さなければならない。   

解決  

inspectする。 エスケープが必要な文字をそれしてくれる。 

コード  

lines.each do |line|
  Table.where(content: line.inspect)
  # do stuff
end

雑感  

シングルクオートは意外といろんなところに含まれている。エスケープ忘れていてクエリのエラーがでるとびっくりするのでちゃんとサニタイズする習慣をつけたい。

【Ruby】文字列からURLを抽出する

状況  

平文に含まれるURLを抽出したい。関係ない文字は省きたい。  

解決  

URI.extractを使う。  

コード

require 'open-uri'

urls = []
Articles.all.each do |article|
  urls << URI.extract(article, ["http", "https"])
End

雑感  

これはとても便利なメソッドで、ふつうの文章に紛れ込んだURLをしっかりと抜き出してくれる。クローラー作るときに便利に使える。

【Ruby on Rails】DBからユニークなデータを配列で取りたい

状況  

テーブルの任意のカラムから重複を省いた要素を配列で取り出したい。  

解決  

pluck()してcompact.uniqする。  

コード  

ids = Music.where(is_favorite: true).pluck(:id).compact.uniq

雑感  

あえてidを配列で取得してからActive Recordsに渡して絞りまなければならない場合、こうすればうまいこと一意なidを取り出せる。  

【Ruby】配列または文字列どちらも受け取れる関数を作りたい

状況  

成り行きで関数に文字列と配列の両方が入ってくるコードができあがった。配列が来たらeachで回して要素を処理すればOK、と思っていたら引数が文字列の場合にエラーが出た。両方対応できるようにしたい。

解決

instance_of?(Array)を使って配列を判定する。引数が配列のときにtrueを返す。  

コード  

引数を受けて配列なら要素べつに処理、文字列ならそいつ自身を処理して配列を返す関数にできる。

def do_stuff(chunk)
  ret = [] 
  if chunk.instance_of?(Array) 
    chunk.each do |element|
      # do something
      ret << hogehoge(element)
    end
  else
    ret = fugafuga(chunk)
  end
  return ret.present? ? ret.flatten.compact.uniq : []
end

雑感  

こういう処理は、有名なモジュールのソースコードをみればうまい書き方が学べる気がする。「こういうときはこう」というベストプラクティス的な書き方は知っておくと役に立つ。

あと、配列変数を宣言したあとでarry << ['hoge', 'fuga']とすると、

[['hoge', 'fuga']]

みたいに配列がネストするのでフラットにしてから返している。なんだけど、もっとうまいやり方ありそう。

【Ruby】配列をずっと循環させる

状況  

バッチ処理などで指定時間のあいだずっと配列をぐるぐるループしつづけていたい。  

コード

cycleをつかう。

['apple', 'banana', 'orange', 'peach', 'grape', 'pineapple'].cycle do |fruit|
  p "I like #{fruit}"
end

cycle(3)のように回転数を指定することもできる。このとき3回転する。  

参考  

わかりやすく、詳しい。

[Ruby] cycleメソッド(Array)の使い方 | qs Developers

雑感  

バッチ処理でたくさんあるデータをぐるぐる回していたい。指定の時間が来たらプロセスはいったん終了される。そしてまた再度プロセスが実行される。その繰り返しがつづく。具体的にはループさせたかったのはテーブルのカラム名だった。カラムごとにデータを処理するために、その名前を配列にして循環させたかった。わざわざ循環させたかった理由は、小刻みにでもすべてのカラムのデータを処理していきたかったからだった。

【デバッグあるある】渡しているはずの引数が存在しないことになっている

状況   

関数とかに確実に渡しているはずの引数の値が空(undefined または nil とか)だと怒られる。 呼び出し元では変数に値が入っている。原因がわからず途方にくれる。    

原因

変数に値が入っていると確認した呼び出し元のコードの後続の行のどっかで同じ変数名を空の値で上書きしている。

雑感

該当の変数に値が入っているかどうかだけに注意をうばわれて、他の可能性が目に入らなくなるという現象。