標準入出力、リダイレクト、パイプ

実家からお歳暮でもらったであろうビールをかっさらってきて、

帰宅と同時に飲むのが至福のひとときな今日この頃。

 

 

全体はこんな感じです。

 

▶︎標準入出力とは。

lsやcatなどの実行結果は、
指定しない限り、普通は画面に表示される。←これです!
デフォルトではそれぞれ以下のようになっている。
標準入力 → キーボード
標準出力 → 画面

こんな感じ

# キーボードで入力(標準入力)したものがそのままに出力(標準出力)される
echo hello!

 

▶︎リダイレクトとは。

「リダイレクト」を使うと、標準入出力を指定のファイルに変更することができる。

出力リダイレクト

画面(標準出力)でなく、ファイルに結果を出力する

# 「hello!」という出力結果を新規ファイルoutput.txtに出力
echo hello! > output.txt

#「>」は上書き
# dateコマンドの出力結果を既存ファイルoutput.txtに上書き
date > output.txt

#「>>」は追記
# 「add!」という出力結果を既存ファイルoutput.txtに追記
echo add! >> output.txt

入力リダイレクト

キーボード(標準入力)でなく、ファイルからの入力をコマンドに渡す

# cat output.txtと同じ結果
# cat 0< output.txtと同じ結果
cat < output.txt

echo 6+1 > input.txt
# bc 0< input.txtと同じ結果
bc < input.txt

 

▶︎パイプ「|」とは。

標準出力をコマンドに渡す

# 中間ファイルが不要となる 
# echo 6+1 > input.txt && bc < input.txt と同じ結果
echo 6+1 | bc

 

#余談

過去のコミット全部自分のものにしたい時

$ git filter-branch -f --env-filter "GIT_AUTHOR_NAME='名前';\
 GIT_AUTHOR_EMAIL='メールアドレス';\
 GIT_COMMITTER_NAME='名前';\
 GIT_COMMITTER_EMAIL='メールアドレス';" \
HEAD

過去のコミットを編集することは歴史をガラリとかえて未来に影響が大きいのであまりしないほうがいいです、はい。
けど今回グローバルネームでコミットしてしまっていたため変更しました。もちろん

$ git config --local user.name 名前
$ git config --local user.email "メールアドレス"

もしましたよ。

SQLの色々

寒いですね。なんかすんごく太りました (x_x)

SQLの本で「なるほど」ってなったことを書きます。

 

■SERIAL型 …連番をふることができる。(postgreSQL)

CREATE TABLE test_db(
  id smaillint,
  name text
);
INSERT INTOtest_db VALUES(1,'ガズ');
INSERT INTO test_db VALUES(2,'シャーロット');
INSERT INTO test_db VALUES(3,'マーニー');

今時点では
id   |     name     |
——–+————–+-
1      | ガズ         |
2      | シャーロット |
3      | マーニー     |

 

そこに、SERIAL型のカラムnumberを追加する。

ALTER TABLE test_db ADD COLUMN number serial;

id   |     name     | number
——–+————–+——–+—–
1      | ガズ         |      1 |
2      | シャーロット|      2 |
3      | マーニー     |      3|

勝手に連番ふってくれる!!!

CREATE SEQUENCEをしなくても、シーケンスをつくってくれます。

 

カラムnumberを指定しなくても連番を勝手にいれてくれる!

INSERT INTO test_db VALUES(4,'アーロン');

id   |     name     | number
——–+————–+——–+—–
1      | ガズ         |      1 |
2      | シャーロット|      2 |
3      | マーニー     |      3|
4      | アーロン     |      4|

 

■複数の論理演算子の優先度

(1) NOT

(2) AND

(3) OR

 

■ JOIN の色々

▶︎INNER JOIN(内部結合)

書き方: JOIN テーブル名

片方にしか存在しない行は結合されない

 

▶︎OUTER JOIN(外部結合)

・FULL OUTER JOIN

書き方: FULL (OUTER) JOIN テーブル名

片方にしか存在しない行でも他方をNULLにして結合する

 

・LEFT OUTER JOIN

書き方:LEFT (OUTER) JOIN テーブル名

左テーブルにしか存在しない行は右テーブルをNULLにして結合する。

右テーブルにしか存在しない行は結合されない。

 

・RIGHT OUTER JOIN

書き方:RIGHT (OUTER) JOIN テーブル名

右テーブルにしか存在しない行は左テーブルをNULLにして結合する。

左テーブルにしか存在しない行は結合されない。

 

■集合関数におけるNULLの扱い

SUM/MAX/MIN/AVG … NULLは無視

→NULLを0に置き換えて集計したい時は、COALESCE関数を使うといい。

 

COUNT … カラム名指定の場合、NULLはカウントしない。

しかし、「*」の時はNULLを含めてカウントする。

→count(カラム名)とcount(*)で結果が異なることがある。

 

 

 

■SQLの区分

・DML …Data Manipulation Language – データ操作言語

SELECT / INSERT / UPDATE / DELETE など

 

・DDL …Data Definition language – データ定義言語

データベース、テーブル、ビューなどの基本データを定義・作成するための言語。

CREATE / ALTER / DROP など

インデックスやシーケンスを含む。データベールオブジェクトと言うこともある。

 

・DCL …Data Control Language – データ制御言語

データベースのユーザー権限の管理やデータのトランザクション処理を行う為の言語

GRANT / REVOKE / BEGIN / COMMIT / ROLLBACK など

 

■トランザクション

…複数の指示をひとかたまりのSQL文として扱うこと。

「一方のテーブルでは差し引き、他方のテーブルでは差し引いた分足す」

といった指示間を他の人に邪魔されると一貫性がなくなるものとかに使う。

 

トランザクション制御を扱うためのSQL

・BEGIN: 開始の指示。この指示以降のSQL文を1つのトランザクションとする。

・COMMIT: 終了の指示。この指示までを人のSQL文とし、変更するを確定する。

・ROLLBACK: 終了の指示。この指示までを人のSQL文とし、変更するを取り消す。

→ACID特性 の 原子性(1か0か)

 

■似た言葉だけど全然違う、ロールバックとロールフォワード。

・ロールバック…実行した処理を取り消す。SQLの実行失敗やデッドロークなどで発生する。

・ロールフォワード …まだ実行されていない処理を実行する。

障害復旧時、バックアップにより障害前の状態にした後、ログより、障害前から障害直前の状態までデータを更新すること。

 

■設計について

▶︎一般的な設計手順

(1)概念設計

(2)論理設計

(3)物理設計

 

▶︎正規化 ….(2)論理設計の時にする。

・第一正規形:繰り返しの列、重複列がない。

 

・第二正規形:部分従属がない

(例)部分従属とは…{A,B}→C = A→CまたはB→C

 

・第三正規形:推移従属がない

※推移従属とは…{A,B}→C = A→BかつB→CでB→Aではない。

(例)クラス→出席番号→名前

名前は、クラスと出席番号が特定されないとわからない、そんな関係性。

 

実際読んだ本がこちらです↓

アップデートしたら、wp-includeの変更きえた。

どうも、寒いですね。

 

WP上級者の方からしたら、この題名で離脱すると思います。
わたしはまだまだWPがわかっておりません。何卒、よろしくです。

 

カスタマイズしたいなら、テーマ(/wp-content/themes/)に手を加えるべきとのことです。
題名通り、WPのアップデートにより変更部分は消されてしまいます!!!

WPってアップデートの時に改定されたフォイルまるごと上書きしちゃうんですよね。

 

例えば、、、

Version 4.9.1
2017年11月29日、WordPress 4.9.1 にて
改訂されたファイル一覧

wp-admin/about.php
wp-admin/includes/class-wp-upgrader.php
wp-admin/includes/file.php
wp-admin/includes/meta-boxes.php
wp-admin/includes/misc.php
wp-admin/includes/plugin.php
wp-admin/includes/upgrade.php
wp-admin/js/theme.js
wp-admin/js/theme.min.js
wp-admin/theme-editor.php
wp-admin/user-new.php
wp-includes/class-wp-theme.php
wp-includes/feed.php
wp-includes/functions.php
wp-includes/general-template.php
wp-includes/script-loader.php
wp-includes/version.php
wp-includes/wp-db.php

参考:https://wpdocs.osdn.jp/Version_4.9.1

 

 

他のバージョンアップの差分ファイルでも大体が
wp-admin/とwp-inclide/以下ですね。
wp-contents/はほとんどないと思っていました。

しかし、wp-content/が変更されることもあるようです。

Version 4.8.3、Version 4.7.3では

wp-content/plugins

Version 4.7.1では

wp-content/themes/twentyseventeen/README.txt
wp-content/themes/twentyseventeen/style.css
wp-content/themes/twentyseventeen/functions.php
wp-content/themes/twentyseventeen/assets/js/customize-controls.js

Version 4.7.4では

wp-content/themes/twentyseventeen/footer.php
wp-content/themes/twentyseventeen/style.css
wp-content/themes/twentyseventeen/functions.php
wp-content/themes/twentyseventeen/template-parts/navigation/navigation-top.php
wp-content/themes/twentyseventeen/template-parts/post/content-excerpt.php
wp-content/themes/twentyseventeen/template-parts/post/content-gallery.php
wp-content/themes/twentyseventeen/template-parts/post/content-audio.php
wp-content/themes/twentyseventeen/template-parts/post/content-image.php
wp-content/themes/twentyseventeen/template-parts/post/content.php
wp-content/themes/twentyseventeen/template-parts/post/content-video.php
wp-content/themes/twentyseventeen/README.txt

Version 4.7.5

wp-content/plugins/akismet/_inc/img/logo-full-2x.png
wp-content/plugins/akismet/_inc/akismet.css
wp-content/plugins/akismet/_inc/akismet.js
wp-content/plugins/akismet/akismet.php
wp-content/plugins/akismet/class.akismet.php
wp-content/plugins/akismet/readme.txt

 

 

バックアップ大事ですね!

 

 

ちゃんとCodeXにも書いてありましたね。

NOTE - wp-includes と wp-admin ディレクトリおよびサブディレクトリ、そしてルートディレクトリ (たとえば index.php, wp-login.php 等)のすべての古いファイルを新しいものに置き換えて下さい。wp-config.phpについては安全ですので心配ありません。 wp-contentディレクトリをコピーする際には注意して下さい。wp-content ディレクトリ全体の置換ではなく、このディレクトリ内からファイルをコピーします。ここには現行のテーマやプラグインが含まれるため保持が必要でしょう。デフォルトや古いテーマを直接書き換えた場合も上書きしないように注意してください。変更が失われます(もっとも、新機能や修正が含まれるため、比較した方がよいですが...)。 最後にファイル wp-config-sample.php を参照して、現行の wp-config.php に追加すべき新しい機能が導入されていないかを確認してください。

 

次はちゃんとGit管理して、差分をじっくり見てみたいなと思います。

PHP Data Object

 

お久しぶりです。
最近、家の周りで工事が大量にされており騒音がひどくちょっとイライラしております。

 

ではでは、PHPをもっと知ろうのコーナーです!
PHPってmysqlなどのデータベースを操作できますよねー
その仕組みとは?ということで、

PDO = PHP Data Odjects

について知ろうと思います!!!

 

 

先ほども申した通り、PHPでデータベースを扱うためには、

PDO(= PHP Data Odjects)というオブジェクトを使います。

 

これにより

▶︎データベースに接続

▶︎データベース操作(SQLの実行)

を行うことができます。

 

 

▶︎データベース接続 …PDOクラスを使う

https://php.net によると

例1 ドライバ呼び出しにより PDO インスタンスを生成する

/* ドライバ呼び出しを使用して MySQL データベースに接続する */
$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
$user = 'dbuser';
$password = 'dbpass';

try {
  $dbh = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
  echo 'Connection failed: ' . $e->getMessage();
}

 

ドライバとは?ってなったんで私の中で落としこんだ意味が

ドライバ…データベースを扱うためするためのモジュール(プログラム)

つまりMysqlやPostgreSQL,SQLiteのことですね

 

 

//第一引数でデータベースドライバの種類:データベース名;host名の指定
$dsn = 'mysql:dbname=testdb;host=127.0.0.1';

 

//第二引数でデータベースユーザ名を指定
$user = 'dbuser';

 

//第三引数でデータベースユーザパスを指定
$password = 'dbpass';

 

 

▶︎SQLの実行 …PDOクラス or PDOStatementクラスを使う

ここで疑問を感じたのが、

\PDOStatement::executeとPDO::exec何が違うん?/

ってことです。

 

では、PDOクラスと PDOStatementクラスについて。

https://php.net によると

PDO::exec — SQL ステートメントを実行し、作用した行数を返す

PDO::exec() は SELECT 文からは結果を返しません。 プログラム中で一度だけ発行が必要になる SELECT 文に対しては、 PDO::query() の発行を検討してください。 複数回発行が必要な文については、PDO::prepare() による PDOStatement オブジェクトの準備と PDOStatement::execute() による文の発行を行ってください。

PDO::exec()は実行結果を返すんじゃなくて、行数しか返さないらしい。
そして、プレースホルダーとか使えんぽい。

やってみよう。

テーブルusers
|id|name|score|
|1|taro|0|

 

$db = new PDO(PDO_DSN, DB_USERNAME, DB_PASSWORD);
$sql ='SELECT * FROM users';
//return 0 ...SELECT 文からは結果を返しません。
print_r($db->exec($sql));

 

//プレースホルダーを使ってみる

使いようがなかった。。

//プレースホルダーではないが、、、こうすることはできる(当たり前)

$db = new PDO(PDO_DSN, DB_USERNAME, DB_PASSWORD);
$whereVal = 1;
$sql ='SELECT * FROM users WHERE id ='. $whereVal;
//return 0 ...SELECT 文からは結果を返しません。
print_r($db->exec($sql));

 

 

PDOStatement::execute — プリペアドステートメントを実行する

返り値

成功した場合に TRUE を、失敗した場合に FALSE を返します。

プリペアドステートメント….直訳で準備陳述。

これも実行するするだけ。成功したか失敗したかを返す。
一方、PDOStatement::executeは
PDOStatement::prepareでSQLステートメントをセットして、プレースホルダーとか使えるぽい。

 

$db = new PDO(PDO_DSN, DB_USERNAME, DB_PASSWORD);
$sql ='SELECT * FROM users';
$stmt = $db->prepare($sql);

//return 1(=true) ...成功!
print_r($stmt->execute());

 

//プレースホルダーを使ってみる

$db = new PDO(PDO_DSN, DB_USERNAME, DB_PASSWORD);
$sql ='SELECT * FROM users WHERE id = :id';
$stmt = $db->prepare($sql);

//さっきと同じ return 1(=true) ...成功!
print_r($stmt->execute(array(':id' => 1)));

 

 

 

結果が欲しいのに!ってときはこれ。

PDO::query

PDO::query — SQL ステートメントを実行し、結果セットを PDOStatement オブジェクトとして返す

$db = new PDO(PDO_DSN, DB_USERNAME, DB_PASSWORD);
$sql ='SELECT * FROM users';
foreach ($db->query($sql) as $row) {

  print('id: ' . $row['id'] . '<br>');
  print('name: ' . $row['name'].'<br>');
  print('score: ' . $row['score'].'<br>');

}

 

<まとめ>

・PDOをサクッと直接的にsql実行できる。

・PDOStatementはプレースホルダーとか使って計画的にsql実行できる。

・ 結果が欲しいときはPDO::query

 

 

重複排除して、他カラムも取得できる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じゃないと、

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