- 投稿日:2020-04-21T22:21:33+09:00
macOS CatalinaでMySQL Workbenchが起動しなくなった時にすること
macOS Catalinaにアップデートしてから、MySQL Workbenchが起動しなくなってしまって困ってたのが、やっと解決。
スタンドアロンで動くし、データベースを使う使わないに関わらず、設計・分析時のE-R図作成ツールとしても重宝してるので、使えないとかなり不便。
というわけで、コマンドラインで直接実行してみて以下のようなエラーが出て起動しない場合の処方箋。
$ /Applications/MySQLWorkbench.app/Contents/MacOS/MySQLWorkbench your pythonpath points to a site-packages dir for python 3.x but you are running python 2.x! pythonpath is currently: "/applications/mysqlworkbench.app/contents/resources/libraries" you should `unset pythonpath` to fix this.launchdからのアプリ起動時にPython2のライブラリを呼び出すように環境変数を設定するのが解なので、~/Library/LaunchAgents/jp.co.cyberz.forMySQLWorkbench.plist に以下のファイルを作って、ログアウト→ログインで解決。
もちろん無保証。~/Library/LaunchAgents/jp.co.cyberz.forMySQLWorkbench.plist<!-- ===== No guarantee!! ===== NAKABAYASHI Toshifumi http://www.cyberz.co.jp/index.php/staff/ ===== No guarantee!! ===== (1)place this file ~/Library/LaunchAgents/jp.co.cyberz.forMySQLWorkbench.plist (2) logout and login --> <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>jp.co.cyberz.forMySQLWorkbench</string> <key>ProgramArguments</key> <array> <string>/bin/launchctl</string> <string>setenv</string> <string>PYTHONPATH</string> <string>/usr/local/lib/python2.7/site-packages/</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist>MySQL Workbenchはシステムデザインに最良のツールの一つ。
それにしても新しいサービスの設計考えるの楽しいよね
- 投稿日:2020-04-21T18:18:08+09:00
【MySQL】サブクエリ × HAVINGで重複データ削除
ついでに
WHERE
とHAVING
の違いもまとめておきます。studentsテーブル
id name class_id 1 鈴木一郎 1 2 田中二郎 3 3 高橋三郎 2 4 佐藤四郎 3 5 木村五郎 1 6 森六郎 3 7 鈴木一郎 1 8 田中二郎 3 9 高橋三郎 2 やりたいこと
何かの間違いで重複して登録されてしまったデータ(id:7~9)を削除したい。
id name class_id 1 鈴木一郎 1 2 田中二郎 3 3 高橋三郎 2 4 佐藤四郎 3 5 木村五郎 1 6 森六郎 3 やったこと
サブクエリで重複しているデータのうち新しく登録された方の
id
を取得。
(GROUP BY
でグループ化してから、総数が1ではない(重複している)グループのid
を取得)
WHERE ~ IN ~
を使ってstudents
テーブルから取得してきたid
のデータを削除。DELETE FROM students WHERE id IN ( SELECT max(students.id) FROM students GROUP BY students.name, students.class_id HAVING count(*) > 1 );結果
ERROR: You can't specify target table 'users' for update in FROM clauseエラーになりました。
「このエラーは、テーブルを変更し、さらにサブクエリーで同じテーブルから選択しようとする次のような場合に発生します。」
サブクエリ内でstudents
テーブルを使っているのが問題みたいです。解決方法はこんな感じ。
IN句
のなかのSELECT
を更にサブクエリにして、AS
で何かテーブル名(今回だったら適当hoge)を付けることでエラーが回避できます。DELETE FROM users WHERE id IN ( SELECT * FROM ( SELECT max(users.id) FROM users GROUP BY users.name, users.class_id HAVING count(*) > 1 ) as hoge )おまけ
WHERE
とHAVING
はともに指定した条件でデータを絞ることができます。
二つの違いはデータを絞るタイミングでキーとなるのはGROUP BY
です。
実行される順番は下記の通り。WHERE → GROUP BY → HAVING使い所の例としては
WHERE
は「グループ化される前に、特定のデータに絞る」場合、HAVING
はグループ化された後に反映されるため「人数の少ないグループだけ取得する」といったケースで使えます。
students
テーブル(重複データ削除後)でclass_id
が1の生徒を抽出したい場合をみてみます。SQL
SELECT class_id, name FROM students WHERE class_id = 1SELECT class_id, name FROM students HAVING class_id = 1結果
class_id name 1 鈴木一郎 1 木村五郎 上記の例だと
GROUP BY
を使っていないのでWHERE
、HAVING
どちらを使っても同じ結果になります。次に
students
テーブル(重複データ削除後)で生徒が2人より多いクラスを抽出したい場合をみてみます。SQL(WHERE)
SELECT class_id, COUNT(class_id) as student_sum FROM users WHERE student_sum > 2 GROUP BY class_id;結果
ERROR: Unknown column 'student_sum' in 'where clause'SQL(HAVING)
SELECT class_id, COUNT(class_id) as student_sum FROM users GROUP BY class_id HAVING student_sum > 2結果
class_id student_sum 3 3
WHERE
の場合、class_id
でGROUP BY
する前に絞ろうとした結果、student_sum
が呼び出せずエラーになっています。
一方でHAVING
の場合はGROUP BY
後に絞られるため正しい結果が得られます。
- 投稿日:2020-04-21T14:23:20+09:00
よく使うけど忘れちゃうコマンド MYSQL
- 日々追記していきます
- ユーザ追加(接続許可IPも設定)
create user '[ID]'@'[IP]' identified by '[PW]'
- 投稿日:2020-04-21T11:26:46+09:00
SQL: ALTER TABLEの使い方メモ
SQLのテーブル定義を変更するalter文の使い方メモ(MySQLで検証)
# テーブル名の変更 alter table target_table rename to new_table; # カラム名の変更 alter table target_table rename target_column to new_column; # カラム定義の変更 alter table target_table modify target_column [新しいカラム定義]; ## (例)alter table target_table modify target_column varchar(20) default NULL; # 新しいカラムの追加 alter table target_table add new_column [カラム定義] ## (例)alter table target_table add new_column varchar(20) default NULL;
- 投稿日:2020-04-21T01:05:24+09:00
PHP+MySQLで作る2ちゃんねる風掲示板機能を解説
記事概要
タイトルの通り、PHPとMySQLを利用して、かつて2ちゃんねると呼ばれていた掲示板のようなものの作り方を解説したものです。はじめにプログラムのソースコード全体を公開します。その後に要所毎に解説を加えていきます。
Recruit
唐突ですが、私は現在、地方の大学に通う、農学部の4年生です。プログラマーとして現在就職活動を行なっております。居住地域の関係上、IT企業でのアルバイト等の経験はなく、1年以上前から独学でプログラミングの学習をしつつ、個人開発を続けていました。
勤務地は問いません。雇用形態はアルバイトやインターンからでも構いません。
ご連絡はTwitterのDM→@Ren_s_off または、こちらのメールアドレスまで(oaihgop4@yahoo.co.jp)お願いいたします。お気軽にお問い合わせください。
Portfolio & Skills
本記事を含め、Qiitaに作成した作品についての記事を投稿していこうと考えています。今は記事が執筆中の段階ですので、随時こちらにURLを更新していきます。
・HTML5,CSS,JavaScript
これらを利用したWebサイトの制作経験があります。レスポンシブに対応しています。JavaScriptはモバイル版のハンバーガーメニューの挙動制御やフッターをメインコンテンツの高さに関わらず常にブラウザの最下部に表示させるために利用しています。
・岩手県のグルメ情報サイトhttps://iwategourmet.com/iwategourmet/・PHP,MySQL
本記事のようにデータベースを利用した掲示板の作成の他、会員制サイトの会員登録およびログイン機能の実装ができます。そちらについてはQiitaにて記事を随時更新していきます。機能
・ハンドルネームと本文を入力して送信すると内容がその下の部分にどんどん追加されていきます。
・それと同時に送信した時刻が記録されていきます。
・ハンドルネームを入力せずに本文を送信すると、ハンドルネームが自動的に「名無し」になります。
・本文を入力せずに送信すると、「本文を入力してください」という警告が出ます。PHPと言えばまあやるよねって感じで、今日は掲示板を作りました。#PHP #MySQL pic.twitter.com/dQDOZ1SE7Y
— K.Takashiro (@Ren_s_off) March 8, 2020データベース
まずデータベースを用意します。bbsという名前のデータベースを作成し、その中にtestというテーブルを作成しました。いい名前を思い付かなかったのでとりあえずtestと名付けました。このtestテーブルに「id」「name」「comment」「date」の4つのカラムを作ります。
bbs.txtCREATE DATABASE bbs DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; CREATE USER 'XXXX'@'YYYY' IDENTIFIED BY 'ZZZZ'; GRANT ALL ON bbs.* TO 'XXXX'@'YYYY'; USE bbs; CREATE TABLE `test` ( `id` INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY , `name` VARCHAR(20) NOT NULL , `comment` VARCHAR(300) NOT NULL , `date` VARCHAR(100) NOT NULL );「txt」として外部ファイルにSQLコマンドを保存して、SOURCEコマンドを利用すれば、外部ファイルで用意したSQLコマンドを一度に読み込めます。
SOURCE (bbs.txtが保存してあるディレクトリまでのパス)/bbs.txt解説
CREATE DATABASE bbs DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;・「CREATE DATABASE XXXX」でXXXXという名前のデータベースを作成します。
・「DEFAULT CHARACTER SET XXXX」で文字コードをXXXXに指定します。マルチバイト文字をプログラムで利用する場合は、文字コードの指定が重要になります。
・「COLLATE (文字コード) _ (言語名) _ (比較法)」 COLLATE以下でデータベースの照合順序を指定します。CREATE USER 'XXXX'@'YYYY' IDENTIFIED BY 'ZZZZ'; GRANT ALL ON bbs.* TO 'XXXX'@'YYYY';・「CREATE USER 〜」でデータベースを操作する際のユーザーを作成します。XXXXにはユーザー名、YYYYにはホスト名が入ります。ホスト名にはIPアドレスやlocalhostも指定できます。ZZZZにパスワードを指定します。
・「GRANT 〜」 GRANTステートメントはMySQLユーザーに権限を付与したり、セキュア接続の使用やサーバーリソースへのアクセスに関する制限などの、その他のアカウント特性を指定する機能もあります。ここではbbsというデータベースに置いて全ての権限をYYYY上でXXXXに与えるという意味です。CREATE TABLE `test` ( `id` INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY , `name` VARCHAR(20) NOT NULL , `comment` VARCHAR(300) NOT NULL , `date` VARCHAR(100) NOT NULL );・「CREATE TABLE
@@@@
〜」 @@@@に作成するTABLE名を設定します。
INT の後に、UNSIGNEDを指定することで、通常、INT型は-2147483648 ~ 2147483647の数値を扱うのに対して、0 ~ 4294967295 の負の数以外の範囲の数値を扱います。負の数が入らないことがわかっているデータに対してはUNSIGNEDを指定すると良いでしょう。ソースコード
・view.php
掲示板の情報の入出力を行う画面です。view.php<?php require_once('model.php'); ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <form action="<?php print $_SERVER['PHP_SELF']; ?>" method="post"> <p>名前<input type="text" name="name"><?php echo $err_msg1; ?></p> <p>本文<textarea name="comment" rows="10" cols="70"></textarea><?php echo $err_msg2; ?></p> <input type="submit" value="送信" name="send"> <?php echo $message; ?> </form> <?php foreach ($res as $value) { echo $value; } ?> </body> </html>・model.php
view.phpで受け取ったデータをデータベースとやりとりするためのプログラムです。データベースの接続に必要な情報や処理は「DBManager.php」という外部ファイルに記述しました。DBManager.php<?php function getDB() { $dsn = "mysql:host=YYYY; dbname=bbs; charset=utf8"; $db_user = "XXXX"; $db_pass = "ZZZZ"; $db = new PDO($dsn, $db_user, $db_pass); return $db; } ?>model.php<?php require_once('DbManager.php'); function h( $str ){ return htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); } //名前,タイトル,本文の内容を取得 $name = ( isset( $_POST["name"] ) === true ) ? h( $_POST["name"] ) : "名無し"; $comment = ( isset( $_POST["comment"] ) === true ) ? h( $_POST["comment"] ) : ""; //エラーメッセージ $err_msg1 = ""; //名前が長過ぎる時に呼び出されるエラーメッセージ $err_msg2 = ""; //本文が入力されていない時に呼び出されるエラーメッセージ $message = ""; //書き込みに成功した時に呼び出されるメッセージ if ( isset($_POST["send"] ) === true ) { if ( $name === "") $name = "名無し"; if ( $comment === "" ) $err_msg2 = "本文を入力してください"; $name = trim( $name ); $comment = trim( $comment ); if ( mb_strlen($name, "UTF-8") > 20 ) $err_msg1 = "名前は20文字以内にしてください"; if ( mb_strlen($comment, "UTF-8") > 300 ) $err_msg2 = "本文は300文字以内にしてください"; if ( $err_msg1 === "" && $err_msg2 === "" ) { try { $date = date("Y-m-d H:i:s"); $pdo = getDB(); $pdo->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_EMPTY_STRING); $stt = $pdo->prepare('INSERT INTO test(name, comment, date) VALUES(:name, :comment, :date)'); $stt->bindValue(':name', $name); $stt->bindValue(':comment', $comment); $stt->bindValue(':date', $date); $stt->execute(); } catch (PDOException $e) { $message = "<p>接続エラー: " . $e->getMessage() . "</p>"; die(); } finally { $pdo = null; } } } //コメントを格納する変数 $res = array(); try { $pdo = getDB(); $stt = $pdo->query( 'SELECT name, comment, date FROM test' ); //内容を出力 $i = 1; while ( $row = $stt->fetch(PDO::FETCH_ASSOC) ) { $res[$i] = "<p>{$i}:{$row["name"]} {$row["date"]}<br>{$row["comment"]}</p>"; $i++; } } catch (PDOException $e) { $message = "<p>接続エラー: " . $e->getMessage() . "</p>"; die(); } finally { $pdo = null; } ?>解説
function h( $str ){ return htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); }XSS対策として、htmlspecialchars()を利用しました。htmlspecialchars()はソースコードが冗長になることを防ぐために、あらかじめ処理をユーザー定義関数h()にまとめました。また、今回のプログラムでは、マルチバイト文字列の長さを判定するif文の直前で、スペースを除去するtrim()を利用していますが、h()の中でtrim()を利用して、文字列を整形するというのも考えられます。
データベースにコメントを追加する
if ( $err_msg1 === "" && $err_msg2 === "" ) { try { $date = date("Y-m-d H:i:s"); $pdo = getDB(); $pdo->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_EMPTY_STRING); $stt = $pdo->prepare('INSERT INTO test(name, comment, date) VALUES(:name, :comment, :date)'); $stt->bindValue(':name', $name); $stt->bindValue(':comment', $comment); $stt->bindValue(':date', $date); $stt->execute(); } catch (PDOException $e) { $message = "<p>接続エラー: " . $e->getMessage() . "</p>"; die(); } finally { $pdo = null; } }ここではユーザーから受け取った名前と本文のデータをデータベースに登録しています。名前や本文が入力されているかのチェックや文字数制限のチェックをクリアすると、日付のデータと共に名前と本文がデータベースに記録されます。
$pdo = getDB(); $pdo->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_EMPTY_STRING); $stt = $pdo->prepare('INSERT INTO test(name, comment, date) VALUES(:name, :comment, :date)'); $stt->bindValue(':name', $name); $stt->bindValue(':comment', $comment); $stt->bindValue(':date', $date); $stt->execute();・getDB()はPDOオブジェクトを利用してMySQLに接続しています。DBManager.phpファイル内に記述がまとめてあります。PHPでは「mysql_connect()」といったMySQLに接続するための専用の組み込み関数がありますが、PHP5.5.0で非推奨になった後、PHP7.0.0以降では削除されました。したがって、PDOオブジェクトを利用するのが一般的です。
・「$pdo->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_EMPTY_STRING);」では空文字列をNULLに変換しています。
・SQL文を実行する方法として、PDO::query,PDO::exec,PDO::prepareを利用する場合の3つがありますが、それぞれ役割があります。ユーザー入力を伴わないクエリに対しては、単純にPDO::queryを実行します。返り値はPDOStatementです。
例$stt = $pdo->query( "SELECT * FROM users" );ユーザー入力を伴わないクエリで、INSERTやUPDATE文を利用した際の件数を直接返り値として欲しい場合や、特に結果を必要としない場合では、PDO::execを利用します。
例$stt = $pdo->exec( "DELETE FROM fruit" );最後にユーザーから入力を受け取ってSQL文を実行する場合には、PDO::prepareを利用します。大まかにはprepareでクエリ文を用意し、bindValueで変数を結び付け、executeで実行するという3段階を踏みます。
「INSERT INTO test(name, comment, date) VALUES(:name, :comment, :date)」の中の「:name」や「:comment」のことをプレースホルダと呼び、ユーザー入力した値を当てはめる場所としてあらかじめ確保しておくものになります。bindValueでプレースホルダと変数を結び付けています。
プレースホルダには「名前なしプレースホルダ」と「名前付きプレースホルダ」の2種類があります。今回利用した「:name」や「:comment」は名前付きプレースホルダです。疑問符プレースホルダと名前付きプレースホルダは混在させて利用してはいけません。以下に名前なしプレースホルダで記述した場合の例を提示します。名前なしプレースホルダで記述した場合$stt = $pdo->prepare( 'INSERT INTO test(name, comment, date) VALUES( ?, ?, ? )' ); $stt->bindValue( 1, $name ); $stt->bindValue( 2, $comment ); $stt->bindValue( 3, $date ); $stt->execute();?は1番目から順番に1,2,3・・・と対応します。
データベースからコメントを読み込む
//コメントを格納する変数 $res = array(); try { $pdo = getDB(); $stt = $pdo->query( 'SELECT name, comment, date FROM test' ); //内容を出力 $i = 1; while ( $row = $stt->fetch(PDO::FETCH_ASSOC) ) { $res[$i] = "<p>{$i}:{$row["name"]} {$row["date"]}<br>{$row["comment"]}</p>"; $i++; } } catch (PDOException $e) { $message = "<p>接続エラー: " . $e->getMessage() . "</p>"; die(); } finally { $pdo = null; } ?>この部分の処理でデータベースからコメントを取得し、出力する準備を行います。
・「\$row = \$stt->fetch(PDO::FETCH_ASSOC)」 fetchメソッドで検索結果に該当するデータを1行ずつ取得します。引数にPDO::FETCH_ASSOCを指定することで、結果セットに返された際のカラム名で添字を付けた配列を返します。これで変数$rowはそれぞれid,name,comment,dateをキーに持つ配列になりました。
$res[$i] = "<p>{$i}:{$row["name"]} {$row["date"]}<br>{$row["comment"]}</p>";あらかじめ用意した配列\$resに、データベースから取得した投稿の情報を整形して、格納していきます。最後にview.phpでforeach文で取り出しています。
改善点などアドバイスありましたら、是非コメント欄の方へ投稿していただけると幸いです。
- 投稿日:2020-04-21T00:33:04+09:00
Ubuntu(18.04.3) MySQLをインストール
1.MySQLインストール
下記のコマンドでMySQLとクライアントツールがインストールできます。
$ sudo apt install mysql-server mysql-clientえ、クライアントツールって何??????
『mysql クライアントツール』でググっても出てこない。
何のクライアントツール!?!?!?!?!?!?!?!???????そんなことを考えてたらインストールが終わります。
確認。$ mysql --version mysql Ver 14.14 Distrib 5.7.29, for Linux (i686) using EditLine wrapper2.Rootのパスワード変更
$ sudo mysql_secure_installationパスワードの強度をチェックするプラグインのセットアップをするかどうからしいです。
あると良さげなのでYESPress y|Y for Yes, any other key for No:ここは新しいパスワード・確認
New password: Re-enter new password:匿名のユーザーを削除するか聞かれます。
YES。Remove anonymous users? (Press y|Y for Yes, any other key for No)リモートからrootにログインできないようにするか聞かれてます。
YES。Disallow root login remotely? (Press y|Y for Yes, any other key for No) :3.文字コードをUTF-8で統一
この状態だと、サーバーサイドアプリから日本語を含んだデータを投入すると文字化けするみたいです。
Rootにログイン
sudo mysql -u root※もし『mysql>』から抜けられなくなったら『\q
』で抜けられます(ひっそり)以下を編集
/etc/mysql/my.cnf[mysqld] character-set-server=utf8 skip-character-set-client-handshake default-storage-engine=INNODB [mysqldump] default-character-set=utf8 [mysql] default-character-set=utf8MySQL再起動
$ sudo /etc/init.d/mysql restart以下でステータス確認
mysql> status;下記がutf8になっていることを確認。
Server characterset: utf84.ユーザー作成
Rootのみでしかログインできないとセキュリティ上あまり良くないみたいです。
ユーザーを作成します。CREATE USER ユーザー名@localhost IDENTIFIED BY ‘パスワード’;※この時の注意
ユーザー名をoh!nobitasan!にしたらエラーが出ました。
!は登録できません。では、ログイン。
$ sudo mysql -u oh_nobitasan -pこれでパスワードを入力すれば入れるはずです。
最後に
下記を参考にさせて頂きました。
ありがとうございました。
https://qiita.com/kojionilk/items/bdace886c3664d75c5f7
https://qiita.com/yokomichi_ask/items/7ff2bff2e57ca9e7450a
https://utano.jp/entry/2017/10/mysql57-mysql-secure-installation/