tag:crieit.net,2005:https://crieit.net/tags/SQL/feed 「SQL」の記事 - Crieit Crieitでタグ「SQL」に投稿された最近の記事 2022-12-19T13:23:20+09:00 https://crieit.net/tags/SQL/feed tag:crieit.net,2005:PublicArticle/18341 2022-12-19T13:23:20+09:00 2022-12-19T13:23:20+09:00 https://crieit.net/posts/c1d391c26b9ce512e85d9960b9f6376d 全テーブルの中から対象データが含まれているテーブルと項目を取得 <pre><code>--//出力表示 SET SERVEROUT ON; DECLARE --//変数定義 type cursor_type is ref cursor; cur_search cursor_type; vCount INTEGER; ERR_CODE NUMBER; ERR_MSG VARCHAR2(255); TYPE objName IS VARRAY(260) OF VARCHAR2(30); --//対象データを指定 tDATE objName := objName('') BEGIN --//テーブルを指定する(全テーブルの中から特定の文字列が含まれるテーブルを指定) FOR vRec (SELECT table_name,column_name FROM user_tab_cols WHERE table_name LIKE '' OR table_name = '') LOOP BEGIN --//指定したテーブルの中から対象データを検索 FOR i IN tDATA.first..tDATA.last LOOP OPEN cur_search FOR 'SELECT COUNT(*) AS cnt FROM ' || vRec.column_name || 'LIKE ''%' || tDATA(i) || '%'''; FETCH cur_search INTO vCount; IF vCount > 0 THEN --//対象データがあった場合、そのテーブルと項目名、件数を出力する DBMS_OUTPUT.PUT_LINE( tDATA(i) || ':' || vRec.table_name || '.' || vRec.column_name || ' ' || vCount || '件あり' ); END IF; CLOSE cur_search; END LOOP; EXCEPTION --//例外処理 WHEN OTHERS THEN ERR_CODE := SQLCODE; ERR_MSG := SUBSTEB(SQLERRM,1,255); DBMS_OUTPUT.PUT_LINE('error:' || ERR_CODE || ' ' || ERR_MSG || ' ' || vRec.table_name || '.' || vRec.column_name ); END; END LOOP; END; / </code></pre> speasmen88 tag:crieit.net,2005:PublicArticle/17825 2021-12-06T07:35:52+09:00 2021-12-06T07:38:02+09:00 https://crieit.net/posts/Access-SQL-JOIN AccessをやりながらSQLもやる。「JOIN」 <p><a href="https://crieit.now.sh/upload_images/3a8a3aeb635ac4a18450abf585db9fb461ad3eb65a82f.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/3a8a3aeb635ac4a18450abf585db9fb461ad3eb65a82f.jpg?mw=700" alt="image" /></a><br /> 今の仕事がいやだというモチベーションによって、<br /> いまだかつてない熱量でAccessとSQLに本気で取り組んでいる。(大袈裟)</p> <p><a target="_blank" rel="nofollow noopener" href="https://www.amazon.co.jp/dp/4865103236/ref=cm_sw_em_r_mt_dp_YGXF681H83ZKYNYKZCT8">Microsoft Office Specialist Microsoft Accsess 2016 対策テキスト&問題集 (よくわかるマスター)</a></p> <p>という本を「2020年5月」にAmazonで購入していたらしい。</p> <p>途中まで進めていたのだけど、最近、最初からせっせとやり直ししている。</p> <p>前に作ったテンプレートを発見したが、日付は1年半前(2020年6月)だった。</p> <p>テーブルの章が終わってクエリに突入した。</p> <p>それで、この前紹介したYouTubeのおかげで、<br /> AccessでのSQLの表示の仕方がわかったので、表示してみたら、「INNER JOIN」と書いてある。</p> <p><a href="https://crieit.now.sh/upload_images/0aef939e06a11cf7d6151d4356db135961ad3b7e8dcde.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/0aef939e06a11cf7d6151d4356db135961ad3b7e8dcde.png?mw=700" alt="image" /></a></p> <p>知ってるけど知らない。(見たことあるけど覚えてない)</p> <p>調べる。</p> <p><a target="_blank" rel="nofollow noopener" href="https://qiita.com/ngron/items/db4947fb0551f21321c0">【INNER JOIN, LEFT JOIN , RIGHT JOIN】テーブル結合の挙動をまとめてみた【SQL】</a></p> <p>両方ともの表にないと表示されないのがINNER JOIN。</p> <p>他のもまとめて覚えよう。</p> <p>Access本の次の課題のSQL文を見たら「INNER JOIN」が何個もあって、気が遠くなった。<br /> 自動生成されているからこうなっているのか、それともこんな見た目が複雑な結合を書くのかな。<br /> (どこが区切りなのか分かりづらい)</p> <p>本の課題自体は、すでに出来上がっているものに一部の操作を加える手順が書いてあるので、<br /> 課題だけをやるならなんの支障もないのだが。</p> <p>まあ(Accessは)クエリが山場だ・・・。</p> <p>でもさらっとやって、あとで見直そう。<br /> 繰り返した方が記憶に定着するだろうし、進んでる気分になれるし。</p> <p>それにこんなところでてこずっている場合ではない。<br /> 総合演習問題が解けないと意味がない。</p> <p>パソコンを開けない時は本を開いている。<br /> <a target="_blank" rel="nofollow noopener" href="https://www.amazon.co.jp/dp/4295005096/ref=cm_sw_r_tw_dp_RR2VJ8KZCNX93YGSEEFC?_encoding=UTF8&psc=1">スッキリわかるSQL入門 第2版 ドリル222問付き! (スッキリわかる入門シリーズ) 中山清喬 </a></p> <p>6章の「集計とグループ化」まで読んだ。JOINは8章に出てくるらしい。</p> <p>この本も前に見ているはずだけど、すっきりさっぱり記憶にない。</p> <p>がんばる。</p> Hata tag:crieit.net,2005:PublicArticle/17816 2021-12-04T06:58:24+09:00 2021-12-18T06:53:28+09:00 https://crieit.net/posts/SQL-SQL-SQL SQLの勉強「SQL攻略」「SQL入門」 <p><a href="https://crieit.now.sh/upload_images/3a8a3aeb635ac4a18450abf585db9fb461aa9047c2ffe.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/3a8a3aeb635ac4a18450abf585db9fb461aa9047c2ffe.jpg?mw=700" alt="image" /></a></p> <h2 id="SQL攻略"><a href="#SQL%E6%94%BB%E7%95%A5">SQL攻略</a></h2> <p>「SQL攻略」<br /> <a target="_blank" rel="nofollow noopener" href="http://sql.main.jp/">http://sql.main.jp/</a></p> <p>ちょっと古いサイトで、httpだけど。。</p> <p>WEBサイト内でサクサク動かせるのはいい。</p> <h2 id="SQL入門"><a href="#SQL%E5%85%A5%E9%96%80">SQL入門</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://www.amazon.co.jp/dp/4844333933/ref=cm_sw_r_tw_dp_WXGCJQZYHXY3PX4Q7T0Q?_encoding=UTF8&psc=1">スッキリわかる SQL 入門 ドリル215問付き! (スッキリシリーズ) 中山 清喬 </a></p> <p>前にこの本を買って、途中まで読んだ。<br /> この本の問題はWEBサイトでできます!って書いてあって<br /> 確かにできるのだけど</p> <p>作りが・・・すごく使いづらい。<br /> 動くけど、使いづらい。もうちょっとなんとかならなかったのかな。</p> <p>普通、WEBサイトで入力しながら学習できます!って<br /> すごく勉強しやすいものだと思うのだけど。</p> <p>だったら、テーブルデータだけをダウンロードできるとか、の方が良かった。</p> <p>データを手入力でポチポチ入れるの大変だから、<br /> selectを使いたいときに、DB作ってテーブル作ってデータ入れてたらちょっと大変すぎる。</p> <p>まあ「DB作ってテーブル作ってデータ入れる」のの練習にはなるだろうけど・・・。</p> <p>会社にこの本が置いてあった。<br /> でもこの本じゃなくて別の本が見たかったなー。</p> <p>SQLってもっと複雑なイメージなんだけど、<br /> もしかしてそうでもないのかな?</p> <h2 id="SQL Server"><a href="#SQL+Server">SQL Server</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://www.amazon.co.jp/dp/4883376826/ref=cm_sw_r_tw_dp_4JSZH3R938Y34QNE324N?_encoding=UTF8&psc=1">SQL Server 2008の教科書―基礎から実践まで学べる 松本 美穂 </a></p> <p>前に(2018年頃?)この本中古で買ってやってみてた。</p> <p>役に立っているかどうかは、<br /> もう内容を忘れたので分からない。</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>本はたくさん(?)出てるので、いくつか図書館で予約して借りる予定。<br /> 借りてみて良さそうだったら、買うこともあるかもしれない。<br /> 重複している箇所がたくさんありそうだけど、ある程度までは練習と思ってやってみるのがいいかも。</p> Hata tag:crieit.net,2005:PublicArticle/16677 2021-02-08T15:25:27+09:00 2021-02-09T09:22:08+09:00 https://crieit.net/posts/Postgres-SQL-CUI Postgres SQL基本の書き方 <h1 id="PostgreSQLにおける主なデータ型"><a href="#PostgreSQL%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E4%B8%BB%E3%81%AA%E3%83%87%E3%83%BC%E3%82%BF%E5%9E%8B">PostgreSQLにおける主なデータ型</a></h1> <h3 id="文字型"><a href="#%E6%96%87%E5%AD%97%E5%9E%8B">文字型</a></h3> <div class="table-responsive"><table> <thead> <tr> <th>コマンド</th> <th>型</th> <th>意味</th> </tr> </thead> <tbody> <tr> <td>char(n)</td> <td>文字型</td> <td>n文字分の長さの固定長文字列</td> </tr> <tr> <td>varchar(n)</td> <td>文字型</td> <td>最大n文字の長さの可変長文字列</td> </tr> <tr> <td>text</td> <td>文字型</td> <td>文字数に上限がない可変長文字列</td> </tr> <tr> <td>smallint</td> <td>数値型</td> <td>-32768~32768の整数</td> </tr> <tr> <td>integer</td> <td>数値型</td> <td>-2147483648~2147483647の整数</td> </tr> <tr> <td>real</td> <td>数値型</td> <td>浮動小数点型で6桁の精度</td> </tr> <tr> <td>double precision</td> <td>数値型</td> <td>浮動小数点型で15桁の精度</td> </tr> <tr> <td>serial</td> <td>数値型</td> <td>1~2147483647の自動増分整数</td> </tr> <tr> <td>date</td> <td>日付/時刻型</td> <td>年月日</td> </tr> <tr> <td>timestamp</td> <td>日付/時刻型</td> <td>年月日時分秒</td> </tr> </tbody> </table></div> <h1 id="コマンドプロンプトから入る"><a href="#%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%97%E3%83%AD%E3%83%B3%E3%83%97%E3%83%88%E3%81%8B%E3%82%89%E5%85%A5%E3%82%8B">コマンドプロンプトから入る</a></h1> <pre><code>psql –U postgres </code></pre> <p>[CUI操作]</p> <h1 id="データベース一覧を表示"><a href="#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E4%B8%80%E8%A6%A7%E3%82%92%E8%A1%A8%E7%A4%BA">データベース一覧を表示</a></h1> <pre><code>¥l </code></pre> <p>アルファベットのエル</p> <h1 id="データベース作成"><a href="#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E4%BD%9C%E6%88%90">データベース作成</a></h1> <pre><code>CREATE DATABASE データベース名; </code></pre> <h1 id="データベースへ接続する"><a href="#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%81%B8%E6%8E%A5%E7%B6%9A%E3%81%99%E3%82%8B">データベースへ接続する</a></h1> <pre><code>¥c データベース名 </code></pre> <h1 id="キャンセル"><a href="#%E3%82%AD%E3%83%A3%E3%83%B3%E3%82%BB%E3%83%AB">キャンセル</a></h1> <pre><code>¥r </code></pre> <h1 id="テーブルの定義の確認"><a href="#%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%AE%E5%AE%9A%E7%BE%A9%E3%81%AE%E7%A2%BA%E8%AA%8D">テーブルの定義の確認</a></h1> <pre><code>¥d テーブル名 </code></pre> <h1 id="テーブルが存在することを確認"><a href="#%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%8C%E5%AD%98%E5%9C%A8%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%82%92%E7%A2%BA%E8%AA%8D">テーブルが存在することを確認</a></h1> <pre><code>¥dt </code></pre> <h1 id="CSVファイルをインポートする"><a href="#CSV%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E3%82%A4%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%88%E3%81%99%E3%82%8B">CSVファイルをインポートする</a></h1> <pre><code>¥copy テーブル名 from ’ファイルパス’ [各種オプション] </code></pre> <p>ファイルパスの後ろにはオプションとして「csv」と「header」を入力。<br /> これはインポートするのがCSVファイルで、そのファイルにはヘッダー行があることを意味</p> ponsuke tag:crieit.net,2005:PublicArticle/16670 2021-02-02T12:49:00+09:00 2021-02-03T10:45:40+09:00 https://crieit.net/posts/Access-SQL Access SQL書き方 <h1 id="用語"><a href="#%E7%94%A8%E8%AA%9E">用語</a></h1> <ul> <li>テーブル:シートのこと</li> <li>カラム:列</li> <li>レコード:テーブルとカラムが、データが保管される場所のこと</li> <li>フィールド:レコードを構成する1つ1つの要素のこと(Excelでいうセルに該当)</li> </ul> <h1 id="画面で定義するデータ型とSQL文のデータ型の対比表"><a href="#%E7%94%BB%E9%9D%A2%E3%81%A7%E5%AE%9A%E7%BE%A9%E3%81%99%E3%82%8B%E3%83%87%E3%83%BC%E3%82%BF%E5%9E%8B%E3%81%A8SQL%E6%96%87%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E5%9E%8B%E3%81%AE%E5%AF%BE%E6%AF%94%E8%A1%A8">画面で定義するデータ型とSQL文のデータ型の対比表</a></h1> <div class="table-responsive"><table> <thead> <tr> <th>設定するデータ型</th> <th>SQL文で使用するデータ型</th> <th>意味</th> </tr> </thead> <tbody> <tr> <td>短いテキスト</td> <td>TEXT(n), VARCHAR(n)</td> <td>文字列や計算対象としない数字。フィールドサイズにて255文字まで格納可能な文字数を設定できる</td> </tr> <tr> <td>長いテキスト</td> <td>LONGTEXT, LONGCHAR</td> <td>256文字以上になる長い文字列</td> </tr> <tr> <td>数値型(長整数型)</td> <td>INTEGER,INT,LONG</td> <td>-2,147,483,648〜2,147,483,647の整数データ</td> </tr> <tr> <td>数値型(整数型)</td> <td>SMALLINT,SHORT</td> <td>-32,768〜32,767の整数データ</td> </tr> <tr> <td>数値型(単精度浮動小数点)</td> <td>SINGLE,REAL</td> <td>-3.40E+38〜3.40E+38の浮動小数点データ</td> </tr> <tr> <td>数値型(倍精度浮動小数点)</td> <td>DOUBLE,FLOAT</td> <td>-1.79E+308〜1.79E+308の浮動小数点データ</td> </tr> <tr> <td>日付、時刻</td> <td>DATETIME</td> <td>日付や時刻のデータ</td> </tr> <tr> <td>オートナンバー型</td> <td>AUTOINCREMENT</td> <td>レコードを新規追加する度に固有の番号が自動入力されるデータ</td> </tr> </tbody> </table></div> <p><strong>数値型とは</strong>…数量や重量など計算対象となる数字。数値型は、フィールドサイズと組み合わせることで実質、それぞれ別のデータ型になります。</p> <h1 id="テーブルを作成するSQL文"><a href="#%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8BSQL%E6%96%87">テーブルを作成するSQL文</a></h1> <pre><code>CREATE TABLE テーブル名 (フィールド名 データ型,…); </code></pre> <h1 id="テーブルの削除"><a href="#%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%AE%E5%89%8A%E9%99%A4">テーブルの削除</a></h1> <pre><code>DROP TABLE テーブル名; </code></pre> <h1 id="主キー設定"><a href="#%E4%B8%BB%E3%82%AD%E3%83%BC%E8%A8%AD%E5%AE%9A">主キー設定</a></h1> <pre><code>CREATE TABLE テーブル名 (フィールド名 データ型 PRIMARY KEY,…); </code></pre> <p>※主キーにしたいフィールドの、データ型を指定した後ろに、半角スペースを入れて、「PRIMARY KEY」と入れる</p> <h1 id="複数の主キーを設定"><a href="#%E8%A4%87%E6%95%B0%E3%81%AE%E4%B8%BB%E3%82%AD%E3%83%BC%E3%82%92%E8%A8%AD%E5%AE%9A">複数の主キーを設定</a></h1> <pre><code>CONSTRAINT 主キー名 PRIMARY KEY (フィールド名1,フィールド名2,…) </code></pre> <h1 id="データを追加"><a href="#%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E8%BF%BD%E5%8A%A0">データを追加</a></h1> <pre><code>INSERT INTO テーブル名 (フィールド名1,フィールド名2,…) VALUES(追加する値1,追加する値2,…); </code></pre> <p>文字列を値として入力する際は、「’(シングルクォーテーション)」で囲みます</p> <h1 id="追加したレコードを参照(検索)する"><a href="#%E8%BF%BD%E5%8A%A0%E3%81%97%E3%81%9F%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E5%8F%82%E7%85%A7%EF%BC%88%E6%A4%9C%E7%B4%A2%EF%BC%89%E3%81%99%E3%82%8B">追加したレコードを参照(検索)する</a></h1> <p>「WHERE」句の後も「抽出条件」とだけ書きましたが、ここも基本的には<br /> フィールド名を使って「<strong>フィールド名=○○</strong>」というような指定をします<br /> 複数の条件を入れる際は「AND」や「OR」を使用。</p> <h1 id="データを更新する"><a href="#%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E6%9B%B4%E6%96%B0%E3%81%99%E3%82%8B">データを更新する</a></h1> <pre><code>UPDATE テーブル名 SET フィールド名=更新する値 WHERE 抽出条件; </code></pre> <p>VBAの変数と似ている部分は、例えばAとBの文字列を<br /> 結合した値でCフィールドを更新する場合、「<strong>SET C=A & B</strong>」とする</p> <h1 id="並べ替え"><a href="#%E4%B8%A6%E3%81%B9%E6%9B%BF%E3%81%88">並べ替え</a></h1> <pre><code>SELECT フィールド名 FROM テーブル名 WHERE 抽出条件 ORDER BY フィールド名 [ASC|DESC]; </code></pre> <p>フィールド名の後ろに何も書かないか「<strong>ASC</strong>」と入れると「<strong>昇順</strong>」に、「<strong>DESC</strong>」と入れると「<strong>降順</strong>」になります</p> <h1 id="集計"><a href="#%E9%9B%86%E8%A8%88">集計</a></h1> <p>1.フィールド名を指定せず「*」</p> <pre><code>SELECT COUNT(*) FROM テーブル名; </code></pre> <p>2.COUNT関数の引数で、フィールド名を指定する</p> <pre><code>SELECT COUNT(フィールド名) FROM テーブル名; </code></pre> <p>SELECT文などで、抽出結果が式の結果であったり、複数のテーブルを結合する際のテーブル名などには、別名(エイリアス名)を付けることが出来る</p> <pre><code>[別名をつけたい式やフィールド名、またはテーブル名] AS 別名 </code></pre> <p>「AS」と書いて、「別名」を入れる</p> <h1 id="集計する(GROUP)"><a href="#%E9%9B%86%E8%A8%88%E3%81%99%E3%82%8B%28GROUP%29">集計する(GROUP)</a></h1> <pre><code>SELECT グループ化するフィールド名,集計式 FROM テーブル名 WHERE 抽出条件 GROUP BY グループ化するフィールド名; </code></pre> <ul> <li><p>「GROUP BY」の後ろにグループ化するフィールド名を指定するのですが、同じものを、必ずSELECTの後ろにも書かないといけません。</p></li> <li><p>「GROUP BY」で集計した結果を抽出条件としたい場合は「<strong>HAVING</strong>」<br /> ※この場合は「WHERE」句で指定するわけではない</p></li> </ul> <h1 id="今あるテーブルから新しいテーブルを作る"><a href="#%E4%BB%8A%E3%81%82%E3%82%8B%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%8B%E3%82%89%E6%96%B0%E3%81%97%E3%81%84%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%82%92%E4%BD%9C%E3%82%8B">今あるテーブルから新しいテーブルを作る</a></h1> <pre><code>SELECT フィールド名 INTO 新しいテーブル名 FROM テーブル名 WHERE 抽出条件; </code></pre> <p>「FROM」の前に「INTO 新しいテーブル名」と入れるだけ</p> <p>※他のデータベースでは似たような機能を使う場合、「CREATE VIEW」を使ってビューと呼ばれるものを作成する必要がある</p> <h3 id="抽出した結果を既存のテーブルに追加"><a href="#%E6%8A%BD%E5%87%BA%E3%81%97%E3%81%9F%E7%B5%90%E6%9E%9C%E3%82%92%E6%97%A2%E5%AD%98%E3%81%AE%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%AB%E8%BF%BD%E5%8A%A0">抽出した結果を既存のテーブルに追加</a></h3> <p>「INSERT INTO」ステートメントの「VALUES」句の代わりに「SELECT」ステートメントを繋げて指定する</p> <pre><code>INSERT INTO 追加先テーブル名 SELECT フィールド名(式、リテラル値) FROM 追加元テーブル名 WHERE 抽出条件; </code></pre> <p>※SELECTの抽出結果の値が、必ず追加先テーブルのフィールドの数とそのデータ型に、合致しないといけない</p> <h1 id="条件式を使って抽出(IIF関数/あんま使わん)"><a href="#%E6%9D%A1%E4%BB%B6%E5%BC%8F%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E6%8A%BD%E5%87%BA%28IIF%E9%96%A2%E6%95%B0%2F%E3%81%82%E3%82%93%E3%81%BE%E4%BD%BF%E3%82%8F%E3%82%93%29">条件式を使って抽出(IIF関数/あんま使わん)</a></h1> <p>この関数はSQLの標準ではなく、使えるのは、Microsoft社製のデータベースのみ(Accessの他にSQLSeverがある)となります。</p> <pre><code>IIF(評価する式,[真の場合の値または式],[偽の場合の値または式]) </code></pre> <h1 id="年齢を計算(あんま使わん)"><a href="#%E5%B9%B4%E9%BD%A2%E3%82%92%E8%A8%88%E7%AE%97%28%E3%81%82%E3%82%93%E3%81%BE%E4%BD%BF%E3%82%8F%E3%82%93%29">年齢を計算(あんま使わん)</a></h1> <p>最初の引数には「’yyyy’」「生年月日」フィールドを使って、「2020年4月1日」を基準に何歳かを出しますので、次の引数には「生年月日」を、最後の引数には「#2020/4/1#」を<br /> 入れています。</p> <pre><code>DATEDIFF(計算に使用する時間単位,日付1,日付2) </code></pre> <h1 id="内部結合"><a href="#%E5%86%85%E9%83%A8%E7%B5%90%E5%90%88">内部結合</a></h1> <pre><code>SELECT 取得フィールド名 FROM 結合元テーブル名 INNER JOIN 結合先テーブル名 ON 結合元テーブルの結合キー = 結合先テーブルの結合キー; </code></pre> <p>それぞれのテーブルで、結合するキーが一致するレコードのみ抽出する結合方法</p> <h1 id="左外部結合"><a href="#%E5%B7%A6%E5%A4%96%E9%83%A8%E7%B5%90%E5%90%88">左外部結合</a></h1> <pre><code>SELECT 取得フィールド名 FROM 結合元テーブル名 LEFT JOIN 結合先テーブル名 ON 結合元テーブルの結合キー = 結合先テーブルの結合キー; </code></pre> <p>結合元のテーブルのレコード全件取得に対し、結合先のテーブルは結合キーが合致するレコードのみ結合する方法です。結合キーが合致しないレコードには「NULL」が入る</p> <h1 id="右外部結合"><a href="#%E5%8F%B3%E5%A4%96%E9%83%A8%E7%B5%90%E5%90%88">右外部結合</a></h1> <pre><code>SELECT 取得フィールド名 FROM 結合先テーブル名 RIGHT JOIN 結合元テーブル名 ON 結合元テーブルの結合キー = 結合先テーブルの結合キー; </code></pre> <p>左外部結合が反転した結合方法</p> ponsuke tag:crieit.net,2005:PublicArticle/16666 2021-02-01T17:27:32+09:00 2021-02-03T10:56:56+09:00 https://crieit.net/posts/SQL-6017bb74d787b 覚えておきたいSQL文 <h1 id="前書き"><a href="#%E5%89%8D%E6%9B%B8%E3%81%8D">前書き</a></h1> <p>この項目では自分が覚えておきたいと思ったSQL文と用法を記載しておく。<br /> Access VBAで使うことを想定して書いている。</p> <h2 id="CREATE TABLE"><a href="#CREATE+TABLE">CREATE TABLE</a></h2> <pre><code>CREATE TABLE テーブル名 (フィールド名 データ型,…) CONSTRAINT 主キー名 PRIMARY KEY (フィールド名1,フィールド名2,…); </code></pre> <p>テーブルを作成すコマンド。</p> <ul> <li>例<br />  CREATE TABLE 社員マスタ<br /> (<br /> 社員番号 TEXT(3),<br /> 社員名 TEXT(8),<br /> CONSTRAINT 社員マスタPK PRIMARY KEY (社員番号)<br /> );</li> </ul> <h3 id="データ型の設定"><a href="#%E3%83%87%E3%83%BC%E3%82%BF%E5%9E%8B%E3%81%AE%E8%A8%AD%E5%AE%9A">データ型の設定</a></h3> <div class="table-responsive"><table> <thead> <tr> <th>データ型名</th> <th>SQLで使用するデータ型</th> </tr> </thead> <tbody> <tr> <td>短めのテキスト</td> <td>TEXT(n),VARCHER (n)</td> </tr> <tr> <td>長いテキスト</td> <td>LONGTEXT,LONGCHR</td> </tr> <tr> <td>数値型(長整数型)</td> <td>INTEGER,INT,LONG</td> </tr> <tr> <td>数値型(整数型)</td> <td>SMLLINT,SHORT</td> </tr> <tr> <td>数値型(単精度浮動小数点)</td> <td>SINGLE,REAL</td> </tr> <tr> <td>数値型(倍制度浮動小数点)</td> <td>DOUBLE,FLOAT</td> </tr> <tr> <td>日付、時刻</td> <td>DATETIME</td> </tr> <tr> <td>オートナンバ型</td> <td>AUTOINCREMENT</td> </tr> </tbody> </table></div> <h2 id="INSERT INTO"><a href="#INSERT+INTO">INSERT INTO</a></h2> <pre><code>INSERT INTO テーブル名 (フィールド名1,フィールド名2,…) VALUES(追加する値1,追加する値2,…); </code></pre> <p>作成したテーブルにデータを追加する。<br /> * 例<br /> INSERT INTO 社員マスタ(社員番号,社員名) VALUES(’S01’,'立花 太郎');</p> <h2 id="SELECT"><a href="#SELECT">SELECT</a></h2> <pre><code>SELECT フィールド名 FROM テーブル名 WHERE 抽出条件; </code></pre> <p>テーブルからデータを検索する。<br /> * 例<br /> SELECT * FROM 社員マスタ</p> <h2 id="UPDATE"><a href="#UPDATE">UPDATE</a></h2> <pre><code>UPDATE テーブル名 SET フィールド名=更新する値 WHERE 抽出条件; </code></pre> <p>データの更新を行う。</p> <ul> <li>例<br /> UPDATE SET 社員名='田中 太郎' WHERE 社員番号=’S01’;</li> </ul> <h2 id="DELET"><a href="#DELET">DELET</a></h2> <pre><code> DELETE * FROM テーブル名 WHERE 抽出条件; </code></pre> <p>データの削除を行う。</p> <ul> <li>例<br /> DELET * FRPM 社員マスタ WHERE 社員番号='S01';</li> </ul> <h2 id="ORDER BY"><a href="#ORDER+BY">ORDER BY</a></h2> <pre><code>SELECT フィールド名 FROM テーブル名 WHERE 抽出条件 ORDER BY フィールド名 [ASC|DESC]; </code></pre> <p>データを昇順か降順かで、並び順を変える。<br /> * 例<br /> SELECT * FROM 社員マスタ WHERE 年齢 >= 10 ODER BY 年齢;</p> <h2 id="COUNT"><a href="#COUNT">COUNT</a></h2> <pre><code>SELECT COUNT(*) FROM テーブル名; </code></pre> <p>宣言したテーブルの件数や、フィールドの件数を出力する。<br /> * 例<br /> SELECT COUNT(*) FROM 社員マスタ;</p> <h2 id="AS"><a href="#AS">AS</a></h2> <pre><code>SELECT COUNT(*) AS 別名 FROM テーブル名; </code></pre> <p>項目を別名にしたい際に使う。<br /> * 例<br /> SELECT COUNT(*) AS 件数 FROM 社員マスタ;</p> kamakiri01234 tag:crieit.net,2005:PublicArticle/15593 2019-12-08T18:59:22+09:00 2019-12-09T10:28:21+09:00 https://crieit.net/posts/PostgreSQL PostgreSQL: とにかく簡単にトレンドやホットランキングを出したい <p>CGMなどを作っていると、現在人気の投稿をトレンドとしてランキング表示したいときがある<br /> これをSQLだけで実装する<br /> パフォーマンスも何も考えていないしテーブル構成にも依存しているし、さらには手動で調整するマジックナンバーさえある<br /> だが、簡単なのでとりあえずそれっぽい機能を付けたいとなったときに役立つかもしれない</p> <h1 id="環境"><a href="#%E7%92%B0%E5%A2%83">環境</a></h1> <p>PostgreSQL</p> <h1 id="テーブル構成1"><a href="#%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E6%A7%8B%E6%88%901">テーブル構成1</a></h1> <p>ランク付けの対象として投稿を表すPOSTテーブルがあり、閲覧数を表すVIEWテーブルと一対多のリレーションになっているとする(閲覧されるたびにVIEWテーブルのレコードが増えるパターン)</p> <p>また、各テーブルには作成日時created_atと更新日時updated_atがあるとする</p> <p><a href="https://crieit.now.sh/upload_images/eb23ee475f9ebc4e2f26928c82bdba9a5decb796a57a9.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/eb23ee475f9ebc4e2f26928c82bdba9a5decb796a57a9.png?mw=700" alt="Entity Relationship Diagram.png" /></a></p> <h1 id="SQL"><a href="#SQL">SQL</a></h1> <pre><code class="sql">SELECT "post".*, (COUNT(DISTINCT view)) as rank_point FROM "post" LEFT OUTER JOIN "view" ON "view"."post_id" = "post"."id" AND "view"."created_at" >= now() - interval '1 hour' GROUP BY post.id ORDER BY rank_point DESC; </code></pre> <h2 id="説明"><a href="#%E8%AA%AC%E6%98%8E">説明</a></h2> <p>LEFT OUTER JOINで結合する時にVIEWテーブルの条件を調整している<br /> <code>"view"."created_at" >= now() - interval '1 hour'</code>は現在時刻から1時間前までの間に作成されたVIEWのレコードのみ結合するという条件で、これをカウントしてrank_pointとし、post.idでグルーピングしたものを降順に並べれば1時間のうちで閲覧が多い投稿が順に並ぶはずである<br /> <code>interval '1 hour'</code>の部分は好きなように調整できる</p> <h1 id="テーブル構成2"><a href="#%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E6%A7%8B%E6%88%902">テーブル構成2</a></h1> <p>他の指標を使いたくなったらいくらでも追加できる<br /> 例えば投稿には一対多でコメントがあって、コメントの多さでも人気なことを表したいとする<br /> <a href="https://crieit.now.sh/upload_images/eb23ee475f9ebc4e2f26928c82bdba9a5decc0f9a666b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/eb23ee475f9ebc4e2f26928c82bdba9a5decc0f9a666b.png?mw=700" alt="Entity Relationship Diagram.png" /></a></p> <h1 id="SQL"><a href="#SQL">SQL</a></h1> <pre><code class="sql">SELECT "post".*, ( (COUNT(DISTINCT view) * 10) + (COUNT(DISTINCT comment) * 1000 ) ) / 2 as rank_point FROM "post" LEFT OUTER JOIN "view" ON "view"."post_id" = "post"."id" AND "view"."created_at" >= now() - interval '1 hour' LEFT OUTER JOIN "comment" ON "comment"."post_id" = "post"."id" AND "comment"."created_at" >= now() - interval '1 hour' GROUP BY post.id ORDER BY rank_point DESC; </code></pre> <h2 id="説明"><a href="#%E8%AA%AC%E6%98%8E">説明</a></h2> <p>VIEWに加えてCOMMENTも同じように結合する<br /> あとは適当に掛け算して桁を合わせてから指標の数で割ったものをrank_pointとするだけだ<br /> 桁を合わせるとは書いたが、重視したい指標に多めの数値を掛けるなりすれば重み付けもできる</p> <h1 id="蛇足"><a href="#%E8%9B%87%E8%B6%B3">蛇足</a></h1> <p>最初はちゃんと移動平均を出そうと思ってWindow関数とか調べたが、複数テーブルが絡むと面倒な記述になりそうでやめてしまった</p> <h1 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h1> <p><a target="_blank" rel="nofollow noopener" href="https://www.postgresql.jp/document/11/html/functions-datetime.html">9.9. 日付/時刻関数と演算子</a></p> sink tag:crieit.net,2005:PublicArticle/14984 2019-05-13T21:16:38+09:00 2019-05-14T00:18:53+09:00 https://crieit.net/posts/MySQL-5cd96026915a7 MySQLの負荷を軽くするための方法や設計の例を色々書いてみる <p>昔1年ほどアクセスが多く下手なことをするとすぐ重くなってしまうソーシャルゲームの開発&運用を行ったことがあります。MySQLの運用方法についてはそこで色々と叩きこまれたためその後の開発にも役立っていますが、せっかくですので色々と思いつく軽量化の方法を色々と例をあげつつ書いてみたいと思います。</p> <h2 id="前提"><a href="#%E5%89%8D%E6%8F%90">前提</a></h2> <p>最新のMySQLの挙動を全て正確に把握しているわけではありません。そのため「いや、ここはこういうやり方でも大丈夫なはずだけど…」という話も出てくると思います。ただ、とりあえず深く考えたり知ったりしていなくてもできる極端な方法をあげていくため、そのあたりの細かい部分は考慮していません。何かあればコメントで補足していただいたり、別途最新の仕様に基づいたより良い使い方ができる記事などを書いていただけると助かります。</p> <h2 id="シンプルなSQLしか使わない"><a href="#%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E3%81%AASQL%E3%81%97%E3%81%8B%E4%BD%BF%E3%82%8F%E3%81%AA%E3%81%84">シンプルなSQLしか使わない</a></h2> <p>基本的に、下記のようなシンプルなSQLのみを使います。</p> <pre><code class="sql">SELECT columns FROM table WHERE column = value ORDER BY order_column </code></pre> <h3 id="インデックスを効かせる"><a href="#%E3%82%A4%E3%83%B3%E3%83%87%E3%83%83%E3%82%AF%E3%82%B9%E3%82%92%E5%8A%B9%E3%81%8B%E3%81%9B%E3%82%8B">インデックスを効かせる</a></h3> <p>columnやorder_columnはインデックスが効くものだけを使います。EXPLAINで適宜確認しましょう。</p> <p>ただしデータが1000以下程度のテーブルであればそもそもインデックスを使ってくれなかったりするため、適当で良いです。ただ後のちデータが増える可能性があるテーブルはその時に調整などが必要になると面倒ですので、予め考えておいて作り直しがなるべく必要ではなくなるように作っておいたほうが良いとは思います。</p> <h3 id="便利な機能は使わない"><a href="#%E4%BE%BF%E5%88%A9%E3%81%AA%E6%A9%9F%E8%83%BD%E3%81%AF%E4%BD%BF%E3%82%8F%E3%81%AA%E3%81%84">便利な機能は使わない</a></h3> <p>下記のものは基本的には使いません。</p> <ul> <li>OR</li> <li>JOIN</li> <li>UNION</li> <li>サブクエリ</li> <li>関数</li> </ul> <p>等々。こういうものを使っていくとインデックスが効かなくなったりする場合がありますし、検索も各データ毎に更に検索を行う、というN+1問題的なものが発生し始めるので負荷が発生する原因となる可能性がでてきます。</p> <p>でも使えないとこまるんだけど…、という場合が出てくると思いますが、それは設計でカバーすることで実現していきます。引き続き色々と例をあげてみます。</p> <h2 id="わざわざデータを冗長にする"><a href="#%E3%82%8F%E3%81%96%E3%82%8F%E3%81%96%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E5%86%97%E9%95%B7%E3%81%AB%E3%81%99%E3%82%8B">わざわざデータを冗長にする</a></h2> <p>例えばフレンドについて考えてみます。Twitterのようなフォロー、フォロワーのような双方で独立した関係であれば問題はありませんが、「フレンド」というお互いのつながりのみで成り立つ場合の話です。たとえば下記のようなfriendsテーブルがあるとします。</p> <div class="table-responsive"><table> <thead> <tr> <th>カラム</th> <th>型</th> </tr> </thead> <tbody> <tr> <td>user_id</td> <td>int</td> </tr> <tr> <td>friend_id</td> <td>int</td> </tr> </tbody> </table></div> <p>これでAさんとBさんをフレンドとして繋げる場合、user_idにAさんのID、friend_idにBさんのIDを入れるとします。この場合、「Aさんのフレンド一覧」を検索する場合、下記のSQLが必要になります。</p> <pre><code class="sql">SELECT * FROM friends WHERE user_id = AさんのID OR friend_id = AさんのID </code></pre> <p>ORを使っているのでインデックスも効かなくなりますし悲惨です。Twitterのフォロー・フォロワーの関係と同じように、フレンドになった場合は1レコードではなく双方用の2レコードを入れることで、シンプルなSELECT文だけでフレンド一覧を取得できるようになります。</p> <pre><code class="sql">SELECT * FROM friends WHERE user_id = AさんのID </code></pre> <h2 id="わざわざテーブルの構造を冗長にする"><a href="#%E3%82%8F%E3%81%96%E3%82%8F%E3%81%96%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%AE%E6%A7%8B%E9%80%A0%E3%82%92%E5%86%97%E9%95%B7%E3%81%AB%E3%81%99%E3%82%8B">わざわざテーブルの構造を冗長にする</a></h2> <p>例えば記事にタグを付けられるように下記のようなbelongsToMany的な構成の3つのテーブルが存在するとします。</p> <pre><code>posts - post_tag - tags </code></pre> <p>post_tagはこんな感じです。</p> <div class="table-responsive"><table> <thead> <tr> <th>カラム</th> <th>型</th> </tr> </thead> <tbody> <tr> <td>post_id</td> <td>int</td> </tr> <tr> <td>tag_id</td> <td>int</td> </tr> </tbody> </table></div> <p>ここで、「あるタグがついている記事を検索する」という機能をつけたい場合、これは簡単です。</p> <pre><code class="sql">SELECT * FROM post_tag WHERE tag_id = 検索したいタグID </code></pre> <p>これでpost_idが分かるので、表示する10件程度だけpostsを取得すれば完了です。(LaravelのEager Loadingだとこれも勝手にやってくれます)</p> <p>では「あるユーザーが記事につけた全てのタグを取得」となるとどうでしょう。下記のSQLでしょうか?</p> <pre><code class="sql">SELECT DISTINCT post_tag.tag_id FROM post_tag LEFT JOIN posts ON posts.id = post_tag.post_id WHERE posts.user_id = ユーザーID </code></pre> <p>個人的にリレーションしたテーブルだけでWHEREを作ったSQLは危険だと思っています。インデックスも効きませんし、内部的には全データの関連データを取得しないと絞り込みができません。</p> <p>この場合、post_idとtag_idしかないpost_tagにuser_idも加え、保存時にuser_idも入れておくことでシンプルなSQLにすることができます。</p> <div class="table-responsive"><table> <thead> <tr> <th>カラム</th> <th>型</th> </tr> </thead> <tbody> <tr> <td>post_id</td> <td>int</td> </tr> <tr> <td>tag_id</td> <td>int</td> </tr> <tr> <td>user_id</td> <td>int</td> </tr> </tbody> </table></div> <pre><code class="sql">SELECT * FROM post_tag WHERE user_id = ユーザーID </code></pre> <h2 id="SQLを分けてシンプルにする"><a href="#SQL%E3%82%92%E5%88%86%E3%81%91%E3%81%A6%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E3%81%AB%E3%81%99%E3%82%8B">SQLを分けてシンプルにする</a></h2> <p>上記の話と少しかぶりますが、例えばユーザー名で記事を検索したい場合。JOINして検索するのではなく、まずユーザー名がヒットするユーザーのIDを先に取得しておきます。次にそれを使って<code>user_id IN (既に取得したユーザーID一覧)</code>で記事の検索は行います。</p> <p>クエリ数は増えますがJOIN先の検索が不要になるため状況によっては大幅な軽量化ができる場合があります。</p> <h2 id="表示時には複雑な検索をしない"><a href="#%E8%A1%A8%E7%A4%BA%E6%99%82%E3%81%AB%E3%81%AF%E8%A4%87%E9%9B%91%E3%81%AA%E6%A4%9C%E7%B4%A2%E3%82%92%E3%81%97%E3%81%AA%E3%81%84">表示時には複雑な検索をしない</a></h2> <p>どうしても複雑なSQLでランキング作成や集計処理を行わなければならない場合があると思います。この場合、表示する際に集計するのではなく、予め定期的なバッチ処理で集計してキャッシュに保存しておき、表示する際にはそれを表示するだけにすることで負荷をなくすことができます。</p> <h2 id="生SQLを見る"><a href="#%E7%94%9FSQL%E3%82%92%E8%A6%8B%E3%82%8B">生SQLを見る</a></h2> <p>ORMを使っているとよく分からず作ってしまう場合もありますので、生SQLを時々見るようにしましょう。大体のフレームワークが開発時にはログを垂れ流してくれていると思います。</p> <p>Laravel等はDebugbarがあり画面上でログを見れたりします。SQLの件数等も表示されていたりするため、誤ってループ内で更にSQLを実行してしまっている時なども気づきやすいです。</p> <p>特にリレーションのSQL等はフレームワークによって異なっていたりするため、確認せずに進めているととんでもないSQLが発行されていることなどもあります。ちょこちょこ確認し、気になったものはEXPLAINでチェックするようにしましょう。</p> <h2 id="やっても問題ないことはある"><a href="#%E3%82%84%E3%81%A3%E3%81%A6%E3%82%82%E5%95%8F%E9%A1%8C%E3%81%AA%E3%81%84%E3%81%93%E3%81%A8%E3%81%AF%E3%81%82%E3%82%8B">やっても問題ないことはある</a></h2> <p>色々書きましたが、実際にはやっても問題ないことはあると思います。バージョンによって可能なことは変わっていくでしょうし、MySQL以外だと最適化されているものもると思います。ただ、「あれ、どうだったっけ?」みたいなパターンが増えてくると思うため、まずは思考停止でなるべく怪しいことはしないようにし、どうしても必要な場合はちゃんと調査&検証を行って問題ないことがはっきりしたらやっていくと良いと思います。</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>色々と書きましたがそんなに多くはなかったですね…。</p> <p>何にしろこのように、手法や設計を変えることで負荷を与えなくても同じ動作を行えることというのはいくつもありますので、負荷的にあやしい処理をつくってしまいそうな場合にはまず設計を見直して軽量化できないかを考えてみると良いと思います。</p> <p>ちいさなシステムの場合はさほど問題ないかもしれませんが、大規模な場合はそれだけでサーバーを数台減らせて何十万円と節約できたのに、という場合も多々あると思います。メリットが「ほんのちょっと」というレベルではない場合もありますので、その都度色々と考えてみてください。</p> <p>この他にも色々と改善のアイデアはその都度出てくると思いますので、とにかくその都度DBちゃんの気持ちになって、どれだけたくさんのデータを見なきゃいけないかをその都度考えてみましょう。DBちゃんは現実に存在するのです…。無理に酷使してはいけません…。</p> <h2 id="まとめ2"><a href="#%E3%81%BE%E3%81%A8%E3%82%81%EF%BC%92">まとめ2</a></h2> <p>もっと色々知りたい方は、大規模ゲームについてや、大規模サービスについてのDB運用方法について書かれたスライドが数多く存在します。僕はもう見つけられないのですが是非探してみてください。単にクエリやスキーマの調整だけでなく、DB自体の分け方や様々なテクニックが色々なところで紹介されています。</p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/14939 2019-04-21T19:39:23+09:00 2019-04-23T15:51:13+09:00 https://crieit.net/posts/5308d8a3ed140ecc15e1310dad28e9e9 野球のデータを用いて選手間の「類似度」を計算する <h1 id="背景"><a href="#%E8%83%8C%E6%99%AF">背景</a></h1> <p>今回は統計の話に近くなってしまうのですが、選手を傾向に応じてグループ化できると面白いなーと思っていて、「野球 似ている 選手」などでググってみると、Baseball LABのコラムを見つけました。<br /> <a target="_blank" rel="nofollow noopener" href="http://www.baseball-lab.jp/column/entry/14/">コラム:「近い」選手とは?</a></p> <h1 id="類似度の計算について"><a href="#%E9%A1%9E%E4%BC%BC%E5%BA%A6%E3%81%AE%E8%A8%88%E7%AE%97%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6">類似度の計算について</a></h1> <p>現在データを集めている野球リーグでは、選手によって打席数の差がかなりあり、グラフを描いたときの二点間の距離(<strong>ユークリッド距離</strong>)の算出に打席数の偏りが反映されてしまうため、x,y軸に割合を採用することにしました。</p> <h2 id="ユークリッド距離とは"><a href="#%E3%83%A6%E3%83%BC%E3%82%AF%E3%83%AA%E3%83%83%E3%83%89%E8%B7%9D%E9%9B%A2%E3%81%A8%E3%81%AF">ユークリッド距離とは</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://ja.wikipedia.org/wiki/ユークリッド距離">ユークリッド距離 - wikipedia</a><br /> 要は2点間の距離です。今回は二次元を扱うので、皆さんご存知の三平方の定理を使います。</p> <h2 id="x,y軸に採用するもの"><a href="#x%2Cy%E8%BB%B8%E3%81%AB%E6%8E%A1%E7%94%A8%E3%81%99%E3%82%8B%E3%82%82%E3%81%AE">x,y軸に採用するもの</a></h2> <p>どのような指標を採用するかによって、類似度の現れ方が異なるので、<br /> 事前にChart.jsなどでグラフを書いていくつか検討した結果です。</p> <h3 id="打者の場合"><a href="#%E6%89%93%E8%80%85%E3%81%AE%E5%A0%B4%E5%90%88">打者の場合</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://ja.wikipedia.org/wiki/長打率">長打率(SLG)</a>:塁打の合計数を打席数で割った指標。</li> <li><a target="_blank" rel="nofollow noopener" href="https://ja.wikipedia.org/wiki/三振#打数_/_三振比率_%28At_Bats_per_Strikeout_:_AB/K%29">打数 / 三振比率 (At Bats per Strikeout : 三振のしにくさ)</a></li> </ul> <h3 id="MySQLでのユークリッド距離の計算"><a href="#MySQL%E3%81%A7%E3%81%AE%E3%83%A6%E3%83%BC%E3%82%AF%E3%83%AA%E3%83%83%E3%83%89%E8%B7%9D%E9%9B%A2%E3%81%AE%E8%A8%88%E7%AE%97">MySQLでのユークリッド距離の計算</a></h3> <p><strong>SELECT</strong><br /> 全体の成績を計算したものからselectして、ある選手のAB/Kと長打率の数値を渡して類似度が近い順に5つのレコードを取り出します。<br /> <strong>WHERE</strong><br /> 選手自身が類似度0として取れてしまうのでwhere句で除外します。<br /> 代表的な選手を類似度の候補として挙げるために条件を42打席以上としています。</p> <pre><code class="sql">select name, player_id, tpa, not_strike_out, SLG, SQRT(POW(?-not_strike_out,2)+POW(?-SLG,2)) as distance from( SELECT player_id, name, p.team_id as teamId, team_name, CASE WHEN (sum(hit)/sum(at_bats)) is null THEN 0 WHEN (sum(hit)/sum(at_bats)) is not null THEN (sum(hit)/sum(at_bats)) END as average, CASE WHEN ((sum(hit)+sum(twobase)*1+sum(homerun)*2)/sum(at_bats)) is null THEN 0 WHEN ((sum(hit)+sum(twobase)*1+sum(homerun)*2)/sum(at_bats)) is not null THEN ((sum(hit)+sum(twobase)*1+sum(homerun)*2)/sum(at_bats)) END as SLG, CASE WHEN ((sum(hit)+sum(four_ball))/sum(tpa))+((sum(hit)+sum(twobase)*1+sum(homerun)*2)/sum(at_bats)) is null THEN 0 WHEN ((sum(hit)+sum(four_ball))/sum(tpa))+((sum(hit)+sum(twobase)*1+sum(homerun)*2)/sum(at_bats)) is not null THEN ((sum(hit)+sum(four_ball))/sum(tpa))+((sum(hit)+sum(twobase)*1+sum(homerun)*2)/sum(at_bats)) END as OPS, (sum(hit)+sum(four_ball))/sum(tpa) as OBP, ((((sum(hit)+sum(twobase)*1+sum(homerun)*2)+0.26*sum(four_ball)-0.03*sum(strike_out)+3*sum(tpa))*(sum(hit)+sum(four_ball)+2.4*sum(tpa)))/(9*sum(tpa))-0.9*sum(tpa))*27/(sum(at_bats)-sum(hit)) as RC27, CASE WHEN sum(at_bats)/sum(strike_out) is null THEN 0 WHEN sum(at_bats)/sum(strike_out) is not null THEN sum(at_bats)/sum(strike_out) END as not_strike_out, CASE WHEN sum(four_ball)/sum(strike_out) is null THEN 0 WHEN sum(four_ball)/sum(strike_out) is not null THEN sum(four_ball)/sum(strike_out) END as bbk, CASE WHEN sum(at_bats)/sum(homerun) is null THEN 0 WHEN sum(at_bats)/sum(homerun) is not null THEN sum(at_bats)/sum(homerun) END as avg_homerun, CASE WHEN sum(rbi)/sum(at_bats) is null THEN 0 WHEN sum(rbi)/sum(at_bats) is not null THEN sum(rbi)/sum(at_bats) END as avgRbi, sum(tpa) as tpa, sum(at_bats) as at_bats, sum(hit) as hit, sum(rbi) as rbi, sum(four_ball) as four_ball, sum(strike_out) as strike_out, sum(twobase) as twobase, sum(homerun) as homerun FROM batting_sum b INNER JOIN game g on b.game_id=g.game_id INNER JOIN player p on b.player_id=p.id INNER JOIN team t on t.team_id=p.team_id group by player_id)p where tpa>=42 and player_id <> ? order by distance asc limit 5 </code></pre> <h3 id="投手の場合"><a href="#%E6%8A%95%E6%89%8B%E3%81%AE%E5%A0%B4%E5%90%88">投手の場合</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://ja.wikipedia.org/wiki/WHIP">WHIP(Walks plus Hits per Inning Pitched)</a>:与四球・被安打数合計の合計を投球回数で割ったもの。</li> <li><a target="_blank" rel="nofollow noopener" href="https://ja.wikipedia.org/wiki/三振#奪三振率">奪三振率</a>:奪三振率=奪三振数× <strong>5</strong> ÷投球回</li> </ul> <h3 id="MySQLでのユークリッド距離の計算"><a href="#MySQL%E3%81%A7%E3%81%AE%E3%83%A6%E3%83%BC%E3%82%AF%E3%83%AA%E3%83%83%E3%83%89%E8%B7%9D%E9%9B%A2%E3%81%AE%E8%A8%88%E7%AE%97">MySQLでのユークリッド距離の計算</a></h3> <pre><code class="sql">select player_id, name, WHIP, strike_avg, inning, SQRT(POW(?-WHIP,2)+POW(?-strike_avg,2)) as distance from ( SELECT p.player_id, name, pl.team_id as teamId, team_name, sum(runs)/sum(inning)*5 as era, (sum(hit)+sum(four_ball))/sum(inning) as WHIP, sum(four_ball)/sum(inning)*5 as BB5, sum(strike_out)/sum(inning)*5 as strike_avg, sum(inning) as inning, sum(pa) as pa, sum(hit) as hit, sum(homerun) as homerun, sum(four_ball) as four_ball, sum(strike_out) as strike_out, sum(runs) as runs, sum(complete) as complete, sum(shutout) as shutout, sum(win) as win, sum(lose) as lose, sum(save) as save, p3.run_support FROM pitching p inner join game g on g.game_id=p.game_id inner join player pl on p.player_id=pl.id inner join team t on t.team_id=pl.team_id left outer join( SELECT p.player_id as player_id, sum(p2.runs)/sum(p2.inning)*5 as run_support FROM pitching p inner join game g on p.game_id=g.game_id inner join pitching p2 on p.game_id=p2.game_id and p.myteam_id!=p2.myteam_id group by player_id)p3 on p3.player_id=p.PLAYER_ID group by player_id)p where player_id<>? and inning >= 18 order by distance asc limit 5 </code></pre> <h1 id="実装結果"><a href="#%E5%AE%9F%E8%A3%85%E7%B5%90%E6%9E%9C">実装結果</a></h1> <p><a target="_blank" rel="nofollow noopener" href="https://jcblscore-react.netlify.com/player/show/1">野球リーグスコア管理システム</a>の方に改修内容を反映しています。</p> <h2 id="選手例1"><a href="#%E9%81%B8%E6%89%8B%E4%BE%8B1">選手例1</a></h2> <p>この選手の場合は投球は軟投派の投手との類似度が高いことが示せています。<br /> <strong>軟投派の場合は奪三振率が低めになる傾向があります。</strong><br /> 打者としてはパワーヒッターで三振のしにくい選手が類似選手として挙がっています。<br /> <a href="https://crieit.now.sh/upload_images/c757f121bd19245f1a30d36c3424b0625cbc467eab1ef.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c757f121bd19245f1a30d36c3424b0625cbc467eab1ef.jpg?mw=700" alt="image" /></a></p> <h2 id="選手例2"><a href="#%E9%81%B8%E6%89%8B%E4%BE%8B2">選手例2</a></h2> <p>この選手の場合は<strong>速球派</strong>で<strong>長打力</strong>のある選手が類似度が高いことを示しています。<br /> <a href="https://crieit.now.sh/upload_images/c3dfcfa14baac17d4d2396730359de445cbc47f573614.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c3dfcfa14baac17d4d2396730359de445cbc47f573614.jpg?mw=700" alt="image" /></a></p> ckoshien tag:crieit.net,2005:PublicArticle/14900 2019-04-02T18:05:06+09:00 2019-04-02T18:09:36+09:00 https://crieit.net/posts/SQL-NULL-0 SQLでNULLを0として出力する方法 <p>MySQLで外部結合したときに、COUNT()の結果が0件だとNULL値が返却されてしまったため、NULLを0として表示する関数を調べました。<br /> ついでに他のDBについても書きました。</p> <h2 id="関数"><a href="#%E9%96%A2%E6%95%B0">関数</a></h2> <p><strong>IFNULL (a, 0)</strong><br /> <a target="_blank" rel="nofollow noopener" href="https://dev.mysql.com/doc/refman/5.6/ja/control-flow-functions.html#function_ifnull">MySQL</a>、<a target="_blank" rel="nofollow noopener" href="http://www.sqlitetutorial.net/sqlite-functions/sqlite-ifnull/">SQLite</a></p> <p><strong>COALESCE (a, 0)</strong><br /> <a target="_blank" rel="nofollow noopener" href="https://dev.mysql.com/doc/refman/5.6/ja/comparison-operators.html#function_coalesce">MySQL</a>、<a target="_blank" rel="nofollow noopener" href="http://www.sqlitetutorial.net/sqlite-functions/sqlite-coalesce/">SQLite</a>、<a target="_blank" rel="nofollow noopener" href="https://www.postgresql.jp/document/10/html/functions-conditional.html">PostgreSQL</a>、<a target="_blank" rel="nofollow noopener" href="https://docs.oracle.com/cd/E96517_01/sqlrf/COALESCE.html#GUID-3F9007A7-C0CA-4707-9CBA-1DBF2CDE0C87">Oracle</a></p> <p><strong>NVL (a, 0)</strong><br /> <a target="_blank" rel="nofollow noopener" href="https://docs.oracle.com/cd/E96517_01/sqlrf/NVL.html#GUID-3AB61E54-9201-4D6A-B48A-79F4C4A034B2">Oracle</a></p> <p><strong>ISNULL (a, 0)</strong><br /> <a target="_blank" rel="nofollow noopener" href="https://docs.microsoft.com/ja-jp/sql/t-sql/functions/isnull-transact-sql?view=sql-server-2017">SQL Server</a></p> <h2 id="使い方"><a href="#%E4%BD%BF%E3%81%84%E6%96%B9">使い方</a></h2> <p>第1引数(この場合カラムa)がNULLだった場合、関数は第2引数(この場合は0)を返します。<br /> 第1引数がNULLでなければ、関数はそのまま第1引数を返します。</p> <p>これらは同じような使い方ができますが、COALESCE()だけ仕様が異なり、引数を無限に設定することができます。<br /> COALESCE()は第1引数から順に「その値がNULLかどうか」を判定し、NULLであれば次の引数へ、NULLでなければその値を返却します。<br /> 全ての引数がNULLだった場合は、諦めてNULLを返却します。</p> ウラル tag:crieit.net,2005:PublicArticle/14865 2019-03-10T18:30:03+09:00 2019-03-10T18:30:03+09:00 https://crieit.net/posts/SQL-5c84d91b3c1cf SQLデータベースの照合順序の変更 <p>USE master;<br /> GO<br /> ALTER DATABASE MyOptionsTest<br /> COLLATE** French_CI_AS **;<br /> GO</p> <p>--Verify the collation setting.<br /> SELECT name, collation_name<br /> FROM sys.databases<br /> WHERE name = N'MyOptionsTest';<br /> GO</p> Azure Sheep tag:crieit.net,2005:PublicArticle/14607 2018-11-19T22:22:08+09:00 2018-11-19T22:22:08+09:00 https://crieit.net/posts/SQL 開発時はSQLのログを見よう <p>最近はSQLを利用したアプリケーションを開発する際、ほとんどORMを使うのが当たり前になってきていると思います。しかし実際に実行されるクエリを確認しないと大変なことになることもあるため、開発時のログをある程度は見ながら進めた方が良いと思います。</p> <h2 id="実行したSQLを見る方法"><a href="#%E5%AE%9F%E8%A1%8C%E3%81%97%E3%81%9FSQL%E3%82%92%E8%A6%8B%E3%82%8B%E6%96%B9%E6%B3%95">実行したSQLを見る方法</a></h2> <p>例えばPHPのフレームワークの場合、LaravelであればDebugbar、CakePHPであればDebugKitを導入することで、ページを開いた際に実行されたSQLの一覧を見たりすることができます。</p> <p><a href="https://crieit.now.sh/upload_images/b0e1b116fac539b7d0a3c9a41774dda55b0d18a8ddb66.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b0e1b116fac539b7d0a3c9a41774dda55b0d18a8ddb66.png?mw=700" alt="" /></a></p> <p>その他の言語やフレームワーク、例えばRuby on RailsやPhoenix等は、開発サーバーを実行している際にSQLの履歴が流れてくるためそれによって確認することができます。</p> <h2 id="実際に何を見たら良いのか"><a href="#%E5%AE%9F%E9%9A%9B%E3%81%AB%E4%BD%95%E3%82%92%E8%A6%8B%E3%81%9F%E3%82%89%E8%89%AF%E3%81%84%E3%81%AE%E3%81%8B">実際に何を見たら良いのか</a></h2> <p>基本的に見たほうが良いところというのは、重いSQLではないかと、SQLの実行回数あたりになると思います。</p> <p>普通のシンプルなSQLであればだいだい問題ないとは思いますが、JOINしていたりサブクエリを含んでいたり関数を使っていたりする場合、そのSQLのEXPLAINで確認してちゃんとインデックスが効いているかなどを見たほうが良いです。ただ、このあたりはスキーマを検討する際にだいたい考えられていたりすることもあるので、大丈夫なことも多いかもしれません。</p> <p>もう一つ問題となるのが、実行回数です。以前僕が既存のプロジェクトに途中から参加したことがあったのですが、一ページ内で3000以上のクエリを実行しているページがいくつもありました。たしかカレンダーとか、データ一覧のそれぞれのデータに対してループを行い、更に取得したデータに対してもループしてSELECTを実行する、みたいな感じでそれくらいになってしまっていたと思います。</p> <p>なんでこんな事になっていたのかと言うと、それはLaravelのプロジェクトだったのですが、結局Debugbarを入れていなかったためクエリの実行回数が可視化されておらず、作成した本人も全く気づいていなかったような状態でした。後で僕がDebugbarを入れたため、そのページを触った時に気づきました。</p> <p>この場合の解決法としては、ループの中でSELECTを実行せず、最初に1回必要なデータを全部取得しておき、ループの中ではそれを参照するだけにする、という形にすればほとんどSELECTを実行する必要がなくなります。</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>今もしSQLを利用しているプロジェクトで実行しているSQLを全く見ていない、という場合、ちょろっと見てみると良いと思います。</p> <p>特に炎上中のプロジェクトの場合、誰もそのあたり考える余裕もなく適当に作られている場合があるため、怖いもの見たさで可視化してみるとよいかもしれませんよ…。</p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/14560 2018-10-04T14:04:32+09:00 2018-10-30T15:57:50+09:00 https://crieit.net/posts/Laravel-Model-SQL LaravelのModelからSQLを直接実行する方法 <p>LaravelでSQLを直接実行する方法というのは、一般的には下記のようにDBのFacadeを使用する方法になる。</p> <pre><code class="php">DB::statement($query); </code></pre> <p>Laravelだと上記で問題ないのだが、EloquentはLaravelでなくても使用できるので、プロジェクトに個別にEloquentを入れている場合はDBファザードが無いため上記が実行できない。</p> <p>その場合、モデル自体からDBファザードと同じものを取得する方法があるためそれで同じ様にしてSQLを実行することができる。</p> <p>まず適当にモデルのインスタンスを作成する。</p> <pre><code class="php">$user = new User; </code></pre> <p>あとは同様。</p> <pre><code class="php">$user->getConnection()->statement($query); </code></pre> だら@Crieit開発者