2008年11月22日土曜日

買出しは楽し

本日、朝からわが家の子供たちがおばあちゃん(義母)のところに遊びに行きました。妻も外出。すなわち私は半日の自由をゲット。

というわけで以前から気になっていた練馬の城北流通センター(大江戸河岸城北卸売市場)に行ってきました。Webにはほとんど情報はありません。それでも頑張って(というかダラダラ?)調べたところ、どうやら「安いらしい」「プロ用の店らしい」「でも一般客も歓迎らしい」「定休日が不明。営業時間はAM6:00~午前いっぱいらしい」とのこと。果たして土曜日は営業しているのか。無駄足となる懸念はありつつも、安くて新鮮な魚が手に入るのでは、と期待して朝9:00頃出かけてみました。

寒い中、リュックを担いで自転車で遠征。環八(笹目通り)から練馬高野台方面に入って目的地に到着。大丈夫。ちゃんと営業してました。

自転車置き場にはママチャリが10~20台ほど並んでます。結構一般の人も来てるのかな。自転車を止めて恐る恐る入ってみました。子連れのお父さんを何人か確認。一般人も普通に来ているようです(実はプロかもしれないけどね)。ざっと周りを散歩。そんなに広くはありません。中堅スーパーくらいかな。昔の魚屋さんとか乾物屋さんという店が並んでます。ダンボールや発泡スチロールが通路にせり出している。市場(いちば)って感じがします。でもちょっと活気がないか。全体に高齢化している模様。

一番元気なのが入り口近くの魚屋さんです。元気なおばさんと若いあんちゃんが店先で仕事してます。おばさんが主に客の相手をしていて、あんちゃんは力仕事担当。店の奥ではおっさんが黙々と魚を捌いている。うん。好ましいなー。なぜだか昭和の感じがする。

発泡スチロールの中には魚が氷に埋もれています。サンマ、サバ、アジ、太刀魚、キンメ、イカ、スズキ。いいねえ。値札を見るとサンマ一本80円とある。まあまあだな。子供の手のひら大の小アジ一匹70円。う~ん、ちょっと小さすぎる。アジの中くらいのがグラム150円(うろ覚え)。グラムじゃあ安いんだか高いんだか分からない。でも値札にご丁寧に「今日は高いです!」と書いてある。

「高い!」はないだろうと不審に思っていたら、あんちゃんが別の客に説明しているのが聞こえてきました。「今日はシケだったから高いよ~。それに量もなくてね~」。

はたと気がつく。そうか。この「高い!」は不本意の「高い!」だったのか。安くて新鮮な魚を期待するお客さんに申し訳ない、普段はもっと安いんだという忸怩たる「高い!」だったのだ。う~ん。ますます好ましい。というわけで「サンマ5本くださーい」とおばちゃんに言ってみる。一本は刺身に、残りの4本はパン粉焼き+自家製ピクルスのタルタルソースにしよう。サンマを5本袋に詰めてもらって支払い。「月曜はやってますか?」とおばちゃんに聞いたら「カレンダー通りだから休みなのよ。ごめんね」とのこと。了解。また来週来てみよう。

それから野菜を仕入れにスーパーに行きました。その前にTully'sに寄って熱いコーヒーを一杯。本日のコーヒー/ショートサイズ290円。こんなに高かったっけ?でもコーヒーは美味い。

スーパーでまずは野菜売り場に。葉尽きの大根、小松菜、ほうれん草、にんじんを買い物カゴへ投入。大根の葉っぱは茹でて刻んでしょうゆと削り節と砂糖少々で炒めると旨いのですよ。大根本体はどうやって食べようかと悩んでいたら、養殖鯛のおかしらが目に留まりました。つぶらな瞳が僕を見ている。しばらく目を合わせていると「こいつと大根を酒と薄口しょうゆで煮付けたら旨いんじゃないか」という、地平線上に漂う黒い雲のような「これは来るんじゃないか」という予感がしてきました(なんちゅう喩えか)。すなわち鯛の頭購入。それからアメリカ産豚肩ロース塊が安かったので購入。これは焼き豚にしよう。魚と肉とでは明らかに子供のテンションが違うのです。料理を作る以上、喜んでもらいたい。というわけで豚塊肉。

これから材料の仕込みを始めます。よい休日になりそうな予感。

(夕方追記)
パン粉焼きにしようと思ったんですが、子供を迎えに行っていろいろ用事を済ますうちに疲れてきちゃったので、からあげに変更。美味かった。
でも子供には不評。箸もつけずに「不味い」「嫌い」とのたまう。いいから食べてみ。美味いから。「イヤ。食べたくない」。じゃあ食べないとオヤツなしにする、と脅して無理やり食べさせる。と、一口、二口。一切れ食べて、二切れ目、タルタルソースをたっぷりつけて美味しそうに食べ始めた。な、美味いだろ!と聞いてみると、タルタルソースが美味しい。タルタルソースが魚の味を消してくれる、とのこと。やれやれ。

不思議と腹が立たないのは、オレも昔母親の料理に文句つけてたなあ、という記憶がよみがえるからか・・・。子供は親の料理にケチをつけるもの。親は子供にケチをつけられても(食べてさえくれれば)腹は立たない。そんなものかも知れません(食べないと怒るけどね)。

私の母親も、子供に文句を言われて悲しかったのかな。親になってみるとそうは思えないんだな。とにかく子供が食べてくれさえすれば、文句は言われても気にならないもん。母親の本当の気持ちは分かりませんがそんな気がしました。たまに「美味しかった!」と言ってくれれば、それでいいんだ。不味いとか美味いとか言いながら家族で食べる、それだけで。

刺身用と思ったサンマ一本。これも取りやめ。むしろ酢締めが食べたい。疲れたらやっぱり酢っぱいものでしょ。で、大名おろしした後に塩をまぶしたはいいものの(酢で〆る前の手順です)、そのまま忘れて夕食完了(ありがち)。しょうがないので塩を水で流して水分をキッチンペーパーで拭き取り、冷凍しました。末は蒸しサンマか焼きサンマ?

鯛のおかしら(アラだけ)+大根は旨かった。これでダシを取っておでん作ったら旨いんじゃないかな。今度試してみよう。

焼き豚いい感じ。ちょっと焦がしてしまいましたが、味は悪くない。明日が楽しみです。

(2008/11/28追記)
冷凍したサンマは燻製にして美味しく頂きました。中華鍋で燻製。使えますよ。おっちゃんのママゴトかとおもったら十分実用的。ちょっとしたたんぱく質を軽く熱燻すれば、燻製風味の深い味に速変わり。簡単・美味しい・後片付けが楽。こんなお遊びみたいな調理法が定番化するとはね。
.

2008年11月21日金曜日

RAD v7.5 試用版 を使ってJSFとEJB3.0・・・(3)

手順

1. サンプルDBを作成
2. DBをWAS v7のデータソースに登録
3. JPAプロジェクトを作成
4. EJBを作成(findメソッドのみ)
(前回はここまで)
5. JSFを作成
(今回はここまで)
6. EJBに挿入メソッドを作成する
7. JSFを作成(挿入)
8. EJBに更新メソッドを作成する
9. JSFを作成(更新)

5. JSFを作成(参照のみ)

以下の3ページを作成します。
(1) mysampleのキー(myid)入力画面
(2) キーを持つレコードが存在した場合、それを表示するページ
(3) 存在しない場合に表示するページ

テスト環境(WAS v7サーバー)が起動していたら停止して下さい(私の環境ではハングすることがありました。重すぎるんでしょう)。

ファイル > 新規 > 新規動的 Web プロジェクトをクリックします。
ウィンドウが現れるので、プロジェクト名に MySampleJSF と入力し、EARメンバーシップに MySampleEar があることを確認して 終了 をクリックします。

例によってモジュールの依存関係を定義します。 MySampleJSFを右クリック > プロパティ > Java EE モジュール依存関係 でMySampleEJBClient.jarとMySampleJPA.jarにチェックを入れます。

JSF用のBeanを作成します。

MySampleJSF を右クリックし、新規作成 > クラス を選択します。
新規Javaクラス ウィンドウに

パッケージ:test.beans
名前:MySampleBean

と入力し「終了」をクリックします。

MySampleBean を以下のように作成します。
package test.beans;

import java.io.Serializable;

public class MySampleBean implements Serializable {

private static final long serialVersionUID = 1L;

int id;
String value;

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}

/* まずはスタブを作成します */
public String checkInput() {
if (id == 0) {
return "success";
} else {
return "failure";
}
}
}

■web.xmlにFacesサーブレットを定義します。
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>
MySampleJSF</display-name>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

■/WEB-INF/faces-config.xml を作成します。
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE faces-config PUBLIC
"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
"http://java.sun.com/dtd/web-facesconfig_1_1.dtd">

<faces-config>
<managed-bean>
<managed-bean-name>mySampleBean</managed-bean-name>
<managed-bean-class>test.beans.MySampleBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<navigation-rule>
<from-view-id>/search.jsp</from-view-id>
<navigation-case>
<from-outcome>failure</from-outcome>
<to-view-id>/WEB-INF/results/failure.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/WEB-INF/results/success.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>

次にJSPです。

■index.jsp
<% response.sendRedirect("search.faces"); %>

■search.jsp
<%@page language="java" contentType="text/html; charset=windows-31j"
pageEncoding="windows-31j"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<f:view>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>検索</title>
<meta http-equiv="Content-Type"
content="text/html; charset=windows-31j">
</head>
<body>
<CENTER>
<h2>検索</h2>
<h:form>
myid:
<h:inputText value="#{mySampleBean.id}" />
<h:commandButton value="検索実行"
action="#{mySampleBean.checkInput}" />
</h:form>
</CENTER>
</body>
</html>
</f:view>

■/WEB-INF/results/success.jsp (パスに注意)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><%@page
language="java" contentType="text/html; charset=windows-31j"
pageEncoding="windows-31j"%>
<html>
<head>
<title>exists</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
</head>
<body>
OK
</body>
</html>

■/WEB-INF/results/failure.jsp
success.jspの"OK"を"NG"に変更するだけ。

これで動作確認をしてみましょう。"0"を入力すれば画面に"OK"が、"0"以外だと"NG"が表示されるはずです。

次にEJBと連携させます。

実はここまでくれば簡単です。

MySampleBean を以下のように変更します(太字部分)。
package test.beans;

import java.io.Serializable;

public class MySampleBean implements Serializable {

private static final long serialVersionUID = 1L;

int id;
String value;

@EJB(name="MySample")
private MySampleSessionLocal mySampleSession;

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}

public String checkInput() {
MySample ms = mySampleSession.findMySample(id);
if (ms != null) {
this.setValue(ms.getMyFlag());
return "success";
} else {
return "failure";
}
}
}

次にsuccess.jspを変更します。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><%@page
language="java" contentType="text/html; charset=windows-31j"
pageEncoding="windows-31j"%>
<html>
<head>
<title>exists</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
</head>
<body>
Found bean.<br>
id -> ${ mySampleBean.id }
value -> ${ mySampleBean.value }
</body>
</html>

以上。
.

RAD v7.5 試用版 を使ってJSFとEJB3.0・・・(2)

今回の手順は以下のページほとんどそのままです。
Building Java EE applications with IBM Rational Application Developer V7.5 ... (IBM dWの記事)
が、若干違う+dWの手順では上手く行かないところがあるのでご注意ください。

手順

1. サンプルDBを作成
2. DBをWAS v7のデータソースに登録
(前回はここまで)
3. JPAプロジェクトを作成
4. EJBを作成(findメソッドのみ)
(今回はここまで)
5. JSFを作成(参照のみ)
6. EJBに挿入メソッドを作成する
7. JSFを作成(挿入)
8. EJBに更新メソッドを作成する
9. JSFを作成(更新)

3. JPA/EJBを作成(findメソッドのみ)
一旦サーバーは止めましょう。重いし(私の環境ではハングすることもありました)。
(1)JPAプロジェクトを作成します。
ファイル > 新規 > その他 > JPAプロジェクト をクリックし 「新規 JPA プロジェクト」ウィンドウを起動します。以下の情報を入力します(MySampleEARは存在していません)
プロジェクト名:MySampleJPA
EARメンバーシップのEAR プロジェクト名:MySampleEAR
「終了」をクリックします。

(2)エンティティ(≒DBとマップされたBean)を作成します
「MySampleJPA」フォルダを右クリックし、新規作成 > エンティティー を選択します。
「新規 JPA エンティティー・エンティティークラス」ウィンドウで以下の情報を入力します。
Java パッケージ:test
クラス名:MySample
「次へ」をクリックします。
「新規 JPA エンティティー・エンティティープロパティ」のエンティティ・フィールドを埋めます。
追加をクリックしてエンティティーフィールドダイアログを表示
タイプ:int
名前:myid
としてOKをクリック。
「鍵」カラムにチェックを入れておきます。
再び「追加」をクリック。以下の情報を入力します。
タイプ:String 要注意!!タイプは char ではありません!!
名前:myFlag (敢えてテーブルのカラム名と異なるものを使っています)

「終了」をクリックすると以下のソースが生成されます。
package test;

import java.io.Serializable;
import javax.persistence.*;

/**
* Entity implementation class for Entity: MySample
*
*/
@Entity

public class MySample implements Serializable {


@Id
private int myid;
private char myFlag;
private static final long serialVersionUID = 1L;

public MySample() {
super();
}
public int getMyid() {
return this.myid;
}

public void setMyid(int myid) {
this.myid = myid;
}
public char getMyFlag() {
return this.myFlag;
}

public void setMyFlag(char myFlag) {
this.myFlag = myFlag;
}

}

アノテーションを追加します。(太字部分)
package test;

import java.io.Serializable;
import javax.persistence.*;

/**
* Entity implementation class for Entity: MySample
*
*/
@Entity
// ここでスキーマ名を指定します
@Table(schema="learning", name="mysample")
public class MySample implements Serializable {


@Id
private int myid;
// ここで変数名とは異なるカラム名を指定します。
@Column(name="mychar")
private char myFlag;
private static final long serialVersionUID = 1L;

public MySample() {
super();
}
public int getMyid() {
return this.myid;
}

public void setMyid(int myid) {
this.myid = myid;
}
public char getMyFlag() {
return this.myFlag;
}

public void setMyFlag(char myFlag) {
this.myFlag = myFlag;
}

}

次にJPAのpersistent.xmlにデータソース名指定します。
「MySampleJPA」フォルダ > 「JPAコンテンツ」 > persistence.xml をクリックします。
左ペインの「パーシスタンス・ユニット」を選択し、右側のペインのJTAデータ・ソースに 2.(3)で指定した jdbc/learning を入力します。

以上でJPAの作成が完了です。(残念ながらここでは動作検証は出来ません)

次にEJBプロジェクトを作成します。

ファイル > 新規 > EJBプロジェクト を選択して「新規 EJB プロジェクト・EJB プロジェクト」ウィンドウを起動します。
プロジェクト名に MySampleEJB と入力します。EJBモジュールバージョンが3.0であること、EARメンバーシップのEARプロジェクト名が MySampleEAR であることを確認し、「終了」をクリックします。

MySampleEJBフォルダを右クリックし プロパティ > Java EE モジュール依存関係 をクリックします。「選択可能な従属JAR」で「MySampleJPA.jar」にチェックを入れ、OKをクリックします。

MySampleEJB > MySampleEJB > セッションBean を右クリックします。コンテキストメニューから 新規 > セッションBean を選択します。

「EJB 3.0 セッションBean の作成ウィンドウ」に以下の情報を入力し、「ビジネス・インターフェースの作成」が「ローカル」になっていることを確認して終了をクリックします。
Javaパッケージ:test
クラス名:MySampleSession

生成されたEJBソースに下記の太字部分を追記します。
package test;

import javax.ejb.Stateless;

/**
* Session Bean implementation class MySampleSession
*/
@Stateless
public class MySampleSession implements MySampleSessionLocal {

/**
* Default constructor.
*/
public MySampleSession() {
// TODO Auto-generated constructor stub
}

@PersistenceContext(unitName="MySampleJPA")
EntityManager em;

public MySample findMySample(int myid) {
MySample ret = (MySample)em.find(MySample.class, myid);
return ret;
}

}

次にメソッドをプロモートします。

(重要 dWの記事には欠けていますが、以下の手順が必要です)
MySampleEJBClientフォルダを右クリックして、コンテキストメニューからプロパティを選択、次にJava EE モジュール依存関係 をクリックして「選択可能な従属JAR」で「MySampleJPA.jar」にチェックを入れます。

次に「アウトライン」ビューでfindMySampleを右クリック、コンテキストメニューから「Java EEツール」>「メソッドのプロモート」を実行します。

以上でEJBの作成が完了しました。

ここでユニバーサルクライアントを利用して動作確認をしてみます。

RADのサーバービューでWASサーバを起動します。

サーバーを右クリックし プロジェクトの追加および除去 を選択、MySampleEARを追加します。

公開が完了し、同期済みステータスになったら、サーバーを右クリックし、Universal Test Client > 実行 をクリックします。UTCが起動するのでログインします。(プロジェクトの追加以前にUTCを起動したことがあれば、一度「再起動」をクリックしてください)

「JNDIエクスプローラー」リンクをクリックします。

test.MySampleSessionLocalが表示されるのでクリックします。
※ そもそも表示されない場合はどこかで手順を間違っている可能性があります。プロジェクトがサーバに追加(デプロイ)されているかどうか等、手順を確認して下さい。
※ リンクではなく単にテキストが表示されている場合は、UTCの再起動を試して下さい。それでも出来ない場合はやはり手順を見直して下さい。

左側のペインに
MySampleSessionLocal
- MySampleSessionLocal
- メソッドの可視性
- MySample findMySample(int)
と表示されます。MySample findMySample(int) をクリックして下さい。

右側のペインに
test.MySample findMySample(int)

パラメーター 値
int: 0

と表示されています。そのまま「起動」をクリックします。

test.EJSLocal0SLMySampleSession_af305597.findMySample()
test.MySample@1ee01ee0 (test.MySample)

などと表示されれば成功です。「オブジェクトの使用」> String getMyFlag() ('a'が返る)などで遊んでみてください。

以上でEJBの作成が完了です。(しかしEJB簡単になったなあ)

RAD内のサーバーを止めてから、次のステップに行きます。

.

Rational Application Developer(以下RAD) v7.5 試用版 でJSFとEJB3.0を使ってCRUDアプリ作成(1)

試用版のダウンロード先です。IBM IDが必要です。(誰でも作成できると思います。)ダウンロードには丸一日かかります。

DBはMySQL v5.0を使いました。

IBM dWのBuilding Java EE applications with IBM Rational Application Developer V7.5 and WebSphere Application Server V7.0をベースに発展させて行きます。

手順

1. サンプルDBを作成
2. DBをWAS v7のデータソースに登録
(初回はここまで)
3. JPA/EJBを作成(findメソッドのみ)
4. JSFを作成(参照のみ)
5. EJBに挿入メソッドを作成する
6. JSFを作成(挿入)
7. EJBに更新メソッドを作成する
8. JSFを作成(更新)

1. サンプルDBを作成
create database learning;
use learning; -- このコマンドはMySQL独自。
drop table mysample;
create table mysample(
myid integer primary key,
mychar char(1)
);

delete from mysample;
insert into mysample values(0,'a');
insert into mysample values(1,'b');
insert into mysample values(2,'c');

2. WAS v7のデータソースに登録
RAD上にまだWAS v7 サーバが存在しない場合は作成して下さい。サーバービューを右クリックして「新規作成」を選択してテキトーに入力すれば出来ると思います。
WASを起動します。(重い・・・)
起動が完了したらServer view の サーバーを右クリックして「管理」>「管理コンソール」をクリック。
管理コンソールにログインします。

(1)JDBCを登録
メインメニュー Resources > JDBC > JDBC Providers > New >
ウィザード形式で入力を求められます。以下の情報を入力。
Database type -> User-defined
Implementation class name -> com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
Name -> MySQL JDBC Driver
次のページで
ClassPath: c:/path/to/mysql-connector-java-5.1.6-bin.jar
以上。

(2)認証情報を登録
メインメニュー Security > Global security > Authenticationエリアにある Java Authentication and Authorization Service を展開 > J2C authentication data > New
ここで
Alias -> MySQL_Learning
Userid, password -> DBに接続するときのID、パスワード
と入力する。

(3)データソースを作成
メインメニュー Resources > JDBC > Data source
Scope : 適切なスコープを設定(分からなければ'...Server = server1'とあるものを選びます)
> New
ウィザード形式で入力を求められるので下記情報を適宜入力します。
Data source name -> MySQL_Learning
JNDI name -> jdbc/learning(JPAに教えるので覚えておくこと)
> Next
Select an existing JDBC provider で MySQL JDBC Driver を選択
> Next
(何もしない)
> Next
Component-managed authentication alias
で、先ほど作成した認証情報(ノード名/MySQL_Learning)を選択。(JPAから参照するため、その他のaliasは不要。多分ServletからID/Passwordなしで利用するときはContainer-managed...が必要。Mapping-configurationは何か知らない・・・)
> Next
内容を確認
> Finish

次にカスタムプロパティを設定します。
JDBC providers > MySQL JDBC Driver > Data sources > MySQL_Learning > Connection pools > Custom properties > New
以下の情報を入力します。
Name -> url
Value -> jdbc:mysql://localhost:3306/learning
テスト接続。Warningが出ますが無視して問題ありません。
(注意:カスタムプロパティを設定しなくてもテスト接続は成功する。でもこのプロパティを定義しておかないと実際には使えない。認証情報をContainer-managedのみにしても成功するが、JPAからは使えない。すなわちテスト接続が成功しても油断しないで下さい)

まずは以上です。
.

2008年11月20日木曜日

SQL私観

はい。負け犬の遠吠えです。

SQL。難しすぎです。

もちろん単体のテーブルをSelectしたり、2,3個のテーブルをJOINする分には難しいことなどなにもありません。しかし、ちょっと細かい選択をSQLで作りこもうとすると、大変です。まるでクイズ。つーか実際SQLで課題を解くクイズが、実際にありますよね(しかもかなりたくさん)。今仕事してんだよ。クイズ解いてる暇ねーんだよ。とちょっと野蛮な言葉遣いになってしまいましたが、SQLのクイズとか見ていると何でこんなに難しいんだ、と思います。つーかクイズが成り立つ言語ってどうよ?

難しい理由は何か。それはSQLだから。つまりSQLだから難しい。すなわち難しさはSQLの本質的な属性と言えましょう。(この辺、論理大丈夫かな)

あまりにも難しいがために、便宜的で安直な解釈/用法がまかり通り、今ではそれがほとんど常識となっています。つまりデータを正規化してテーブルに落とし、キーでJOINして、SELECTしてINSERTして・・・。実際には非常に複雑で非直感的な仕組みの上に成り立っているのですが、それをばっちり隠蔽してしまっている。

いや、結果的にそうならざるを得ないと思いますし、それが正しいと思うんですよ。正面からSQLを理解するのってもはや研究者の仕事としか思えません。

初心者が何となくJOINを使って実装してしまうと(何となく実装するな、というツッコミを貰いそうですが、そうならざるを得ないプロジェクトがいかに多いことか)、内部の動きが隠蔽されている分、やってみなければパフォーマンスに問題がないか分からない。いや、やってみても分からない。十分にデータを突っ込んで試さない限りは。すなわちパフォーマンスチューニングが非常に難しい(統計情報に依存/実行計画の確認が必要)のがSQLです。初心者がSQLを作りこめばそこにパフォーマンスのリスクが生じる。脅しや極論ではなく、プロジェクトを運営する人が身に染みている分かっていることだと思います。

Java、Cなどの言語に毒された発想かもしれませんが、少なくとも処理が逐次的に理解できる手続き的な解法の方が、パフォーマンスへの影響を見極めやすいなど、メリットが多いと思えます。しかし複雑なSQLで解こうとすると、実行計画を実際に見ない限りどうなっているのか分からないし、しかもそれも統計情報に依存するわけで。

初心者はJOIN原則禁止。副照会厳禁。別に恥じることはないですよ。これでいいんじゃないか、と私は思います。え?それじゃ学習しないって?やっぱりダメか。

以上
.

ダイエットその後

ダイエット順調ですね。うむ。胃が軽くなって来た感じがあります。体重もゆるゆると落ちてきました。

酒は一日2~3合あたりで安定してます。最近は飲み過ぎてないです。食事中にワイン1合。食後にチーズなど軽くつまみながらウィスキー60~100ml。なんだか物足りないな、という日は寝る前にウィスキーか焼酎を少々(~60ml)。休日はいつも飲み過ぎてしまうんですが(夕方から一杯やり始めてだらだらと止めどなく飲む)先週末は3合未満で押さえました。素晴らしい!!よくやった!!

私の場合「食べる→飲む」という流れが強いようです。「飲む→食べる」というベクトルはさほど強くない。また、「飲む→飲む」という展開にはなりにくい。すなわち「飲む」の後に「食べる」をかまさなければ、だらだら飲み続けるということがなくなるようです。なんか食べたいなあ、でも我慢しなきゃ、などと思いつつ昔買ったDancyu(グルメ雑誌)や料理本など眺めながら、今度は何を食べてやろう、とチビチビ酒をすするのが至福の時です(しかし食欲強いな)。

さあこの調子で頑張ろう。
.

2008年11月17日月曜日

SQLについて分かったこと

「以降でSQLの勉強結果をブログに書込みたい」と書きました。はい。何が分かったか。自分が分かっていなかったことが分かりました。SQLが難しいということがよく分かった。そういうことです(泣)。

SQLの基本はデカルト積で得られた集合ってことですね。
正規化だとか「表」だとかプライマリキーとか、そういう話は一度忘れてしまうこと。手続き型の解法も忘れてしまえ。

以下、JOINの振る舞いについて記載します。(MySQLとDB2の方言をただ並べただけかも・・・)

準備。
drop table hoge;
create table hoge(
col1 char(1),
colh_2 char(1)
);
drop table piyo;
create table piyo(
col1 char(1),
colp_2 char(1)
);

delete from hoge;
insert into hoge values('a','a');
insert into hoge values('b','b');
insert into hoge values('c','c');
insert into hoge values('X','x');
insert into hoge values('Y','y');
insert into hoge values('Z','z');
delete from piyo;
insert into piyo values('1','1');
insert into piyo values('2','2');
insert into piyo values('3','3');
insert into piyo values('X','X');
insert into piyo values('Y','Y');
insert into piyo values('Z','Z');

MySQLとDB2で試しました。JOINについては方言があることを確認(仕様としてどうなのか、までは踏み込んでいません。MySQLに癖がある気がします)。

(1) デカルト積-1
MySQL: select h.col1, h.colh_2, p.col1, p.colp_2 from hoge h, piyo p;
DB2: select h.col1, h.colh_2, p.col1, p.colp_2 from hoge h, piyo p;

(2) デカルト積-2
MySQL: select h.colh_2, p.colp_2 from hoge h, piyo p;
DB2: select h.colh_2, p.colp_2 from hoge h, piyo p;

(3) しつこくデカルト積-3
MySQL: select h.col1, p.col1 from hoge h, piyo p;
DB2: select h.col1, p.col1 from hoge h, piyo p;

要するに
from hoge, piyo

した時点で、裏に巨大な直積集合ができているわけです。

今度はjoinの振る舞いを確認
(4) JOIN単体
MySQL: select h.colh_2, p.colp_2 from hoge h join piyo p; (1)と同じ結果が得られる。
DB2: JOIN単体ではエラー
(select h.colh_2, p.colp_2 from hoge h, piyo pで同様の結果が得られる)

(5) 今度はクロス結合。
MySQL: select h.colh_2, p.colp_2 from hoge h cross join piyo p;
DB2: CROSS JOINはエラー(でもMySQLでも単に'cross'が無視されているだけかも?)

(6) INNER JOIN ON ~
MySQL: select h.colh_2, p.colp_2 from hoge h inner join piyo p;
DB2: select h.colh_2, p.colp_2 from hoge h inner join piyo p on h.col1 = p.col1;

結果に相違あり。MySQLでは直積集合がそのまま表示されている。(inner が無視されている?)
DB2では合致する行のみが表示される。↓
db2 => select h.colh_2, p.colp_2 from hoge h inner join piyo p on h.col1 = p.col1

COLH_2 COLP_2
------ ------
x X
y Y
z Z

3 レコードが選択されました。

同様の結果をMySQLで表示するためには、
mysql> select h.colh_2, p.colp_2 from hoge h inner join piyo p where h.col1 = p.col1;
+--------+--------+
| colh_2 | colp_2 |
+--------+--------+
| x | X |
| y | Y |
| z | Z |
+--------+--------+
3 rows in set (0.00 sec)

とする。

(7) LEFT JOIN
MySQL: select h.colh_2, p.colp_2 from hoge h natural left outer join piyo p;
DB2: select h.colh_2, p.colp_2 from hoge h left outer join piyo p on h.col1 = p.col1;

これはMySQLとDB2で結果が同じ。
+--------+--------+
| colh_2 | colp_2 |
+--------+--------+
| a | NULL |(DB2では'-'ハイフンが表示される)
| b | NULL |
| c | NULL |
| x | X |
| y | Y |
| z | Z |
+--------+--------+
6 rows in set (0.00 sec)

(8) RIGHT JOIN
MySQL: select h.colh_2, p.colp_2 from hoge h natural right outer join piyo p;
DB2: select h.colh_2, p.colp_2 from hoge h right outer join piyo p on h.col1 = p.col1;
(DB2での実行例)
db2 => select h.colh_2, p.colp_2 from hoge h right outer join piyo p on h.col1 = p.col1;

COLH_2 COLP_2
------ ------
- 1
- 2
- 3
x X
y Y
z Z

6 レコードが選択されました。

これもLEFT JOIN同様両者の結果は変わらず。

(9) FULL OUTER JOIN
MySQL: full outer join 未対応
DB2: select h.colh_2, p.colp_2 from hoge h full outer join piyo p on h.col1 = p.col1;
db2 => select h.colh_2, p.colp_2 from hoge h full outer join piyo p on h.col1 = p.col1;

COLH_2 COLP_2
------ ------
- 1
- 2
- 3
x X
y Y
z Z
a -
b -
c -

9 レコードが選択されました。

同様の結果をMySQLで出すためには
mysql> select h.colh_2, p.colp_2 from hoge h natural right outer join piyo p
-> union
-> select h.colh_2, p.colp_2 from hoge h natural left outer join piyo p
-> ;
+--------+--------+
| colh_2 | colp_2 |
+--------+--------+
| NULL | 1 |
| NULL | 2 |
| NULL | 3 |
| x | X |
| y | Y |
| z | Z |
| a | NULL |
| b | NULL |
| c | NULL |
+--------+--------+
9 rows in set (0.00 sec)

まあ美しくはありません。

なんだか支離滅裂なようですが、SQLが難しいってことがよく分かりましたよ。あたしゃ。

もうこれから原則JOIN禁止。副照会はいかなる理由があっても厳禁。

以上。
.

2008年11月16日日曜日

節酒は楽しい

今回は、わりと節酒は楽しいというトーンで。

節酒もいいですね。目標は一日二合まで。それが私の節酒です。

日本酒一合が、大体ワイン一合ですね。他は、≒ビール(発泡酒・チューハイ)500ml ≒ウィスキー60ml ってとこです。

つまり二合を日本酒、ワイン、ビール類、ウィスキーで消費するわけで。となると、組み合わせは4x4の16通り。日本酒一合にワイン一合とか、ウィスキーとワインとか、日本酒二合とか。いやいや、例えばビール(発泡酒・チューハイ)を350mlにすれば、残りの150ml分を別の酒に割り当てられるわけで。結構選択肢はあるわけで。

それはともかく、呑む量を制限するとそこに投資の余地が出てくることは確かです。

今まではいかに安く、効率的に酔うか、が課題でした。

すなわち、ワインは500円前後のもの。焼酎は甲類と乙類の間を彷徨い(分かんない人、分かんなくていいです)、ウィスキーはブラックニッカ決め打ち。ビールは小遣い日のみ。普段は発泡酒未満。とにかく安くて量があればいい。そんな価値観です。

ところが、一日二合となるとその発想は通用しない。せっかくの二合ですから、できればおいしく頂きたい。かといって予算には上限がある。すなわち少し上を目指しつつ、あまり高くないものを探すことになる。

すると、いわゆる「安くて美味い」というエリアが広がってくるんですね。特にワイン、ウィスキー、日本酒、焼酎。(要するにビール・チューハイ以外ですな)

ワインについて言えば、1,000円前後/720mlなら躊躇せず買えるレベルになる。この価格帯だと、結構(500円未満に慣れた舌を)うならせるものが出てくるんです。ウィスキーも1,000円前後/720mlを許容すると、普通の輸入スコッチ(カティサーク、ジョニ赤)、バーボン(ジャックダニエル白、緑(好き)、フォア・ローゼズなど)も選択肢に入ってくる。焼酎も雲海(蕎麦)とか黒霧島(芋)あたりが射程範囲。日本酒も、米100%だけど純米酒じゃない酒(分かんない人、分かんなくていいです)、あるいはとっても薄い本醸造、一升1,000円前後から脱出して「美味い本醸造酒」「安いけどまっとうな純米酒」が選択肢に入ってくる。

心置きなく泥酔するという選択肢を捨てるのは残念ではありますが(残念だけど、死ぬまで泥酔し続けるわけにもいかんし)、それに変わっておいしくお酒をたしなむというのも、まあ、悪くないかな、という気がしないでもない今日この頃であります。

以上。
.