重複排除して、他カラムも取得できるDISTINCT ON

 

SQLの話です。

使ったもの → Postgres

 

ここにこんなテーブルがあります。

# SELECT * FROM duplication_table;
id | sub_id | name | price | description
—-+——–+——————+——-+———————-
1 | 1 | 普通の牛乳 | 190 | 生乳100%
1 | 2 | 濃厚な牛乳 | 240 | 一番好きなやつ
1 | 3 | 生乳30%の牛乳 | 180 |
2 | 1 | スーパーなカップ | 120 | 結局これ買っちゃう
2 | 2 | ハーゲンなダッツ | 250 | たまに買っちゃう
3 | 1 | ハッピーなターン | 150 | 粉が多いのもあるよね

 

Q.重複idは排除してidを知りたい時ってどうしてますか?

A.DISTINCTですよね!

# SELECT DISTINCT(id) FROM duplication_table;
id
—-
1
3
2

 

 

まあ、これはよく見る。

 

 

Q.「他のカラム情報も欲しいんや!」って時ありません?

A.朗報です!!DISTINCT ONってのがあります!

# SELECT DISTINCT ON(id)* FROM duplication_table;
id | sub_id | name | price | description
—-+——–+——————+——-+———————-
1 | 1 | 普通の牛乳 | 190 | 生乳100%
2 | 1 | スーパーなカップ | 120 | 結局これ買っちゃう
3 | 1 | ハッピーなターン | 150 | 粉が多いのもあるよね

ほらね?けど、これどの条件で絞ってるんだっけ?

謎ですね。sub_idっぽいけど。。。。。

 

id中の最小priceを残して、重複排除

# SELECT DISTINCT ON(id)* FROM duplication_table ORDER BY id,price;
id | sub_id | name | price | description
—-+——–+——————+——-+———————-
1 | 3 | 生乳30%の牛乳 | 180 |
2 | 1 | スーパーなカップ | 120 | 結局これ買っちゃう
3 | 1 | ハッピーなターン | 150 | 粉が多いのもあるよね

こうすれば全カラム取得できました!!!

 

LINE@で苦手な漢字を読んでもらう

 

私、漢字苦手です。
(もっと広くいうと日本語が苦手です。)

真面目なんで読めない漢字はググる時もあるんですが、
ブラウザ開いて、検索して、それっぽい読み仮名探すの面倒!

 

 

「そうだ!LINEBotに読んでもらおう!」

 

 

使ったもの
> heroku
> dropbox
> yahoo!のAPIのテキスト解析

サーバーはherokuで用意して、dropboxでデプロイ
yahoo!のAPIを使って、漢字をひらがなにしてもらうといった感じです。

参考:https://codezine.jp/article/detail/9810

 

できたbotはこんな感じ

LINEのbot「漢字を読む人」

 

「外郎」とか初見でした!

 

 

index.phpに以下を追記しただけです。楽しました。

if (preg_match('/^Read/', $event->getText())) {
      $queryString = str_replace('Read ', '', $event->getText());
      $accessToken = "yahoo!デベロッパーのトークン";
      $url = "https://jlp.yahooapis.jp/FuriganaService/V1/furigana?appid=". $accessToken . "&grade=1&sentence=" . $queryString;
      $responsexml = simplexml_load_file($url);
      $bot->replyText($event->getReplyToken(), 'おそらく「' . $responsexml->Result->WordList->Word->Furigana . '」と読みます。');
  }

INNER JOINとOUTER JOIN

テーブルって結合すると便利ですよね。

結合しないと得たいものを得ることができないことがよくあります。

 

 

しかし、右にJOINする?左?いや、INNER?

ってなる

結果、副問い合わせをする。

パフォーマンス的にどうなの?ってなる。

 

 

ということで、今日の目標は

JOIN使いこなせるようになろう!

 

<データ>

# SELECT * FROM parent_table ORDER BY id;
id | name
—-+——–
1 | サザエ
2 | フネ
3 | タイ子

 

# SELECT * FROM child_table ORDER BY id;
id | name | parent_id
—-+——–+———–
1 | サザエ | 2
2 | カツオ | 2
3 | ワカメ | 2
4 | タラ     | 1
5 | イクラ | 3
6 | タマ     |

 

 

まずは、INNER JOIN

・child_tableにINNNER JOINするパターン

# SELECT * FROM child_table INNER JOIN parent_table ON child_table.parent_id = parent_table.id ORDER BY child_table.id;
id | name | parent_id | id | name
—-+——–+———–+—-+——–
1 | サザエ | 2 | 2 | フネ
2 | カツオ | 2 | 2 | フネ
3 | ワカメ | 2 | 2 | フネ
4 | タラ  | 1 | 1 | サザエ
5 | イクラ | 3 | 3 | タイ子

 

タマはparent_idがないため、結合されませんでした。

 

・parent_tableにINNNER JOINするパターン

# SELECT * FROM parent_table INNER JOIN child_table ON parent_table.id =child_table. parent_id ORDER BY child_table.id;
id | name | id | name | parent_id
—-+———-+—-+———–+———–
2 | フネ  | 1 | サザエ | 2
2 | フネ  | 2 | カツオ | 2
2 | フネ  | 3 | ワカメ | 2
1 | サザエ | 4 | タラ  | 1
3 | タイ子 | 5 | イクラ | 3

 

ここでも、タマはparent_idがないため、結合されませんでした。

よって、左右テーブルどちらにINNER JOINしてもデータの内容としては変わらない。

また、フネ(parent_id=2)に関しては、3行あります。

 

 

▶︎INNER JOINは、

左右それぞれのテーブル.カラムの値が一致するレコードだけを取得する。

よって、一致しないレコードは削除される。

 

 

 

 

次はOUTER JOIN。

まずは、parent_tableにOUTER JOIN

parent_tableが右に来るのでRIGHT JOINしてます。

# SELECT * FROM child_table RIGHT JOIN parent_table ON child_table.parent_id = parent_table.id ORDER BY child_table.id;
id | name | parent_id | id | name
—-+——–+———–+—-+——–
1 | サザエ | 2 | 2 | フネ
2 | カツオ | 2 | 2 | フネ
3 | ワカメ | 2 | 2 | フネ
4 | タラ  | 1 | 1 | サザエ
5 | イクラ | 3 | 3 | タイ子

右のparent_tableを基本として結合するため、タマのように結合条件のparetn_table.idを持たないchild_table.parent_idは結合されませんでした。

 

 

次に、child_tableにOUTER JOIN

child_tableが左に来るのでLEFT JOINしてます。

# SELECT * FROM child_table LEFT JOIN parent_table ON child_table.parent_id = parent_table.id ORDER BY child_table.id;
id | name | parent_id | id | name
—-+——–+———–+—-+——–
1 | サザエ | 2 | 2 | フネ
2 | カツオ | 2 | 2 | フネ
3 | ワカメ | 2 | 2 | フネ
4 | タラ  | 1 | 1 | サザエ
5 | イクラ | 3 | 3 | タイ子
6 | タマ  | | |

左のchild_tableを基本として結合するため、タマのように結合条件のchild_table.parent_idがparent_table.idに存在しなくてもNULLとして結合してくれる。

 

▶︎OUTER JOINは、

基本となったテーブル(左右どちらか)のレコードは全て取得し、左右それぞれのテーブル.カラムの値が一致しないレコードもNULLとして結合する。

 

 

※FULL JOINについて

 

ここで、データを追加する。

# INSERT INTO parent_table VALUES (4, ‘花澤さん’);

# SELECT * FROM parent_table ORDER BY id;
id | name
—-+———-
1 | サザエ
2 | フネ
3 | タイ子
4 | 花澤さん

 

# SELECT * FROM child_table ORDER BY id;
id | name | parent_id
—-+——–+———–
1 | サザエ | 2
2 | カツオ | 2
3 | ワカメ | 2
4 | タラ  | 1
5 | イクラ | 3
6 | タマ  |

 

おわかりの通り、花澤さんにひもづくchild_table.parent_idはない。

また、先ほどに続いてタマにひもづくparent_table.idもない。

 

>parent_table(右)に結合!

# SELECT * FROM child_table RIGHT JOIN parent_table ON child_table.parent_id = parent_table.id ORDER BY child_table.id;
id | name | parent_id | id | name
—-+——–+———–+—-+———-
1 | サザエ | 2 | 2 | フネ
2 | カツオ | 2 | 2 | フネ
3 | ワカメ | 2 | 2 | フネ
4 | タラ  | 1 | 1 | サザエ
5 | イクラ | 3 | 3 | タイ子
|    |  | 4 | 花澤さん

child_tableのタマのレコードはないが、

parent_tableは全て取得されている。

 

 

>child_table(左)に結合!

# SELECT * FROM child_table LEFT JOIN parent_table ON child_table.parent_id = parent_table.id ORDER BY child_table.id;
id | name | parent_id | id | name
—-+——–+———–+—-+——–
1 | サザエ | 2 | 2 | フネ
2 | カツオ | 2 | 2 | フネ
3 | ワカメ | 2 | 2 | フネ
4 | タラ  | 1 | 1 | サザエ
5 | イクラ | 3 | 3 | タイ子
6 | タマ  |  |   |

parent_tableの花澤さんのレコードはないが、

child_tableは全て取得されている。

 

 

>>本題のFULL JOIN

# SELECT * FROM child_table FULL JOIN parent_table ON child_table.parent_id = parent_table.id ORDER BY child_table.id;
id | name | parent_id | id | name
—-+——–+———–+—-+———-
1 | サザエ | 2 | 2 | フネ
2 | カツオ | 2 | 2 | フネ
3 | ワカメ | 2 | 2 | フネ
4 | タラ  | 1 | 1 | サザエ
5 | イクラ | 3 | 3 | タイ子
6 | タマ  |  |  |
|    |  | 4 | 花澤さん

# SELECT * FROM parent_table FULL JOIN child_table ON child_table.parent_id = parent_table.id ORDER BY child_table.id;
id | name | id | name | parent_id
—-+———-+—-+——–+———–
2 | フネ   | 1 | サザエ | 2
2 | フネ   | 2 | カツオ | 2
2 | フネ   | 3 | ワカメ | 2
1 | サザエ  | 4 | タラ  | 1
3 | タイ子  | 5 | イクラ | 3
|     | 6 | タマ  |
4 | 花澤さん |  |    |

左右どちらにJOINするにしても、左右全てのレコードを取得する。

 

 

UPDATE文の失敗。

 

UPDATE文にて結合テーブルに対してWHERE句で条件しぼってるのに全行アップデートされるという悪夢におそわれました。

原因不明です。解決策は見つけました!

 

<実行環境>

・PostgreSQL 8.4.20

・PhpPgAdmin 5.0.4

 

<データ>
fruits_table

id name description
1
日向夏 宮崎のくだもの
2
バナナ 私の嫌いなくだもの
3
ぶどう 私の好きなくだもの

 

price_table

id price
1
300
2
100
3
200

 

くっつけると、、、

id name description price
1
日向夏 宮崎のくだもの
300
2
バナナ 私の嫌いなくだもの
100
3
ぶどう 私の好きなくだもの
200

 

 

<失敗SQL>

UPDATE 
 price_table
SET
 price = 150
FROM
 price_table T1
 JOIN fruits_table T2
 ON T1.id = T2.id
WHERE
 T2.name = 'バナナ'
id name description id price
1
日向夏 宮崎のくだもの
1
150
2
バナナ 私の嫌いなくだもの
2
150
3
ぶどう 私の好きなくだもの
3
150

 

(゚⊿゚)!!!

「バナナ」だけpriceを150にしたいのに

すべてのpriceが150になっちゃいました><

 

 

落ち着いて見る

SELECT 
 *
FROM
 fruits_table T1
 INNER JOIN price_table T2
 ON T1.id = T2.id
WHERE 
 T1.name = 'バナナ'

これ(上のSQL)実行したらちゃんとバナナだけの行を選択できるのにな・・・。

id name description id price
2
バナナ 私の嫌いなくだもの
2
100

 

 

 

うーん、ASをつかわず以下を実行すると

UPDATE 
 price_table
SET
 price = 100
FROM
 price_table
 JOIN fruits_table
 ON fruits_table.id = price_table.id
WHERE
 fruits_table.name = 'バナナ'

SQL エラー:

ERROR:  table name "price_table" specified more than once

と言われる。

 

 

(教えて、Googleせんせーい!)

検索ワード「update テーブル結合 postgres」 ポチっ

発見したーーー!!!→リンク

JOINではなくWHEREで結合させるんだってさ。

UPDATE 
 price_table T1
SET
 price = 150
FROM
 fruits_table T2
WHERE
 T2.id = T2.id
 AND T2.name = 'バナナ'
id name description id price
1
日向夏 宮崎のくだもの
1
300
2
バナナ 私の嫌いなくだもの
2
150
3
ぶどう 私の好きなくだもの
3
200

 

これで思い通りのアップデートが無事できました^^

一意性制約違反について




今、実は「基本情報技術者試験」に向け、勉強してます。

んで、ちょうどデータベース分野について調べてました。

 

 

前回と内容がかぶるんですが、

今回は一意性制約(UNIQUEキー制約)について学んだことを書きますー!

 

 

一意性制約(UNIQUEキー制約)とは、、、

  • NULL以外の各行を一意に制約する。
  • ただし、NOT NULLではないためNULLを許します。
    →NULLが複数行存在することもあります。

 

私「NULL以外を一意にするだけで、NULL許すんだー、結構ゆる〜い。」

そこで気になる記事を見つけました。

 

複合一意キーにおいてすべてのキー列に対して NULL を持つ行も同様に複数持つことが可能。

ただし、1つ以上のキー列に対して NULL を持ち、その他のキー列に対して同じ組合せの値を持つ 2つの行は制約違反となる。(※)

一意キーは 1 つの表に 複数定義してもよい。

つまり

  • 単一列における一意キー制約において NULL が存在して NULL を INSERT してもは制約に違反しない。
  • 複合列における一意キー制約において
    (NULL,NULL) ⇔ (NULL,NULL) は制約に違反しないが
    (NULL,1000) ⇔ (NULL,1000) は一意キー制約違反となる。
    (1000,NULL,NULL) ⇔ (1000,NULL,NULL) も同様に違反となる。 (※)

https://www.shift-the-oracle.com/constraint/

 

 

ん?

よくわかんないのでやってみる。(バカです、はい)

※大好きなphpPgAdminでやってます。

 

CREATE TABLE id_number_unique_key_table(
  id integer UNIQUE,
  number integer UNIQUE
);

INSERT INTO id_number_unique_key_table
VALUES(NULL, NULL);

INSERT INTO id_number_unique_key_table
VALUES(NULL, NULL);

 

うんうん、いける!

ユニークキーが複数のテーブルの定義

ユニークキーが複数のテーブルに(NULL,NULL)をINSERT

 

カラムid,number両方において、同じNULLです。

一見、ユニークじゃないのにいいの?ってなるけど、

一意性制約はNULL以外に関しての制約なのでいいのです。

 

前回の復習をすると、ユニークだけどNULL(プライマリキーではない)だから、

レコードの識別ができないため、削除/編集ができない。

 

 

 

しかし、以下の文は一意性制約違反です。

INSERT INTO id_number_unique_key_table
VALUES(NULL, 1000);

INSERT INTO id_number_unique_key_table
VALUES(NULL, 1000);

 

( ー`дー´)/ダメッ てされる。

一意性制約違反

カラムnumberにおいて、1000,1000でかぶってますもんね。

一意ではないです。

NULLでもないです。

だから怒られちゃうんです。

 

 

 

もちろんこれはできます!

INSERT INTO id_number_unique_key_table
VALUES(NULL, 1000);

INSERT INTO id_number_unique_key_table
VALUES(NULL, 2000);

カラムid.number両方とも、NULLかユニークです。

ユニークキー(一意性制約)はNULLかユニークであれば違反ではない!

どちらかが一意であれあ違反にはならない



主キーがあればphpPgAdminで編集できる

 

PhpPgAdminって便利ですよね。

でも、私はたまに以下のことを感じていました。

各レコードの編集/削除が出来るときと出来ないときがある。

この違いなんだ?

 

 

そこでタイミングよく、敏腕上司が教えてくれました。

 

上司「主キーがあればレコード識別ができるから、GUIで編集/削除できる」

 

私「すんごい!本当だー!しらなかったー!さすが●●さん!」

ちょっと考えると、当たり前のことだなと理解しましたwww

 

上司「ブログのネタにできるね」

 

以上、こんな会話がありました。早速ネタにします。

ありがとう●●さん。

 

 

そこで自分でいろいろ試してみました。

参考:https://www.postgresql.jp/document/9.4/html/ddl-constraints.html

 

その前に、、、

データベースは整合性を保つための整合性制約があります。

<整合性制約>

・一意性制約(ユニーク制約)

・参照制約(外部キー制約)

・非NULL制約(NOT NULL制約)

の3つがあります。

 

 

私「主キーはユニークだから一意性制約か!じゃあ、uniquであればいいのか!」

いつも通り、先走る私。

CREATE TABLE id_unique_table(
 id integer UNIQUE,
 name text
);

INSERT INTO id_unique_table
VALUES(1, test1);
INSERT INTO id_unique_table
VALUES(2, test2);
INSERT INTO id_unique_table
VALUES(3, test3);
INSERT INTO id_unique_table
VALUES(NULL, 'null');

 

一意性制約のマーク(「1」)が付いています

ユニークなテーブルの定義

ユニークでNOTNULLなテーブルのデータ

 

ん?

id=NULLのところは編集/削除できんやーん><

 

 

ってことは….NOT NULLも必要なのか!

さっきのCREATE TABLE文に非NULL制約を追加

CREATE TABLE id_not_null_unique_table(
 id integer UNIQUE NOT NULL,
 name text
);

INSERT INTO id_not_null_unique_table
VALUES(1, 'test1');
INSERT INTO id_not_null_unique_table
VALUES(2, 'test2');
INSERT INTO id_not_null_unique_table
VALUES(3, 'test3');

※INSERT INTO id_not_null_unique_table
VALUES(null, ‘null’); は非NULL制約に反するためINSERTできませんでした。

 

想定通り、非NULL制約が追加されました。

ユニークでNOTNULLなテーブルの定義

ユニークでNOTNULLなテーブルのデータ

 

 

私「なるほど。id=NULLのレコードが複数あったらレコードを識別できない。

ってことは、主キーは一意性制約非NULL制約が必要なんだ!」

 

 

つまり、

CREATE TABLE テーブル(
 id integer UNIQUE NOT NULL,
 name text
);

CREATE TABLE テーブル(
 id integer PRIMARY KEY,
 name text
);

は同じ!

 

試しに、後者を実行。

PRIMARY KEY宣言したら、勝手にNOT NULLが入ってた!

idが主キーなテーブルの定義

 

 

 

ユニークでNOT NULLじゃないと、

そりゃー誰でも識別できんですわ。

 

Cookieとセッションの関係性

こんばんは!

 

暑い日はやっぱりそーめんですよね、ぜったい「揖保乃糸」ですよね!

と、言っときながら貧乏なので中間層レベルのそーめんで我慢してます。

 

・Cookie

・セッション

よく聞く語句ですが、わかんない!

この二人のいいところはページを移動しても情報を保持できるところ。

 

 

参考:こちら

 

 Cookie  -ブラウザ側に情報を持たせる

・サーバ → ブラウザ にHTTPレスポンスのヘッダで情報(Name=Value)を送る

・Cookieはサーバにアクセスするたびに自動で送信される。

・意図しないサーバにCookie情報を送らないように、Cookieを受け取ったサーバではないサーバにはCookieを送らない

 

 

セッション -サーバ側に情報を持たせる

・セッションはWebブラウザを閉じるまで保存する

・セッションに保存された内容は、Webサーバーにファイルとして保存される。

・セッションIDとWebサーバー上のファイルが照合されてセッション内容を取り出す。

 

<まとめ>

ブラウザ側:CookieでセッションIDを持たせ、サーバ側に送信

サーバ側 :ファイルのセッションIDと照合→セッション内容を取り出し、

誰がログインしてきたのかを判断する

 

サーバー移行、常時SSL対応、AMP対応したよ!

 

こんにちは、久しぶりです、暑いですね。

フェスに行って、モッシュしまくったらマーライオンのように嘔吐してしまいました、もう若くないですね。

 

なんと、、、

無料レンタルサーバ卒業しました!

 

やったことリスト

1.サーバー(ロリポップ)の申し込み

 

2. ネームサーバ設定変更 (←ムームードメインでポチるだけ)

 

3.DB移行

└ 移行したテーブル ※記事関係だけ

wp_postmeta(記事のメタデータ、主にカスタムフィードらしい),

wp_posts(記事),

wp_terms(記事のカテゴリ),

wp_terms_relationship(記事とカテゴリの紐付け)

 

postgresでいう「シーケンス」がmysqlだと「AUTO_INCREMENT」

ということを知りました。

実際に使うときは、idが「AUTO_INCREMENT=1」としたら

INSERT INTO fruit (name, price) VALUES ("apple", 120),("banana", 200);

を実行すると、勝手にidが設定される。

こんな感じ。

| id |   name    | price |

|  1  |    apple    |   120  |

|  2  |   banana  |   200  |

 

 

4.画像アップロード

wp_contents/uploads/直下のもの

 

5.常時SSL対応

└ 管理画面 設定>一般

WordPress アドレス (URL)、サイトアドレス (URL)をhttps://~に変更

 

└ .htaccess追記

RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://kin29.info/$1 [R=301,L]
RewriteCond %{HTTP_HOST} ^www\.kin29\.info
RewriteCond %{SERVER_PORT} 443
RewriteRule ^(.*)$ https://kin29.info/$1 [R=301,L]

wwwなしに統一し、httpでアクセスしてきてもhttpsになるよう変更

 

 

6.AMP対応

プラグイン再導入

AMPテスト「有効な AMP ページです」だった^^

推奨する対応で、構造化データの項目が不足してい他ので調整は必要ですが、、。

 

7.Googleアナリティクス設定

外観>テーマの編集>ヘッダー にscriptタグ追記

 

 

意外に簡単にできてて、ミスってたり、作業忘れてないか不安です。

何かあれば教えてください。

複数レコードをカンマ区切りの1レコードにまとめる方法

※PostgresSQLでの方法です。Mysqlとかは知りません。

こんな2つのテーブルがあったとして、、

 

<category_table>

category_id category_name
1 html
2 SQL
3 Git
4 PHP

 

<story_table>

story_id category_id
1 1
1 2
1 3
2 2
2 4
3 1
4 4

 

「story_idごとにcategory_nameをカンマ区切りで表示したい。」
そんな時あると思います!

 

 

そんな時は・・・

ARRAY_TO_STRING(カラム, ‘,’)

=配列を「,」区切り文字列にする

・・・ 「カラム, カラム, カラム」こんな感じになる。ウオー

 

 

 

で、やりたいこと

「story_idごとにcategory_nameをカンマ区切りで表示したい。」

を実現する方法

 

 

SELECT
  story_id,
  ARRAY_TO_STRING(ARRAY_AGG(category_name), ‘,’)
FROM   category_table
LEFT JOIN   story_table
USING(category_id)
GROUP BY
  story_id;

 

ARRAY_AGG(カラム)

….複数行→配列に変換

今回はARRAY()ではダメだった

 

GROUP BY story_idで

story_idごとのcateory_nameを1行にするのがポイント!