MySQLをスキーマレスDBとして扱うための試行錯誤のログ2
前回の記事で、やりたいことを実現することはできましたが、indexが効かないので全然実用的ではありませんでした。
そこで別の方法を模索してみます。
MySQLはユーザー変数を使った独自の集計(GROPU BY)を行うことでがきます。
ただし、MySQLのクエリー実行の順番を理解していないと作れません。超難解ですがとても複雑なGROUP BYでも結構実装できてしまいます。
SQLをFROM → WHERE → SELECT → GROUP → HAVING といった順番にループするプログラムと考えて、ユーザー変数をそれぞれの箇所で適切に制御することで、アクロバティックで、プログラミングのような処理が可能になります。
尚、WHERE句で(ユーザー変数)*0+1としているのは、ユーザー変数の初期化を行うためだけのもので、WHEREの条件を常に1(真)とする為です。ちなみにcなどのif文中と同じで、AND条件は前から順番に処理され、条件に合致しなくなったらexitするので、ユーザー変数の初期化や制御はWHERE句の前の方に置く必要があります。またindexが使われた場合は、index以外の行は読み込まれません。(ユーザー変数によるカウントは不可、そうでなければユーザー変数で非表示行もカウントが可能です。)
- ユーザー変数を使った独自 GROUP BY
SELECT @num,@cond,@type,@category,@repeat,v.* FROM (SELECT @num:=0,@user_id:=0,@cond:=0,@type:=0,@repeat:=0,@category:='') setup,( SELECT 1 n,1455 user_id,1 type,4 value UNION ALL SELECT 2 ,1455 ,2 ,'apple' UNION ALL SELECT 3 ,784 ,1 ,8 UNION ALL SELECT 4 ,784 ,2 ,'orange' UNION ALL SELECT 5 ,784 ,3 ,5 UNION ALL SELECT 6 ,784 ,4 ,'option' UNION ALL SELECT 7 ,3151 ,1 ,6 UNION ALL SELECT 8 ,3151 ,2 ,'apple' UNION ALL SELECT 9 ,3151 ,3 ,7 ) v WHERE (@num:=@num+1)*0+1 AND (@type:=type)*0+1 AND (@repeat:= IF(type=1,value,@repeat ))*0+1 AND (@category:=IF(type=2,value,@category))*0+1 AND (@cond:=IF(@type=1,0,@cond))*0+1 AND (@cond:=IF((type=1 AND value > 3) OR (type=2 AND value='apple'),@cond:=@cond+1,@cond))*0+1 AND IF(@cond=2,(@cond:=0)+1,0) AND ((type=1 AND value > 3) OR (type=2 AND value='apple'))
ところで、MySQLのviewにはユーザー変数や、サブクエリーは使えません...。ダメじゃん。