おれの技術日記

元はJava+SQLがメインのエンジニア、フロントエンドは軽くかじった程度で苦手。最近忘れっぽいので覚えたことをいろいろメモするためにブログ開始。

バスケット分析(併売分析)のためのSQL

いわゆる売上トランザクションテーブル、こんなデータから

OrderID Date ProductID ・・・
0000001 2017-07-01 A001 ・・・
0000001 2017-07-01 A002 ・・・
0000002 2017-07-02 A003 ・・・
0000003 2017-07-02 A001 ・・・
0000003 2017-07-02 A002 ・・・
0000003 2017-07-02 A004 ・・・

ある商品を買った人がほかの商品をどれだけ併売しているかを調べるためのこんなテーブルを作る。

YearMonth MainProduct SubProduct # of Purchase
2017-07 A001 A002 2
2017-07 A001 A004 1
2017-07 A002 A001 2
2017-07 A004 A001 1


こんな感じでSQLを書く。書いてしまうと普通のクエリなんだけど、これがひらめいたときは結構感動した。

SELECT
   date_format(a.Date, '%Y-%m') YearMonth
   ,a.ProductID MainProduct
   ,b.ProductID SubProduct
   ,count(*) `# of Purchase`
FROM
   Sales_T a
   ,Sales_T b 
WHERE
   a.OrderID = b.OrderID 
   and a.ProductID <> b.ProductID
GROUP BY
   date_format(a.Date, '%Y-%m')
   ,a.ProductID
   ,b.ProductID

月次累計を求めるSQL

こんなデータがあるときに

Date Sales
2017-07-01 110
2017-07-02 120
2017-07-03 130
・・・ ・・・
2017-07-31 410
2017-08-01 100
2017-08-02 110

ひとつカラムを追加して、月次累計(Monthly Running Total)を求めたい場合に

Date Sales MRT_Sales
2017-07-01 110 110
2017-07-02 120 230
2017-07-02 130 360
・・・ ・・・ ・・・
2017-07-31 410 8060
2017-08-01 100 100
2017-08-02 110 210


こんな感じでSQLを書く。ここで、GROUP BY 1,2はGROUP BY t1.Date,t1.Salesと同じ意味。

SELECT
   t1.Date
   ,t1.Sales
   ,sum(t2.Sales) RunningTotal_Sales
FROM 
   Daily_Sales t1
   ,Daily_Sales t2 
WHERE
   year(t1.Date)=year(t2.Date) 
   AND month(t1.Date)=month(t2.Date) 
   AND t1.Date >= t2.Date
GROUP BY
   1,2


ここでなぜsum(t2.Sales)が月次累計になるかというと、以下のように集計を外したSQLを書くと

SELECT
   t1.Date
   ,t1.Sales
   ,t2.Sales t2_Sales
FROM 
   Daily_Sales t1
   ,Daily_Sales t2 
WHERE
   year(t1.Date)=year(t2.Date) 
   AND month(t1.Date)=month(t2.Date) 
   AND t1.Date >= t2.Date

こんな感じの結果セットが取得できる。(1日目は1レコード、2日目は2レコード、・・・31日目は31レコード)

Date Sales t2_Sales
2017-07-01 110 110
2017-07-02 120 110 ←7/1の値
2017-07-02 120 120
2017-07-03 130 110 ←7/1の値
2017-07-03 130 120 ←7/2の値
2017-07-03 130 130
・・・ ・・・ ・・・
2017-08-01 100 100
2017-08-02 110 100 ←8/1の値
2017-08-02 110 110

これに対してsum(t2.Sales) GROUP BY t1.Date,t1.Salesをするので、月次累計となる。
もし日ごと製品カテゴリごとの売上というテーブルがあった場合は、同様にSELECT句・WHERE句・GROUP BY句に製品カテゴリを含めればOK。

MySQLでカレンダーテーブルを作る

自分がよく使うSQLをライブラリ的にここに残しておく。

こんな感じでSQLを書くと

SELECT
   @date:=date(date_format(CURRENT_DATE(),'%Y-%m-01')) as Date
UNION ALL
SELECT
   @date:=DATE_ADD(@date, INTERVAL 1 DAY)
FROM
   `テーブル名`
WHERE
   @date < last_day(CURRENT_DATE())

こんな感じで結果セットが取れる。

Date
2017-08-01
2017-08-02
・・・
2017-08-31

この時、SQLの中で参照するテーブルはどんな構造になっていてもかまわないけれど、ここで取得する日付範囲よりも多いレコードを持っている必要がある。
例えば上の例でいえば`テーブル名`は30レコード以上持っている必要がある。

JavaScript再入門4 - call, apply, bind

JavaScript再入門3 - thisは何を意味するか - おれの技術日記の続き。

例によってこちらにお世話になります。
最強オブジェクト指向言語 JavaScript 再入門!

7. call/applyを使ってthisをコントロールしつつ関数をコールする。

前回のブログの中で、thisというのはコンテキストによって可変で関数実行時に決定されるということがわかった。
このthisをコントロールしつつ関数を呼び出すのがcallとapply。この二つは記述方式こそ違うものの、やっていることは本質的に同じ。
・関数内でのthisが何か
・呼び出すときの引数は何か
というのを指定しつつその関数を実行する。

callとapplyは以下のようなシンタックスで使用される。ここでobjectは関数内でのthisにあたり、arg1以降は関数を呼び出す時に渡される引数となる。call関数は可変長引数であるのに対して、apply関数はArrayを第二引数にとる。

call(object, arg1 ,arg2...)
apply(object, [arg1,arg2,...])

例えば以下のようなコードの場合、func1は3回呼び出され全く同じ結果となる。

function func1(){
    console.log("123");
}
func1();//123が表示される
func1.call();//123が表示される
func1.apply();//123が表示される

以下のようなコードの場合、最初のコールではthisがwindowオブジェクトになってしまうがwindowオブジェクトにname属性は存在しないためthis.nameは空になる(undefinedにならないのはなんでだろう・・・)。一方でcall・applyを通じてコールする時に引数にobjを渡しているので、this=objとなりname属性の値がログ出力される。

function func1(){
	console.log("log:" + this.name);
}
var obj = {
	name:"obj_name",
}
func1();//log:だけがコンソール出力される
func1.call(obj);//log:obj_nameがコンソール出力される
func1.apply(obj);//log:obj_nameがコンソール出力される

これはfunc1に引数が存在する場合の呼び出し方。callでもapplyでも、可変長引数なのか配列を渡すのかの違いだけで結果は一緒。

function func1(param1, param2){
	console.log("log:" + this.name + ":" + param1 + param2);
}
var obj = {
	name:"obj_name",
}
func1.call(obj, "this is ", "test");//log:obj_name:this is testがコンソール出力される
func1.apply(obj, ["this is ", "test"]);//log:obj_name:this is testがコンソール出力される

8. bindを使って関数内のthisを固定化する。

通常は関数とオブジェクトには何の関連もなく、関数が呼び出されたときにその状況によってthisの値が決定される。しかしbind関数を使えば実際の関数呼び出しの前にthisを固定化することができる。

以下のコードではfunc2はobjとbindされているためthisの値が存在するが、func1はbindされていないのでthis.nameが空となる。

function func1(param1, param2){
	console.log("log:" + this.name + ":" + param1 + param2);
}
var obj = {
	name:"obj_name",
}

var func2 = func1.bind(obj);
func2("this is ", "test");//log:obj_name:this is testがコンソール出力される
func1("this is ", "test");//log::this is testがコンソール出力される

という仕様は理解したものの、実際どういうところで使うんだろう・・・・大規模なJavaScriptでの開発とかやってみたい。

依然として今後の課題

  1. Apply関数って何?→thisをコントロールするためのものらしい
  2. (function() {})();とかいう記法→即時関数というらしい
  3. thisのスコープの考え方

JavaScript再入門3 - thisは何を意味するか

JavaScript再入門2 - おれの技術日記の続き。

例によってこちらにお世話になります。
最強オブジェクト指向言語 JavaScript 再入門!

6. thisの解釈は状況によっていろいろ。

Javaにおいてthisといえば非常に明確で、インスタンス変数を明示的に指定するとかインスタンスのメソッドを呼ぶときにsuperでなくthisなのよってな感じで書くものであった。しかしJavaScriptにおいてthisというのはコンテキストによって全く異なる意味合いを持つ。

ルール1:グローバルスコープにおいて、thisはwindowを指す

これは非常に簡単。windowがオブジェクトの起点なのでグローバルスコープにおいてthisはwindowとなるというのは納得できる。

//グローバルスコープ
console.log(this);//1番目のthis、windowを指す
ルール2:関数呼び出しにおいて、thisはwindowを指す

まあこれもそういうもんかという感じ。

function global_function(){
    console.log(this);
}
global_function();//2番目のthis、windowを指す
ルール3:メソッド呼び出しにおいて、thisはメソッドが呼ばれたインスタンスを指す

じゃあ次にこれは?というとここでのthisはObject {method1: function}となる。ここら辺までは想定の範囲内

var obj = {
    method1:function (){
        console.log(this);
    }
}
obj.method1();//3番目のthis、上のobjを指す
ルール4:入れ子になった関数において、thisはwindowを指す

これはだいぶ意味不明なんだが、そういうもんとして覚えるしかなさそう。thisにおいてスコープチェーン的な考え方は通用しない。

var obj = {
    method1:function (){
        var method2=function(){
            console.log(this);
        }
        method2();
    }
}
obj.method1();//4番目のthis、windowを指す
ルール5:これらルールに基づき、thisが何を指し示すかは処理の"実行時"に決定する

以下のコードでglobal_functionは2回呼ばれているが、全く同じ処理であるにも関わらずそれぞれの指し示すthisはその関数が呼ばれる状況によって異なっている。(=thisは実行時に決定される

function global_function(){
    console.log(this);
}
global_function();//2番目のthis、windowを指す
var obj = {}
obj.method2 = global_function;
obj.method2();//4番目のthis、上のobjを指す

だいぶお腹いっぱいになってきたので今日はこのへんで。

依然として今後の課題

  1. Apply関数って何?→thisをコントロールするためのものらしい
  2. (function() {})();とかいう記法→即時関数というらしい
  3. thisのスコープの考え方

JavaScript再入門2 - スコープチェーンとクロージャ

JavaScript再入門1- プロトタイプチェーンとコンストラクタ関数 - おれの技術日記の続き。

例によってこちらにお世話になります。
最強オブジェクト指向言語 JavaScript 再入門!

4. 変数のスコープはグローバル/ローカルのみ、スコープチェーンのルールに従い内側のスコープから順次走査・参照される

CとかJavaとかだとローカル変数のスコープはブロックを単位としているので、例えば以下のようなコードはコンパイルエラーになってそもそも実行できないが、JavaScriptでは合法。ここで宣言された変数a, bはその関数内のどこでも参照することができる。

function func1(){
    var a = 1;
    if(何らかの条件){
        var b = 3;
    }
    alert(b);
}

JavaScriptはみょうちくりんな言語なので関数を入れ子にすることもできるんだが、では次にこうするとどうなるか。

function func1(){
    var a = 1;
    function func2(){
        var b = 2;
    }
    alert(b);//ここでReferenceError
}
func1();

内側の関数の中で宣言された変数に対して外側の関数からはアクセスすることができず、ReferenceErrorとなる。ブロックレベルのスコープは存在しないが関数の入れ子による変数スコープの制限は有効。

グローバル変数 ローカル変数
関数の外で宣言した変数 関数の内側で宣言した変数・関数の仮引数
プロセス全体で参照できる その関数の内側からのみ参照できる

次にこんなコードを書いてみると、これは動く。

var c = 3
function func1(){
    var a = 1;
    function func2(){
        var b = 2;
        //一番内側の関数からはその外側の変数を参照できる
        alert(a+b+c);//"6"となる
    }
    func2();
}
func1();

func2で参照された変数は一番内側のスコープをまず走査し、見つからなければもうひとつ外のfunc1のスコープを走査し、最後にグローバルスコープを走査する(もしそれでも見つかれなければエラーになる)。このようにして内側から外側に順次走査する機構をスコープチェーンという。ちなみに、複数のスコープで同一名の変数を宣言した場合には、このルールに従って一番最初に見つかったほう(=より内側の変数)が参照される。

5. クロージャを使ってグローバルスコープを汚さずに状態を管理する

先ほど行ったようにJavaScriptでは関数の中にさらに関数を定義することができるが、内側の関数をスコープ外から呼び出すことができるか。と思ってこんなコードを書いてみたら、やはり最後の行のfunc2()は参照できずエラーになった。

function func1(){
    var a = 1;
    function func2(){
        var b = 2;
        alert(a+b);
    }
    func2();
}
func1();//これはOK
func2();//これはエラーになる

このスコープの妙を利用して、以下のように関数内に変数の状態を保持させ続けることができる。このようなやり方をクロージャという。

function func1(){
    var a = 1;
    return function(){
        alert(a);
        a+=1;
    }
}
var f = func1();
var f2 = func1();
f();//1がアラートされる
f();//2がアラートされる
f();//3がアラートされる
f2();//1がアラートされる

ちなみに、「この程度ならグローバル変数でもできるのでは?」と思われる方もいると思うが(というか私も思った)、クロージャはグローバルスコープを汚さずに状態を維持するために使うらしい。クロージャの手順は以下の通り。

  1. 関数を入れ子に定義する
  2. 外側の関数に変数を宣言する
  3. 内側の関数からその変数を操作する
  4. 内側の関数を外側の関数の戻り値にする

依然として疑問なのは

  • この例だと関数の参照をグローバルで持ち続けないといけないから結局グローバルスコープは汚れるのでは?
  • JavaでいうとこのBeanオブジェクトみたいなん作るときにうれしいのかな?でもそれならコンストラクタ関数でもおんなじことできるしな・・・
  • 結局使いどころがピンとこないぞ、ググってもカウンタの例ばっかり出てくる

まあ、これはおいおいわかってくるんでしょう・・

依然として今後の課題

  1. Apply関数って何?
  2. (function() {})();とかいう記法→即時関数というらしい
  3. thisのスコープの考え方

JavaScript再入門1- プロトタイプチェーンとコンストラクタ関数

JavaScriptは昔から触ってるけれど、いつも何となくググりながら場当たり的なコードを書いてやり過ごしてきてプロトタイプとかきちんと理解してないので、備忘も兼ねて勉強したことを記録してみる。

参考:最強オブジェクト指向言語 JavaScript 再入門!


1. オブジェクトはただのハッシュテーブル

オブジェクトは以下のようなコードで定義できる。

var obj = {key:value}

ここでvalueの部分には数値・テキスト・関数などを持ってこれるので、こんな書き方が可能。

var objA = {
    name:"tanaka"
    ,say:function(){alert("my name is " + this.name);}
}
objA.say();

2. Classはないけどプロトタイプチェーンがある

Class型言語の場合はClass定義をひな型としてオブジェクトを作るけれど、JavaScriptにクラスはない。そのかわり自身の上位(?)オブジェクトを自動的に参照するプロトタイプチェーンという機構がある。

var objA = {
    name:"tanaka"
    ,say:function(){alert("my name is " + this.name);}
}
var objB = {
    name:"suzuki"
}
var objC = {}
objB.__proto__ = objA
objC.__proto__ = objB

objA.say(); //"my name is tanaka"となる
objB.say(); //"my name is suzuki"となる
objC.say(); //"my name is suzuki"となる

ここでobjB.say()がコールされたときに、sayという関数はobjB自身はもっていないためそのプロトタイプであるobjAのsay関数が自動的に参照・コールされる。同様にobjC.say()の場合はobjB.nameとobjA.sayが使用される。
(ただし__proto__は標準仕様ではないらしく、通常は__proto__を使ったプロトタイプの設定はしないらしいけど・・・)

3. new演算子と関数を組み合わせてオブジェクトを生成する

ふつうはこんな風に書くらしい。(意味は分かるけど腹落ちしきれない)

var Person = function(name){
    this.name = name;
}
Person.prototype.sayHello = function(){
    alert("Hello " + this.name);
}
var o = new Person("suzuki")
o.sayHello();//"Hello suzuki"となる

「new演算子と組み合わせて関数をコールすることで、関数はコンストラクタ関数として実行され、オブジェクトの生成に利用される」とな。わかるようなわからないような・・・
new Person()をコールしたときに内部では(だいたい)こんなコードが動いているそう。

var obj = {};
obj.__proto__ = Person.prototype;
Person.apply(obj, arguments);
return obj;

__proto__というのは各オブジェクトが継承元の参照を保持するためのフィールドで、prototypeってのはクラス型言語でいうところのstaticともいうべき、各オブジェクトが参照すべきひな型という感じだろうか。3行目の意味はよくわからない、今後の課題とする。
元のスライドを丸パクリ写経した以下のコードだとここの動きがわかりやすい。

var Person = function(name){
	this.name = name;
}
Person.prototype.sayHello = function(){
	alert("Hello " + this.name);
}

var p = new Person("suzuki");
p.sayHello();//"Hello suzuki"

Person.prototype.name="tanaka";
p.sayHello();//"Hello suzuki"

delete p.name;
p.sayHello();//"Hello tanaka"

あやふやだった理解がだいぶしっかりした気がするけど、力尽きたので今日はここまで。

今後の課題

  1. Apply関数って何?
  2. (function() {})();とかいう記法
  3. thisのスコープの考え方