SQLアンチパターンを読んだ人がまとめた記事を読んだ1

失敗から学ぶRDBの正しい歩き方を買おうとしたんだけど、3000円くらいするので、先にSQLアンチパターンをちょっと勉強してから買うことにした。本当はオライリーSQLアンチパターンも読んだ方がいいんだけどこれも3000円くらいするので、先にネットで齧ろうと思う。読んだのはこの記事です。

ざっくり見た所感

アンチパターンに名前がついている

デザインパターンでいうシングルトンパターンとか、リポジトリパターンとか、名前を聞いて他の人と考えを共有しやすいのでいいですね。

結構有名な本なので別の職場の人との話題にしやすそう

今の自分の会社みたいに、同じ会社の人がいろんな現場に行ってていろんな言語やってると、共通の話題ってAWSとか、JSとかの話になりがちなんですが(みんなだいたい使ってるから)、DBもそのうちの一つなので、アンチパターンの話はしやすそうですね。

ふわっと、「これはやらないだろ」と思ってることも、じゃあなんで?って聞かれた時に答えられる内容が書いてある気がする

内容を読んだ所感とかメモ

1部 データベース論理設計のアンチパターン

1章 Jaywalking(信号無視)

「列にカンマ区切りでユーザを格納」!!!!? 絶対やらないですけど都市伝説では聞いたことがあります。分けられるデータを無理やり一つのレコードに詰めるのはやめた方がいいと思います。多分こういうのやる人は「一レコードで全部取れた方がいいでしょ」とか思うんでしょうけど、アプリケーションから一つ一つのユーザをカンマ区切りで取得するのも面倒です。

2章 Naive Trees(素朴な木)

「ブログのコメント欄をスレッド形式で見れるようにしたいよね・・・」の時にどんなテーブル設計をするかって話らしい。 「孤児になったの行を綺麗にするために定期的にスクリプトを実行しなければ」→個人的には、すでにコメントがついているメッセージが削除される場合、それを物理削除せずに、削除フラグ立てればいいのではと思った。で、表示は「すでに削除済みです」みたいな。 階層を同じカラム内でスラッシュ区切りで書くのは微妙かなと思う。でも、この本、「こういう検索はRDBでは問題なくできるからこのやり方は問題ない」みたいな書かれ方してて良いな。スラッシュ区切りは無しだと思うけど。

3章 ID Required(とりあえずID)

主キーはレコードが一意であることを示すキーだから大事だよって話。「疑似キー(idなど) : 対象領域をモデル化したテーブルに意味を持たない人工的な値を格のして主キーとする」。だからって行って全部のテーブルでIDを持たなきゃいけないわけじゃない。

4章 Keyless Entry(外部キー嫌い)

とりあえず外部キー使おうねって話。

5章 EAV (エンティティ・アトリビュート・バリュー)

同じテーブルの同じカラムには同じ属性のデータを入れようっていう話。

6章 ポリモーフィック関連

ヨクワカラナカッタ・・・。正規化しすぎてよくわからなくなっちゃう感じか?

7章 マルチカラムアトリビュート(複数列属性)

コレモヨクワカラナカッタ・・・。ただし、よくあるユーザの電話番号をどう持つかって話は、仕様にもよるけど自宅電話番号と携帯電話番号の2カラム用意して両方NULL可にすれば良いと思う(緊急連絡先や職場の電話番号が必要ならまた別にもつ)。どちらかの登録が必須の場合は、もうアプリケーションで制御。クライアント側とサーバ側で制御すればほぼ安全でしょ。

8章 Metadata Tribble(メタデータ大増殖)

同じデータが入るのに年度別とかにテーブルわけちゃだめだよって話。ソースの修正も必要になるよね・・・やだな。

続きはまた今度。

Safariでlocation.hrefでの遷移ができないからaタグ生成して遷移させた

JSで遷移先を決めたいときに下記みたいなことすることあるじゃないですか。

<html>
<body>
  <a href="#" id="url1" onclick="transition1()">リンク</a>
</body>
</html>

<script>
function transition1(){
  location.href = "https://" + "www.google.co.jp";
}
</script>

Chromeでは動作して、JS内でlocation.hrefしたURLに遷移するんですけど、Safariだと動作しませんでした。

自分が試した時は、この記事のようなdocument.URLを使ってもだめだったし、

この記事に書かれているような2バイト文字も含まれていないURLだったし、

この記事に書かれているようなsetTimeoutを試してもだめだった。

じゃあどうしたかというと、aタグは使わずbuttonタグを使用して、JS内でa要素を生成してclick()して遷移させた。

こういう感じ。

<html>
<body>
  <button id="url2" onclick="transition2()">リンク</a>
</body>
</html>

<script>
function transition2(){
  var a = document.createElement('a');
  a.href = "https://" + "www.google.co.jp";
  a.click();
}
</script>

言語処理100本ノック05(C#)

05. n-gram

与えられたシーケンス(文字列やリストなど)からn-gramを作る関数を作成せよ.この関数を用い,"I am an NLPer"という文から単語bi-gram,文字bi-gramを得よ.

  • n-gramとは、任意の文字列や文書を連続したn個の文字で分割するテキスト分割方法。特に,nが1の場合をユニグラム(uni-gram),2の場合をバイグラム(bi-gram),3の場合をトライグラム(tri-gram)と呼ぶ。
  • 文字単位のn-gramは、n文字単位で分割するが、単語単位のn-gramはn単語単位で分割する。だから分割前に形態素解析を行い単語単位での分割が必要。

→英語の場合の文字単位の分割時、スペースはどうなるのだ?

  • n-gramについてわかりやすい記事はこれだと思う。

日本語の文章で文字N-gram

そもそもN-gramがよくわからなかったので、さらっと調べて実装したんだけど、英語みたいに単語と単語の間にスペースがある場合は使えないし、これは単語と文字のN-gramは別のアルゴリズムを使うしかないことがわかったので、とりあえず墓場として置いておく。 単語N-gramを実現するためには、形態素解析を実施する必要があるが、paiza.IOでMeCabが利用できず、利用できるって書いてあるので問い合わせをしているところ。

using System.Linq;
using System.Collections.Generic;

public class MainClass{
    public static void Main(){

        GetNgramList(2, "今日はいい天気ですね。").ForEach(item => System.Console.WriteLine(item));
    }
    
    public static List<string> GetNgramList(int n, string target)
    {
        List<string> ngramList = new List<string>();
        for(var i=0; i<target.Length-n+1; i++)
        {
            ngramList.Add(target.Substring(i, n));
        }
        return ngramList;
    }
}

言語処理100本ノック04(C#、Java)

04. 元素記号

"Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."という文を単語に分解し,1, 5, 6, 7, 8, 9, 15, 16, 19番目の単語は先頭の1文字,それ以外の単語は先頭に2文字を取り出し,取り出した文字列から単語の位置(先頭から何番目の単語か)への連想配列(辞書型もしくはマップ型)を作成せよ.

C#

using System;
using System.Collections.Generic;
using System.Linq;

public class Hello{
    public static void Main(){
        var text = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can.";
        var words = text.Split(' ');
        
        //1, 5, 6, 7, 8, 9, 15, 16, 19番目の単語は先頭の1文字
        int[] positionA = {1, 5, 6, 7, 8, 9, 15, 16, 19};
        //それ以外の単語は先頭に2文字
        
        Dictionary<string, int> result = new Dictionary<string, int>();
        int i=1;
        foreach(var word in words){
            string symbol = null;
            int position = 0;
            if(Array.IndexOf(positionA, i) != -1){
                //最初の1文字を取得
                symbol = word.Substring(0, 1);
                position = 1;
            }else{
                //最初の2文字を取得
                symbol = word.Substring(0, 2);
                position = 2;
            }
            result.Add(symbol, position);
            i++;
        }
    }
}

Java

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        String target = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can.";
        String[] targetArray = target.split(" ");
        List<Integer> firstChar = Arrays.asList(1, 5, 6, 7, 8, 9, 15, 16, 19);
        
        Map<String, Integer> result = new HashMap<>();
        for(int i=0; i<targetArray.length; i++)
        {
            var item = targetArray[i];
            if(firstChar.contains(i+1)){
                result.put(item.substring(0, 1), i+1);
            }else{
                result.put(item.substring(0, 2), i+1);
            }
        }
    }
}

言語処理100本ノック03(C#)

03. 円周率

"Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."という文を単語に分解し,各単語の(アルファベットの)文字数を先頭から出現順に並べたリストを作成せよ.

C#

  • 正規表現System.Text.RegularExpressionsパッケージを使う
using System.Text;
using System.Text.RegularExpressions;

public class Hello{
    public static void Main(){
        string text = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics.";
        string[] words = text.Split(" ");
        var pattern = @"[^a-zA-z0-9]";
        var resultList = new List<int>();
        foreach(var word in words){
            var charList = word.Split("").AsList();
            int i=0;
            foreach(var c in charList){
                if(!Regex.IsMatch(c, pattern)){
                    charList.RemoveAt(i);
                    continue;
                }
                i++;
            }
            resultList.Add(charList.Count());
        }
        resultList.Select(item => System.Console.WriteLine(item + " "));
    }
}

言語処理100本ノック02(C#)

02. 「パトカー」+「タクシー」=「パタトクカシーー」

「パトカー」+「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ.

C#

using System.Text;

public class Hello{
    public static void Main(){
        var patrolCar = "パトカー".ToCharArray();
        var taxi = "タクシー".ToCharArray();
        StringBuilder sb = new StringBuilder();
        
        for(var i=0; i<patrolCar.Length; i++){
            sb.Append(patrolCar[i]);
            sb.Append(taxi[i]);
        }
        System.Console.WriteLine(sb.ToString());
    }
}

言語処理100本ノック01(C#、Java)

01. 「パタトクカシーー」

「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ.

C#

using System.Text;
using System.Linq;

public class MainClass{
    public static void Main(){
        var targetArray = "パタトクカシーー".ToCharArray();
        StringBuilder sb = new StringBuilder();
        targetArray.Where((c, index) => index%2 == 0).ToList().ForEach(c => sb.Append(c));
        
        System.Console.Write(sb.ToString());
    }
}

Java

  • StreamAPI使いたかったけど標準ではindexが使えなくてやめた。
  • カタカナに対してString#toCharArray()するとcharの配列になってくれなくて調べたら、charは1バイト文字専用だからだった。
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        
        var list = Arrays.asList("パタトクカシーー".split(""));

        StringBuilder sb = new StringBuilder();
        for(int i=0; i<list.size(); i++){
            if(i%2 == 0){
                sb.append(list.get(i));
            }
        }
        System.out.println(sb.toString());
    }
}

Swift

var array = "パタトクカシーー".map { String($0) }
var resultStr = "";
for i in 0...array.count-1 {
    if(i%2 == 0){
        resultStr.append(array[i])
    }
}
print(resultStr)