株式会社ジーワンシステムの代表取締役。 新しいものを生み出して世の中をあっといわせたい。イノベーションってやつ起こせたらいいな。

あなたはすでにSQLを会話のペースでできている!(答え)

»

 前々回の答えです。

 こちらを先に読んでいただいてないと今回の話はまったく分からないので、まずは前々回を読んで、答えを考えてからこちらを読んでみてください(ちょっとイライラしていたので、愚痴が多い記事で申し訳ないm(__)m ですが)。

 一般的なSQLの参考書には、まずないぐらいの難易度の問題になっていると思います。わざと難しくしています。本来は消費税を導出項目にはしない(いちいち計算しない)はずだから、永続化(保存)された請求書データとか、納品書データの消費税を使うでしょうね。

※逆に、少し応用すれば、請求書締処理にもなります。帳票ツールが効率の良いものであれば、請求締処理も本当に簡単に終わります。

 工数の見積もりはどのぐらいが標準なのでしょうか?

 わたしの見積もりは、

実装:30分
チューニング:1時間
テスト:1時間
資料作成:1時間

ぐらいのイメージかな。

 ところが、問題どおり請求書データなどを使わないでJavaなどで処理したら、割と大変な処理です。仕様書のレベルによるけれど、大手なら1週間ぐらいのスケジュールを引きそうな気がしないでもない。最近はそれはないかな?

 対象データが3万件ぐらいでも、秒の処理時間じゃないからテストも結構面倒だと思いますし。

 実際には、ストアドプロシージャで2人日ぐらいが標準的なんでしょうか?

 わたしも、現実的にはおそらくその辺を落としどころにバトルすることになるでしょう(ブチブチ愚痴っていることでしょう)。

 それはともかく、答えを見て、簡単と思うか、難しいと思うかは人それぞれですが、難しいと感じた人でも、難しい部分はほんの少ししかないはずです。

 今回の問題であれば、おそらく文法以外に「ABS、SIGN、DENSE_RANKの組合せを思いつくか」というところに1つの壁があります。文法から入るから、その壁が非常に高く感じるのだと思います。

 しかし、テストのためにエクセルでデータを作っておいてと言われれば、ほとんどのSEは苦もなくできるでしょう(これも間違った決めつけかな……)。

 ということは、「順位をつけるのはオートフィル(マウスでドラッグして連番をつける機能)でやったけど、SQLではどうすればよいか?」と、逆引きのリファレンスを開けば良い。配賦(はいふ:割り当てること)しなければいけない額の絶対値と順位を比べればよい、と手作業では分かっていることですし、SIGN関数を知らなければもう1回、CASE式(エクセルではIF関数)をネストすればいい。

 そのほか、この「エクセル → SQL」のコツを伝えるセミナーをやりたいのですけどね。見込み客が多ければセミナールームなどを借りる予算が取れるので、info@g1sys.co.jpまでお問い合わせください。みなさんが思っているほど、わたしは怖くないです(笑)

 会話のスピードでできるようになれば楽に不況を乗り越えられるよ(青色吐息のわたしが言っても説得力ゼロですな)。

 これができるようになると、打ち合わせのときにエクセル(でなくてもいいけれど)をイメージして最後まで処理できる手順ができれば、SQLで書けたのと同じということが分かることでしょう。

 つまり、みんなエクセルで手作業はできるのですから、ほとんどの機能が会話のペースでできるようになりますし、非効率なテーブル設計もしなくなるはずです。

 今までとは違う世界が見えます(大げさか)。

 SQLもプログラムですから、書いた人の意図が含まれているはずです。しかし、文法から考えられたSQL文は、結果は正しくても、意図が感じられないプログラムになっています。

 SQLは魔法じゃないのです。コンピュータ(DBEngine)にあなたのやりたいことを伝える命令文であることを忘れてはいけません。

 では、答え。

 机上デバッグしかしてないから、間違いがあったらすみません。

■ 1回でするパターン

SELECT
  a.納品書NO, a.請求書NO, a.売上日, a.得意先ID
  , a.商品ID, a.商品区分, a.単価, a.数量
  , CASE
    WHEN a.丸め単位 = 1 THEN
      a.明細消費税
    WHEN a.丸め単位 = 2 THEN
      a.明細消費税 +
      CASE WHEN ABS(a.納品書消費税 - a.納品明細合計) >= 納品SEQ
        
THEN SIGN(a.納品書消費税 - a.納品明細合計) ELSE 0 END
    WHEN a.丸め単位 = 3 THEN
      a.明細消費税 +
      CASE WHEN ABS(a.請求書消費税 - a.請求明細合計) >= 請求SEQ
        
THEN SIGN(a.請求書消費税 - a.請求明細合計) ELSE 0 END
    END AS 配賦後消費税
FROM
  (SELECT
    uh.納品書NO, uh.請求書NO, uh.売上日, uh.得意先ID
    , um.商品ID, p.商品区分, um.単価, um.数量
    , um.単価 * um.数量 AS 明細合計
    , fn丸め(um.単価 * um.数量 * p.消費税率, t.丸め方法) AS 明細消費税
    , SUM(fn丸め(um.単価 * um.数量 * p.消費税率, t.丸め方法))
      OVER (PARTITION BY uh.納品書NO) AS 納品明細合計
    , fn丸め(
      SUM(um.単価 * um.数量) OVER (PARTITION BY uh.納品書NO)
      * p.消費税率, t.丸め方法) AS 納品書消費税
    , DENSE_RANK() OVER (PARTITION BY uh.納品書NO
      ORDER BY um.単価 * um.数量 DESC) AS 納品SEQ
    , SUM(fn丸め(um.単価 * um.数量 * p.消費税率, t.丸め方法))
      OVER (PARTITION BY uh.請求書NO) AS 請求明細合計
    , fn丸め(
      SUM(um.単価 * um.数量) OVER (PARTITION BY uh.請求書NO)
      * p.消費税率, t.丸め方法) AS 請求書消費税
    , DENSE_RANK() OVER (PARTITION BY uh.請求書NO
      ORDER BY um.単価 * um.数量 DESC) AS 請求SEQ
    , t.丸め方法, t.丸め単位
  FROM
    売上 uh
    INNER JOIN 売上明細 um
      ON uh.売上ID = um.売上ID
    INNER JOIN 得意先 t
      ON uh.得意先ID = t.得意先ID
    INNER JOIN 商品 p
      ON um.商品ID = p.商品ID
  WHERE
    uh.売上日 BETWEEN(締めの範囲)
  ) a;

■ 3回でするパターン(UNIONで繋いでもOK)

●明細単位のSQL

SELECT
  uh.納品書NO, uh.請求書NO, uh.売上日, uh.得意先ID
  , um.商品ID, p.商品区分, um.単価, um.数量
  , fn丸め(um.単価 * um.数量 * p.消費税率, t.丸め方法) AS 明細消費税
  FROM
    売上 uh
    INNER JOIN 売上明細 um
      ON uh.売上ID = um.売上ID
    INNER JOIN 得意先 t
      ON uh.得意先ID = t.得意先ID
    INNER JOIN 商品 p
      ON um.商品ID = p.商品ID
WHERE
  uh.売上日 BETWEEN(締めの範囲)
  AND t.丸め単位 = 1;

●納品書単位のSQL

SELECT
  a.納品書NO, a.請求書NO, a.売上日, a.得意先ID
  , a.商品ID, a.商品区分, a.単価, a.数量
  , a.明細消費税 +
    CASE WHEN ABS(a.納品書消費税 - a.納品明細合計) <= 納品SEQ
      
THEN SIGN(a.納品書消費税 - a.納品明細合計) ELSE 0 END
FROM
  (SELECT
    uh.納品書NO, uh.請求書NO, uh.売上日, uh.得意先ID
    , um.商品ID, p.商品区分, um.単価, um.数量
    , um.単価 * um.数量 AS 明細合計
    , fn丸め(um.単価 * um.数量 * p.消費税率, t.丸め方法) AS 明細消費税
    , SUM(fn丸め(um.単価 * um.数量 * p.消費税率, t.丸め方法))
      OVER (PARTITION BY uh.納品書NO) AS 納品明細合計
    , fn丸め(
      SUM(um.単価 * um.数量) OVER (PARTITION BY uh.納品書NO)
      * p.消費税率, t.丸め方法) AS 納品書消費税
    , DENSE_RANK() OVER (PARTITION BY uh.納品書NO
      ORDER BY um.単価 * um.数量 DESC) AS 納品SEQ
    , t.丸め方法, t.丸め単位
  FROM
    売上 uh
    INNER JOIN 売上明細 um
      ON uh.売上ID = um.売上ID
    INNER JOIN 得意先 t
      ON uh.得意先ID = t.得意先ID
    INNER JOIN 商品 p
      ON um.商品ID = p.商品ID
  WHERE
    uh.売上日 BETWEEN(締めの範囲)
    AND t.丸め単位 = 2
  ) a;

●請求書単位のSQL

SELECT
  a.納品書NO, a.請求書NO, a.売上日, a.得意先ID
  , a.商品ID, a.商品区分, a.単価, a.数量
  , a.明細消費税 +
    CASE WHEN ABS(a.請求書消費税 - a.請求明細合計) <= 請求SEQ
      
THEN SIGN(a.請求書消費税 - a.請求明細合計) ELSE 0 END
FROM
  (SELECT
    uh.納品書NO, uh.請求書NO, uh.売上日, uh.得意先ID
    , um.商品ID, p.商品区分, um.単価, um.数量
    , um.単価 * um.数量 AS 明細合計
    , fn丸め(um.単価 * um.数量 * p.消費税率, t.丸め方法) AS 明細消費税
    , SUM(fn丸め(um.単価 * um.数量 * p.消費税率, t.丸め方法))
      OVER (PARTITION BY uh.請求書NO) AS 請求明細合計
    , fn丸め(
      SUM(um.単価 * um.数量) OVER (PARTITION BY uh.請求書NO)
      * p.消費税率, t.丸め方法) AS 請求書消費税
    , DENSE_RANK() OVER (PARTITION BY uh.請求書NO
      ORDER BY um.単価 * um.数量 DESC) AS 請求SEQ
    , t.丸め方法, t.丸め単位
  FROM
    売上 uh
    INNER JOIN 売上明細 um
      ON uh.売上ID = um.売上ID
    INNER JOIN 得意先 t
      ON uh.得意先ID = t.得意先ID
    INNER JOIN 商品 p
      ON um.商品ID = p.商品ID
  WHERE
    uh.売上日 BETWEEN(締めの範囲)
    AND t.丸め単位 = 3
  ) a;

Comment(16)

コメント

インドリ

ちょっと気になるのでコメントします。
データベースエンジニアリングに基づかず、エクセルで考える事は危険な気がします。
というのもExcelにデータ整合性とかACID属性などを考えて書く人はいないからです。
そうなると、第三正規化もしない危険なテーブル設計をされて、おまけにお客様の言葉が変る都度コロコロ変りデスマーチが発生する気がしてなりません。
導入部としてExcelはOKなのですが、Excelオンリーでシステム設計をされるとなると怖すぎます。
新人やSQLを知らない人が誤解するかもしれませんので、その辺の事も書いて欲しかったです。

saki1208

saki1208です。

普通の人はこの長さ、サブクエリを見ただけで拒否反応を示します。
実際にはもっとわかりにくい動的SQLを組んでいたとしてもです。

そういった人は、単に構造化を進めたソースですらまともに読む
ことが出来ず、やたら長いコードブロックを好む傾向があります。
ここら辺の違いがどこからくるのか、会話しながら探っていますが
未だに理解できないですね。

インドリさん、こんばんは。

次に書いていますけれど、新人にやらせようとか考えているわけではないです。
もともと、上流がSQLをやるべきですし、今、曲がりなりにもやっている上流が、ちゃんと実装まで考えれば、何のために正規化するとか、そういうことも分かるようになります。

saki1208 さん、こんばんは。

多分、今の教育で、今の世の中にある参考書で勉強した人のほとんどは拒否反応を起こすでしょうし、SQLからでは直感的には分からないでしょう。

しかし、エクセルに戻していくと読めます。

例えば、上の問題なら内側のクエリーで明細単位で計算した消費税と、丸め単位で計算した消費税を求めています。
これをエクセルに置き換えることは普通のSEなら理解できるはずです。

できない人は、私はSEとは認めたくないな、そんな人も居るのかな……。

私はエディタでしこしこ書いたけれど、現場でツールを使って書けば、DBの物理名が頭に入っている人なら、10分程度で書けるレベルですね。

その後、チューニングと称して、3つに割ったりイロイロやるわけです。
遊んでいるようなものですけれどこの時間が重要で力がつくのよ。

昔は、早く答えにたどり着けたら遊べると思ってがんばって居ました。

saki1208

生島さん、ども。
saki1208です。

確かに、10分位で書けるでしょう。
普通の人には無理でしょうけど…(この位は当たり前に出来て欲しいですが)

更に、10分で書いて時間が余る分、色んなことを試してみる時間も取れます。
差は益々開くばかりです。

て、言うか、普通の人は多分出来たらそこで思考停止。
ですかね。

saki1208

saki1208です。
連投すみませぬ。

今の業界は技術者を育てようとせず、あたかも部品か燃料のように
単純に原価の要素として技術者を扱っていることが多いと思います。

新人の時から成果を求められ、納期に追われて仕事をさせられる。
私が新人の頃はもう少しゆとりがあったように感じます。

saki1208 さん、ども。

弊社でも私が貰っていたぐらいの時間は与えているつもり。
でも、それでも足りないみたい。

今なら、「消費税の誤差配賦」って言われたら、2~5日はもらえるんじゃないかな。
最初、半日で上のを作って、提案資料を作って通るかチャレンジ。
通らないから、残った時間で落とし所のサブクエリーの部分を7、8個のクエリーに分解して通す。

そんなところです。

インドリ

>今の業界は技術者を育てようとせず、あたかも部品か燃料のように
単純に原価の要素として技術者を扱っていることが多いと思います。

その通りです。私が経験したので断言できます。
しかも会社の社長が無知で初めから「分からないものを儲けるから売る」という状態です。
それでもエンドユーザーはIT企業という看板を信じて疑わないのでこれが成り立ちます。
エンドユーザーにももっと賢くなって欲しいものです。
さらに上流は残念ながら情報処理技術そのものを知らないケースが目立ちます。
いわゆる奴隷商人です。こんな状態でまともな商品を売る筈がありません。
それでもこれまたエンドユーザは信じて買います(笑)
エンドユーザーが「知らないものを売っている人を信じない」という当たり前の事をしてくれたらこの業界はもっとよくなるのにと思えてなりません。
ちなみに、昨今はSQLだけではなくてあらゆるプログラミング言語が使えない人が増えてきているらしいです。
開発環境に頼り切っています。
プログラミングが出来なくても(なおかつ無知でも)設計/管理は出来るという妄信がそこにあります。
素人にも出来るならばエンドユーザーに出来ると思うのは私だけでしょうか?
なので残念ながら

>できない人は、私はSEとは認めたくないな、そんな人も居るのかな……。

出来ない人だからSQLもしないと仮定しなくてはならないと思います。
たぶん、プログラミング自体出来ず、情報処理技術って何?状態の人が多いようです。

インドリ

連投済みません。この例題の事なのですが、私は10分で済ませません。
10分で終わらしたら危険ですよ。
というのも、SQLの実行結果とチューニングだけではなくて、ロックや業務手順など様々な要因があるからです。
私は実際システムを構築しているから思うのですが、本当にプロを名乗るならば上流はその辺まで考えて欲しいですね。
そうでなくてはシステムなんて作れるはずがありません。
とはいえ、SQLが分からない状態でのスタートですからそのレベルにになるのは何時になるやら・・・

popo

はじめまして。

生島さんに遅れること十年弱でこの業界に入ったものです。
1年目に放り込まれたプロジェクトで
SQLならこの人に聞けといわれた技術者さんの下で
バッチ処理やデータのサルベージ、DBチューニングなどの仕事をする機会があり、
それまであまり得意でなかったSQLが得意になった記憶があります。
手取り足取り教えていただいたわけではないですが
DBやSQLの考え方、見方というものを叩き込まれた気はします。
吐き気がするような(深くてエグい)SQLなども見たり触ったり書いたり・・・・
いろいろ試したり(SQLで遊ぶといっていた)・・・・
教科書ではなく実践で学んだ気がします。

私はプログラムやSQLを研修などで学んだわけではなく
独学と経験で身に着けたのですが
逆にそれがよかったのかもしれません。
文法など小難しいことは苦手なのです。
SQLだけではなく言語全般にいえるのですが
文法云々よりも先に
「何がしたいのか」を考えるので、
文法や関数は手段でしかないのです。
(必要な手段は後で調べればいい)
最終的には何をしたくて、
そのためにはこれとこれをつないでこれをとってきて
というようにビジュアルで考えて
その後それをSQL化するといった具合です。

さまざまなことを考慮した上で
SQLで出来ることはできるだけSQLでしたいと思いますし、
そのほうが外部プログラムでグルグルするより
効率的に出来たりする事も多いと思います。

SQLが苦手な若手が非常に多いですが(私もまだ若手?だけれど)
文法や関数から入るからでしょうか。
データの逆算といいますか、
最後は何がほしくて、
そのためにはどれをとってこないといけなくて
そうするとどういう条件が必要で
という風に考えていくと
自ずとSQLが組めるのになと思ったりはします。
もちろんその後にパフォーマンスや
その他もろもろを考慮する必要はあるのですが。

皆が当たり前にSQLを使えるようになると
随分と楽になるのになぁと思う今日この頃です。

インドリさん、ども。

10分か60分かは誤差の範囲だと思いますよ。
10分ではテストゼロの意味ですし、最低、テスト込みで1時間は掛けるでしょう。
ここからチューニング含め2時間になるのか、4時間になるのか。

これぐらいの時間でできる人は、余った時間で納得するまでチューンしているはずです。ですから、書いたSQLはすべて実行計画を見ているはずです。
これをやってるかどうかで実力が全然変わってきます。

私はオヤジなので、オヤジ色満点ですけど、続きは、今日か明日かにアップされるそうです。

インドリ

>皆が当たり前にSQLを使えるようになると
>随分と楽になるのになぁと思う今日この頃です。

popoさんの仰るとおりですね。
私もそう祈っています。
少しぐらい苦手な人がいるのは当然なのですが、業界全体のレベルで苦手な人が多いとなると問題ですからね・・・


どうも生島さん。

>これをやってるかどうかで実力が全然変わってきます。

仰るとおりだと思います。
さっさと基礎を終わらせる能力は本当に必要ですね。
特に上流は・・・


>私はオヤジなので、オヤジ色満点ですけど、続きは、今日か明日かにアップされるそうです。

楽しみにしています。

popo さん、はじめまして。

私も文法というか一般的な教育は受けたことはないですし、業界に入って数ヵ月後には教えるほうに回っていたので、新人教育については本当に判りません。

英語と同じかなと思う。
10年以上習ってても、文法から入っている人は外人に話しかけられたらフリーズする(私も照れ笑いしかでない……)
赤ん坊から、5年も居たら(つまり5歳児)私よりはしゃべれる。
じゃぁ、文法から入る教育方法はたとえ大学に通ったとしても間違っているのです。

資格とかの点数は文法から入ったら確かに取れますけれど、OracleのGoldクラスで実戦経験が浅かったら多分解けないと思う。
じゃぁ、意味ないでしょう。

実践を積んで、更に、洩れている知識を確認するのには、資格の勉強は非常に良いので、社員にはしごいた後に取れと言うのですけれど、先ずは実践から。

oumi

こう、なんていうんでしょうか・・・
まじまじとこのSQLを見ていると、非常にCOBOL臭を感じます(W)
むしろ、COBOLと何が違うんだ?くらいに・・・
COBOLバリバリだった人って実は、SQLに向いてるんでないだろうか・・・
(SQL文を組み立てるという意味ではですが。)
言語もアーキテクチャも違うのですけどチューニングの作法(観点)は変わりませんし・・

ちょっと考えてみた。

Working Storage の内容
・表のカラムとSELECTの結果、一時的なファイル相当の配列

Procedureディビジョン?実行部(ちょとうろ覚え)
 サブクエリーや、クエリーの入れ子は、ループ構造の入れ子と思えばOK
 Working Strage Section(だったかな)の01レベルは
 1つのSelect 結果に相当する。
 中間ファイルも01レベルで表わされる。 

そのた関数は好きに使って、とやっていくと、
COBOLでバッチ処理とかやっていた人ってすぐ入り込めるような・・・
COBOLというか、手続き型の得意な人はみな、容易に入ってこれる?

逆に、Objectiveな言語系にはまったく合わない感じ・・・

もしかして、RDBMSへの入り方がみなさんまずかったから?
リレーショナルだ!とか古いファイルシステムがあーだこーだとか・・
(RDBMSメーカの戦略がへぼいかったから?)
手続き型のバッチ処理が、こんなにお手軽にかけますよ!
とかだったら、もしかしたら・・・・
手続き型の処理フローとSQL文って相性良いのではないかな
(手続き型アプリ全盛時代に出てきた言語ですから当然かもしれません)

「おやじだからこそできる、本当のSQLクエリー!?」
とか売れないか・・・ネーミングだめすぎですね(^^;

oumiさん、ども。

COBOLと似てるか、似てないか、どうのは分かりません。
ただ、次のエントリーにも書いたし、前から書いてますけれど、上流こそSQLをすべきと。

SQLは業務知識で処理手順を感じたまま書けば出来上がりです。

もっとも、これができる人は極めて少なくって、私は会ったことがあるのはスタロジの羽生さんぐらいかな……。

それはさておき、COBOLか、オブジェクト指向か、SQLか、何が偉いとか、新しいから良いとか思ってないです?
COBOLだろうが、オブジェクト指向だろうが、SQLだろうが、結果が出せるのが一番でしょう?

私がSQLにこだわるのは、RDBMSは完全に普及しているわけです。
ちゃんと使われてないだけなのです。
30分で実装して、半日遊んで終わらせれるのなら、それを超えられないRDBMSの使い方は、ちゃんと使えてないのです。

「ちゃんと使えてない」と認識して、ちゃんと使わない方法を選ぶことは間違いとは言わないけれど、導入は済んでいるのに嫌う人が居る。

その矛盾に対して導入したならちゃんと使おうよ。って言ってるのです。

コメントを投稿する