ゆるこあプログラミング

新卒社員がプログラミングとたたかう

クエリを考える際の範囲条件と複数等価条件の違い

こんにちは,coreです

indexを使って,テーブルの最適化を行なっているのですが
どうもEXPLAIN結果を見て,「using index」と出ていると

それだけで満足してしまいます_(┐「ε:)_

それではよくない!
ということで,曖昧になりがちな複合インデックスについて.

エントリーのタイトル,自分でもどうつけようか迷ったんですが
とりあえず詰まったので勉強しながら書いていきます…

今回の場面設定
  • テーブルの最適化
  • SELECTで範囲検索を行いたい
  • explainの結果から分からない状況
  • テーブル定義は次の通り
createdb user_data(
    user_id         int unsigned
    age             int unsigned     #20-80歳
    sex             varchar(2)       #M,F
    updatetime      int unsigned     #unixtime
)type=InnoDB;


で,じゃあ今回は上のテーブルから
20代の女の子,テーブル登録が2013年8月に当てはまる行をSELECTします.


その前に

indexはどうやってはりましょうね

とりあえず,user_idは恐らくユーザ固有のもので
他のテーブルにもuser_idがあって,結びつくはずなので

auto_incrementにしているわけではありませんが
user_idはprimary keyにします

mysql> alter table user_data add primary key(user_id);


それから,セカンダリインデックスについては

今回はSELECT文の,WHERE句以降に
user_id以外の全てのカラムが入ってくるので
複合インデックスになることは大体察しがつきます.

ではどういう順番にインデックスを貼ればいいのでしょうか

コメントアウトにあるように,
実際の想定されるデータ値から考えてみます.
---
age -> 20歳から80歳(つまり60通り)
sex -> 女性か男性(2通り)
updatetime -> unixtime
---

あれ.
えーっと,ageとsexについては値が何通りなのか想像出来ますが
updatetimeについては,決め打ちの値が出せません.

このテーブルに登録されている日時は
「いつが最古で,そしていつまでの値が入っているのか」を
明確化していないからですね.


まぁ,なのでとりあえず
「たくさんある」認識で進めていきます.


複合インデックスはindex i1(A,B,C)としたときに
Aの値をソートし,それにあわせてB,Cをおいた状態でindexが作成されます.

だからそれがごちゃごちゃにならないよう
パターンが少ないものからindexをはっていくと 良いような気がします

mysql> alter table user_data add index i1(sex, age, updatetime);


で SELECT文です.
とりあえず素直に書いてみました.

mysql> SELECT user_id from user_data
     -> WHERE sex = 'F'
     -> AND age BETWEEN 20 AND 29
     -> AND updatetime BETWEEN 1375282800 AND 1377961199;

ところで
MySQLは,範囲条件が2つある場合
その片方でしか綺麗に取ってくることができません.

なので,少し格好悪いかたちになりますが
ageは全てIN句で指定します.

mysql> SELECT user_id from user_data
     -> WHERE sex = 'F'
     -> AND age IN(20,21,22,23,24,25,26,27,28,29)
     -> AND updatetime BETWEEN 1375282800 AND 1377961199;


先ほどあげたクエリと,このクエリ
EXPLAINでみたときに,type=rangeと表示され
ともに範囲クエリであるように思えます.

ただ前者は,カラムageにおいて「値の範囲」を条件とし
後者は「値の等価」を条件としてます.

範囲条件の場合,MySQLはindexの他の列を考慮しませんが
等価条件の場合,そういった制限がありません.


ついつい忘れがちです.


IN句で書ききれないときは,

テーブル設計を見なおしたり
検索条件そのものの方針が誤っていたり

…っていう可能性が
高いのではないでしょうか.

MySQLにおけるベンチマークのなんたるかを考える必要性に陥った

MySQLのパフォーマンスを向上させる

MySQLやその他RDBMSについて深い理解があるわけじゃないけど
データベースから欲しい値を取り出すまでの時間は
そりゃあやっぱり短いほうがいいわけで。


ということで,データが1万行を超えてくる頃から
パフォーマンスについて考える必要性が出てくるわけです。


どこがボトルネックなのか


_(┐「ε:)_?

ベンチマークとプロファイリング

このふたつは,ボトルネックを探す際によく用いられますが目的が違います.

  • ベンチマーク
    • システムのパフォーマンスを調べる
  • プロファイリング
    • 今のパフォーマンスなのは何故か調べる

ということで,では
MySQL自体のパフォーマンスを向上させるためには

ベンチマークを行った上でプロファイリングをする


ということで良さそうです.


続きはまた書きましょうかね

PerlでPlease check that your locale settingsという警告が出たときの対処

perlでコンパイルしたときに,

perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
	LANGUAGE = (unset),
	LC_ALL = (unset),
	LANG = "ja_JP.eucJP"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").

…というエラーが出て来ました.

こまった(●☌◡☌●)


コンパイルは普通に通るのですが,
のちのち何かで困りそうなので対処します.

エラーメッセージにあるように,ロケールの設定がおかしいので
/etc/sysconfig/i18nの中身を次のように変更します.

LANG="ja_JP.UTF-8"
LANG="ja_JP.eucJP"
SUPPORTED="ja_JP.eucJP:ja_JP:ja"

いったんログインユーザからrootにログアウトし,
再ログインを行えばエラーはでなくなります.


今回はeuc-jpでしたが,utf-8など他のエンコードであっても
環境に合わせて変えることで対処できそうです.

Linuxに管理者権限を持った新規ユーザの作成

※!suではなくsu -でrootユーザにしないと
  環境によってはコマンドがnot foundと言われてしまいます!※

以下の例では,作成するユーザ名をcoreにしています(*´ェ`*)

[root@localhost ~]# useradd core
useradd: user core exists
[root@localhost ~]# passwd core
Changing password for user core.
New UNIX password: 
Retype new UNIX password: 
passwd: all authentication tokens updated successfully.

wheelグループに追加し,sudoできるようにします

[root@localhost ~]# usermod -G wheel core
[root@localhost ~]# visudo

以下の行を「/wheel」などのviコマンドで検索し,コメントアウトを外します.

## Same thing without a password
%wheel ALL=(ALL)       NOPASSWD: ALL

実際に作成したユーザでログインし,適当なコマンドを打って通ったら成功です.
以下の例では日時を出しています.

[root@localhost ~]# su core
[core@localhost root]$ sudo date
[sudo] password for core: 
Thu Jun 27 15:21:41 JST 2013

.vimrcの設定をメモしておく

今のところこんなかんじです

set tabstop=4
set number
set fileencodings=utf8,iso-2002-jp,cp932,euc-jp
set autoindent
set shiftwidth=4
set smartindent
set cindent
"when open the file, show the status.
set laststatus=2
set statusline=%F%m%r%h%w\%=[TYPE=%Y]\[FORMAT=%{&ff}]\[ENC=%{&fileencoding}]\[LOW=%l%L]
set t_Co=256
"colorscheme desert
set nocompatible
set whichwrap=b,s,h,l,<,>,[,]
set backspace=indent,eol,start
syntax on

githubでpush時にrejectされてしまうあれ

gitがうまく使いこなせない

pushすると次のようなメッセージがくる

To https://github.com/0x0core
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'https://github.com/0x0core'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

これとかこれとか試してみたんだけれど
よくわからなくなって結局繰り返し

(´;ω;`)


とりあえずローカルのリポジトリの.gitを削除して
いちからやることに


# git init
Initialized empty Git repository in ***/.git/
# git add .
# git status
# git remote add origin https://github.com/0x0core
# git push origin master


…と,ここで上記エラー.
何が違うんだろうか…

fetchrow_hashref()でDBIのvalueだけ取り出したい

件名そのまんまです
未解決なのですが同じ事を何度もやらないように…

環境:OS->CentOS 5.9 / MySQL-> 5.0

【目的】
Perl内でSQL文を実行
その際にSELECTを用いた場合,結果の取得の手法

DBIだしデータ量が膨大なのでfetchrow_hashref()を使う



ほにゃ_(┐「ε:)_

とりあえずプログラムの一部を載せます

my $dbh = DBI->connect($ds,$user,$pass,$option)or die("connection failed.");
my $sth= $dbh->prepare("SELECT * FROM table_name ORDER BY columns" );
$sth->execute();

my @file_data; #格納用
while(my $row = $sth->fetchrow_hashref()){
  $printer=%$row;
  push(@file_data,%$row,"---<br>");
}
$dbh->disconnect();


テーブルの中からすべてのVALUEを取得したいんだけれど
このままだと(おそらく)ハッシュのKEYまで拾ってきてしまって
情報として整理するときに不便…

フィールド名が何回も登場するんです´v`


なのと,

配列に格納するときに「,」で区切りたいんだけれど
その正規表現をどうやってハッシュに対してやればいいのか


ハッシュとリファレンスの理解の浅さが@ω@



なやむ


未解決〜


追記

結局あきらめた_(┐「ε:)_

正攻法がわからなかったので
フィールド名だけ正規表現で切り出しました(●☌◡☌●)