20200421のMySQLに関する記事は6件です。

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はシステムデザインに最良のツールの一つ。
それにしても新しいサービスの設計考えるの楽しいよね

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【MySQL】サブクエリ × HAVINGで重複データ削除

ついでにWHEREHAVINGの違いもまとめておきます。

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
    )

おまけ

WHEREHAVINGはともに指定した条件でデータを絞ることができます。
二つの違いはデータを絞るタイミングでキーとなるのはGROUP BYです。
実行される順番は下記の通り。

WHERE → GROUP BY → HAVING

使い所の例としてはWHEREは「グループ化される前に、特定のデータに絞る」場合、HAVINGはグループ化された後に反映されるため「人数の少ないグループだけ取得する」といったケースで使えます。

studentsテーブル(重複データ削除後)でclass_idが1の生徒を抽出したい場合をみてみます。

SQL

SELECT
    class_id, name
FROM
    students
WHERE
    class_id = 1
SELECT
    class_id, name
FROM
    students
HAVING
    class_id = 1

結果

class_id name
1 鈴木一郎
1 木村五郎

上記の例だとGROUP BYを使っていないのでWHEREHAVINGどちらを使っても同じ結果になります。

次に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_idGROUP BYする前に絞ろうとした結果、student_sumが呼び出せずエラーになっています。
一方でHAVINGの場合はGROUP BY後に絞られるため正しい結果が得られます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

よく使うけど忘れちゃうコマンド MYSQL

  • 日々追記していきます

  • ユーザ追加(接続許可IPも設定)
create user '[ID]'@'[IP]' identified by '[PW]'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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にて記事を随時更新していきます。

機能

・ハンドルネームと本文を入力して送信すると内容がその下の部分にどんどん追加されていきます。
・それと同時に送信した時刻が記録されていきます。
・ハンドルネームを入力せずに本文を送信すると、ハンドルネームが自動的に「名無し」になります。
・本文を入力せずに送信すると、「本文を入力してください」という警告が出ます。

データベース

 まずデータベースを用意します。bbsという名前のデータベースを作成し、その中にtestというテーブルを作成しました。いい名前を思い付かなかったのでとりあえずtestと名付けました。このtestテーブルに「id」「name」「comment」「date」の4つのカラムを作ります。

bbs.txt
CREATE 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文で取り出しています。

改善点などアドバイスありましたら、是非コメント欄の方へ投稿していただけると幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 wrapper

2.Rootのパスワード変更

$ sudo mysql_secure_installation

パスワードの強度をチェックするプラグインのセットアップをするかどうからしいです。
あると良さげなのでYES

Press 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=utf8

MySQL再起動

$ sudo /etc/init.d/mysql restart

以下でステータス確認

mysql> status;

下記がutf8になっていることを確認。

Server characterset:    utf8

4.ユーザー作成

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/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む