20200119のMySQLに関する記事は8件です。

LaradockでLaravelの環境構築するもMysqlが立ち上がらない!

開発環境

  • macOS Mojave 10.14.4
  • Laravel6.(php7.3以上)
  • Mysql8.

環境構築:参考記事
初心者でもLaradockでLaravelの環境構築をエラーなしで行おう!(Mac ver)


初心者なりに環境構築で詰まったところを共有します。
記事を参考に進めていく途中

docker-compose up -d nginx mysql phpmyadmin

mysqlが立ち上がらない
Docker再起動しても立ち上がらない!
困った!

エラー内容

Recreating laradock_mysql_1            ... error
ERROR: for mysql  Cannot start service mysql:
driver failed programming external connectivity on endpoint 
laradock_mysql_1(aa02fd7a36f486c3c62720a98798b7625cd855e49b5c4ef25b26a425e1299e0c): 
Error starting userland proxy: listen tcp 0.0.0.0:3306: bind: address already in use

3306が既に使われているってことか?

ググる...

Laradock公式にこんな記述が

スクリーンショット 2020-01-19 23.37.45.png

docker-compose.ymlの中のports欄を消せばよいとのお告げ

docker-compose.yml
### MySQL ################################################
    mysql:
      build:
        context: ./mysql
        args:
          - MYSQL_VERSION=${MYSQL_VERSION}
      environment:
        - MYSQL_DATABASE=${MYSQL_DATABASE}
        - MYSQL_USER=${MYSQL_USER}
        - MYSQL_PASSWORD=${MYSQL_PASSWORD}
        - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
        - TZ=${WORKSPACE_TIMEZONE}
      volumes:
        - ${DATA_PATH_HOST}/mysql:/var/lib/mysql
        - ${MYSQL_ENTRYPOINT_INITDB}:/docker-entrypoint-initdb.d
      ports:
        - "${MYSQL_PORT}:3306"
      networks:
        - backend

ports: - "${MYSQL_PORT}:3306"
これを削除してみる...

再度docker-compose up -d nginx mysql phpmyadmin

スクリーンショット 2020-01-19 23.43.06.png

できた!ありがとうLaradock公式!!

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

悲劇!LaradockでLaravelの環境構築するもMysqlが立ち上がらない!

開発環境

  • macOS Mojave 10.14.4
  • Laravel6.(php7.3以上)
  • Mysql8.

環境構築:参考記事
初心者でもLaradockでLaravelの環境構築をエラーなしで行おう!(Mac ver)


初心者なりに環境構築で詰まったところを共有します。
記事を参考に進めていく途中

docker-compose up -d nginx mysql phpmyadmin

mysqlが立ち上がらない
Docker再起動しても立ち上がらない!
困った!

エラー内容

Recreating laradock_mysql_1            ... error
ERROR: for mysql  Cannot start service mysql:
driver failed programming external connectivity on endpoint 
laradock_mysql_1(aa02fd7a36f486c3c62720a98798b7625cd855e49b5c4ef25b26a425e1299e0c): 
Error starting userland proxy: listen tcp 0.0.0.0:3306: bind: address already in use

3306が既に使われているってことか?

ググる...

Laradock公式にこんな記述が

スクリーンショット 2020-01-19 23.37.45.png

docker-compose.ymlの中のports欄を消せばよいとのお告げ

docker-compose.yml
### MySQL ################################################
    mysql:
      build:
        context: ./mysql
        args:
          - MYSQL_VERSION=${MYSQL_VERSION}
      environment:
        - MYSQL_DATABASE=${MYSQL_DATABASE}
        - MYSQL_USER=${MYSQL_USER}
        - MYSQL_PASSWORD=${MYSQL_PASSWORD}
        - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
        - TZ=${WORKSPACE_TIMEZONE}
      volumes:
        - ${DATA_PATH_HOST}/mysql:/var/lib/mysql
        - ${MYSQL_ENTRYPOINT_INITDB}:/docker-entrypoint-initdb.d
      ports:
        - "${MYSQL_PORT}:3306"
      networks:
        - backend

ports: - "${MYSQL_PORT}:3306"
これを削除してみる...

再度docker-compose up -d nginx mysql phpmyadmin

スクリーンショット 2020-01-19 23.43.06.png

できた!ありがとうLaradock公式!!

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

MySQL Shell 8.0.19 で InnoDB ReplicaSet をちょっとだけ試してみる

先日 MySQL 8.0.19 がリリースされ、あわせて MySQL Shell 8.0.19 もリリースされました。

MySQL Shell には「AdminAPI」という管理 API が実装されていますが、8.0.19 でこの AdminAPI が機能強化されて、InnoDB ReplicaSet の構築ができるようになりました。

今回、この InnoDB ReplicaSet を構築する作業を試してみたいと思います。

※おそらく近日中にもっと詳しい(マニアックな?)エントリがこちらに掲載されることでしょう。

MySQL InnoDB ReplicaSet とは?

簡単にいうと「GTID と非同期レプリケーションを利用した、Master(Writer) - Slave(Reader) 型のクラスタ」です。Master(Writer) は 1 台のみですが、Slave(Reader) は複数台の構成にすることができます。

これまで、MySQL(InnoDB)には InnoDB Cluster が存在しましたが、

  • グループレプリケーションではなく非同期レプリケーションを使う
  • InnoDB ReplicaSet のフェイルオーバは手動のみ可能(スクリプト等で自動化することは可能)
  • マルチ Master(Writer) の構成は不可

などの点が InnoDB Cluster と異なります。

要は、グループレプリケーションではない、MySQL としては「従来型」のレプリケーションを使うクラスタが簡単に構築できるようになった、ということです。

簡単に試してみる

細かい調査はプロの方々にお任せして、ここでは簡単に試してみます。

  • 構成は Master(Writer) 1 台=サーバ名rs-1、Slave(Reader) 1 台=サーバ名rs-2
  • InnoDB ReplicaSet を構築して、Master(Writer) で行った変更が Slave(Reader) に伝搬することを確認する
  • 今回は MySQL Router の設定は行わない

準備

以下のような環境を用意します。

  • OS:Linux × 2 台(サーバ名rs-1rs-2
  • rs-1rs-2双方に mysql-community-server の 8.0.19 をインストールし、mysql_secure_installationで初期設定しておく
  • rs-1に MySQL Shell 8.0.19 をインストールしておく
  • rs-1rs-2双方にroot@rs-1ユーザを作成し、WITH GRANT OPTION付きで全権限を許可しておく
  • 双方の IP アドレスを /etc/hosts ファイルで名前解決できるようにしておく

Master(Writer) インスタンスを設定する

まず MySQL Shell を起動します。

# mysqlsh -u root -p
Please provide the password for 'root@localhost': ************
※パスワードを入力

Save password for 'root@localhost'? [Y]es/[N]o/Ne[v]er (default No):
※[Enter]を入力

お好みでパスワードを保存(Y)/一切保存しない(v)を選択しても良いでしょう。

MySQL Shell 8.0.19

Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
Other names may be trademarks of their respective owners.

Type '\help' or '\?' for help; '\quit' to exit.
Creating a session to 'root@localhost'
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 16 (X protocol)
Server version: 8.0.19 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.

あらためて Master(writer) インスタンスにネットワーク接続します。

 MySQL  localhost:33060+ ssl  JS > \connect root@rs-1:3306
※rs-1(Master)に接続

Creating a session to 'root@rs-1:3306'
Please provide the password for 'root@rs-1:3306': ************
※パスワードを入力

Save password for 'root@rs-1:3306'? [Y]es/[N]o/Ne[v]er (default No):
※[Enter]を入力

Fetching schema names for autocompletion... Press ^C to stop.
Closing old connection...
Your MySQL connection id is 18
Server version: 8.0.19 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.

Master(Writer) インスタンスを設定します。

 MySQL  rs-1:3306 ssl  JS > dba.configureReplicaSetInstance('root@rs-1:3306', {clusterAdmin: "'rsadmin'@'rs-1%'"});
※Masterインスタンスを設定

Please provide the password for 'root@rs-1:3306': ************
※パスワードを入力

Save password for 'root@rs-1:3306'? [Y]es/[N]o/Ne[v]er (default No):
※[Enter]を入力

Configuring local MySQL instance listening at port 3306 for use in an InnoDB ReplicaSet...

This instance reports its own address as rs-1:3306
Clients and other cluster members will communicate with it through this address by default. If this is not correct, the report_host MySQL system variable should be changed.
Password for new account: ************
※ReplicaSet管理アカウント(新規追加)用パスワードを入力

Confirm password: ************
※同上

NOTE: Some configuration options need to be fixed:
+--------------------------+---------------+----------------+--------------------------------------------------+
| Variable                 | Current Value | Required Value | Note                                             |
+--------------------------+---------------+----------------+--------------------------------------------------+
| enforce_gtid_consistency | OFF           | ON             | Update read-only variable and restart the server |
| gtid_mode                | OFF           | ON             | Update read-only variable and restart the server |
| server_id                | 1             | <unique ID>    | Update read-only variable and restart the server |
+--------------------------+---------------+----------------+--------------------------------------------------+

設定は動的適用されませんので、再起動が必要です。

Some variables need to be changed, but cannot be done dynamically on the server.
Do you want to perform the required configuration changes? [y/n]: y
※「y」を入力

Do you want to restart the instance after configuring it? [y/n]: y
※「y」を入力

Cluster admin user 'rsadmin'@'rs-1%' created.
Configuring instance...
The instance 'rs-1:3306' was configured to be used in an InnoDB ReplicaSet.
Restarting MySQL...
NOTE: MySQL server at rs-1:3306 was restarted.

ReplicaSet を作成する

Master(Writer) インスタンス設定後は、ReplicaSet を作成します。

ここからは先ほどの MySQL Shell 操作の続きとなります(以降同じ)。

念のため Master(Writer) インスタンスに再接続します(おそらく不要だと思いますが)。

 MySQL  rs-1:3306 ssl  JS > \connect root@rs-1:3306
※rs-1(Master)に再接続

Creating a session to 'root@rs-1:3306'
Please provide the password for 'root@rs-1:3306': ************
※パスワードを入力

Save password for 'root@rs-1:3306'? [Y]es/[N]o/Ne[v]er (default No):
※[Enter]を入力

Fetching schema names for autocompletion... Press ^C to stop.
Closing old connection...
Your MySQL connection id is 9
Server version: 8.0.19 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.

exampleという名前の ReplicaSet を作成します。

 MySQL  rs-1:3306 ssl  JS > var rs = dba.createReplicaSet("example")
※ReplicaSetを作成

A new replicaset with instance 'rs-1:3306' will be created.

* Checking MySQL instance at rs-1:3306

This instance reports its own address as rs-1:3306
rs-1:3306: Instance configuration is suitable.

* Updating metadata...

ReplicaSet object successfully created for rs-1:3306.
Use rs.addInstance() to add more asynchronously replicated instances to this replicaset and rs.status() to check its status.

作成できたようなので、画面の指示通り ReplicaSet のステータスを確認してみます。

 MySQL  rs-1:3306 ssl  JS > rs.status()
※ReplicaSetのステータスを確認

{
    "replicaSet": {
        "name": "example",
        "primary": "rs-1:3306",
        "status": "AVAILABLE",
        "statusText": "All instances available.",
        "topology": {
            "rs-1:3306": {
                "address": "rs-1:3306",
                "instanceRole": "PRIMARY",
                "mode": "R/W",
                "status": "ONLINE"
            }
        },
        "type": "ASYNC"
    }
}

Master(Writer) インスタンスrs-1の情報が表示されました。

Slave(Reader) インスタンスを設定する

引き続き、Slave(Reader) インスタンスを設定します。

今度は Slave(Reader) インスタンスにネットワーク接続します。

 MySQL  rs-1:3306 ssl  JS > \connect root@rs-2:3306
※rs-2(Slave)に接続

Creating a session to 'root@rs-2:3306'
Please provide the password for 'root@rs-2:3306': ************
※パスワードを入力

Save password for 'root@rs-2:3306'? [Y]es/[N]o/Ne[v]er (default No):
※[Enter]を入力

Fetching schema names for autocompletion... Press ^C to stop.
Closing old connection...
Your MySQL connection id is 15
Server version: 8.0.19 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.

Slave(Reader) インスタンスを設定します。

 MySQL  rs-2:3306 ssl  JS > dba.configureReplicaSetInstance('root@rs-2:3306', {clusterAdmin: "'rsadmin'@'rs-2%'"});
※Slaveインスタンスを設定

Please provide the password for 'root@rs-2:3306': ************
※パスワードを入力

Save password for 'root@rs-2:3306'? [Y]es/[N]o/Ne[v]er (default No):
※[Enter]を入力

Configuring MySQL instance at rs-2:3306 for use in an InnoDB ReplicaSet...

This instance reports its own address as rs-2:3306
Clients and other cluster members will communicate with it through this address by default. If this is not correct, the report_host MySQL system variable should be changed.
Password for new account: ************
※ReplicaSet管理アカウント用パスワードを入力

Confirm password: ************
※同上

NOTE: Some configuration options need to be fixed:
+--------------------------+---------------+----------------+--------------------------------------------------+
| Variable                 | Current Value | Required Value | Note                                             |
+--------------------------+---------------+----------------+--------------------------------------------------+
| enforce_gtid_consistency | OFF           | ON             | Update read-only variable and restart the server |
| gtid_mode                | OFF           | ON             | Update read-only variable and restart the server |
| server_id                | 1             | <unique ID>    | Update read-only variable and restart the server |
+--------------------------+---------------+----------------+--------------------------------------------------+

Master(Writer) と同様に再起動が必要です。

Some variables need to be changed, but cannot be done dynamically on the server.
Do you want to perform the required configuration changes? [y/n]: y
※「y」を入力

Do you want to restart the instance after configuring it? [y/n]: y
※「y」を入力

Cluster admin user 'rsadmin'@'rs-2%' created.
Configuring instance...
The instance 'rs-2:3306' was configured to be used in an InnoDB ReplicaSet.
Restarting MySQL...
NOTE: MySQL server at rs-2:3306 was restarted.

Slave(Reader) インスタンスを ReplicaSet に追加する

Slave(Reader) インスタンスを ReplicaSet に追加します。

今度は Master(Writer) インスタンスにネットワーク接続します。

 MySQL  rs-2:3306 ssl  JS > \connect root@rs-1:3306
※rs-1(Master)に接続

Creating a session to 'root@rs-1:3306'
Please provide the password for 'root@rs-1:3306': ************
※パスワードを入力

Save password for 'root@rs-1:3306'? [Y]es/[N]o/Ne[v]er (default No):
※[Enter]を入力

Fetching schema names for autocompletion... Press ^C to stop.
Closing old connection...
Your MySQL connection id is 14
Server version: 8.0.19 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.

Slave(Reader) インスタンスrs-2を追加します。

※2020/01/19 現在、公式リファレンスマニュアルの実行例が間違っている可能性があります(addInstance()ではパラメータとしてサーバ(インスタンス)名:ポート番号を指定)。

 MySQL  rs-1:3306 ssl  JS > rs.addInstance('rs-2:3306')
※ReplicaSetにrs-2(Slave)を追加

Adding instance to the replicaset...

* Performing validation checks

This instance reports its own address as rs-2:3306
rs-2:3306: Instance configuration is suitable.

* Checking async replication topology...

* Checking transaction state of the instance...

NOTE: The target instance 'rs-2:3306' has not been pre-provisioned (GTID set is empty). The Shell is unable to decide whether replication can completely recover its state.
The safest and most convenient way to provision a new instance is through automatic clone provisioning, which will completely overwrite the state of 'rs-2:3306' with a physical snapshot from an existing replicaset member. To use this method by default, set the 'recoveryMethod' option to 'clone'.

WARNING: It should be safe to rely on replication to incrementally recover the state of the new instance if you are sure all updates ever executed in the replicaset were done with GTIDs enabled, there are no purged transactions and the new instance contains the same GTID set as the replicaset or a subset of it. To use this method by default, set the 'recoveryMethod' option to 'incremental'.

Master(Writer) からのクローンを行います。

Please select a recovery method [C]lone/[I]ncremental recovery/[A]bort (default Clone): C
※「C」を入力

* Updating topology
Waiting for clone process of the new member to complete. Press ^C to abort the operation.
* Waiting for clone to finish...
NOTE: rs-2:3306 is being cloned from rs-1:3306
** Stage DROP DATA: Completed
** Clone Transfer
    FILE COPY  ####################################################  100%  Completed
    PAGE COPY  ####################################################  100%  Completed
    REDO COPY  ####################################################  100%  Completed
NOTE: rs-2:3306 is shutting down...

* Waiting for server restart... ready
* rs-2:3306 has restarted, waiting for clone to finish...
** Stage RESTART: Completed
* Clone process has finished: 59.63 MB transferred in about 1 second (~1.00 B/s)

** Configuring rs-2:3306 to replicate from rs-1:3306
** Waiting for new instance to synchronize with PRIMARY...

The instance 'rs-2:3306' was added to the replicaset and is replicating from rs-1:3306.

完了したようなので ReplicaSet のステータスを確認します。

 MySQL  rs-1:3306 ssl  JS > rs.status()
※ReplicaSetのステータスを確認

{
    "replicaSet": {
        "name": "example",
        "primary": "rs-1:3306",
        "status": "AVAILABLE",
        "statusText": "All instances available.",
        "topology": {
            "rs-1:3306": {
                "address": "rs-1:3306",
                "instanceRole": "PRIMARY",
                "mode": "R/W",
                "status": "ONLINE"
            },
            "rs-2:3306": {
                "address": "rs-2:3306",
                "instanceRole": "SECONDARY",
                "mode": "R/O",
                "replication": {
                    "applierStatus": "APPLIED_ALL",
                    "applierThreadState": "Slave has read all relay log; waiting for more updates",
                    "receiverStatus": "ON",
                    "receiverThreadState": "Waiting for master to send event",
                    "replicationLag": null
                },
                "status": "ONLINE"
            }
        },
        "type": "ASYNC"
    }
}

今度は Slave(Reader) インスタンスrs-2の情報も表示されました。

MySQL Shell を終了します。

 MySQL  rs-1:3306 ssl  JS > \q
※MySQL Shellからログアウトする

Bye!

Master(Writer) インスタンスにデータを入れてみる

ここからは、Master(Writer) → Slave(Reader) 間のレプリケーションの確認です。

まずは Master(Writer) でデータベース・テーブルを作成し、テストデータを挿入します。

# mysql -u root -p
Enter password:
※パスワードを入力

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 25
Server version: 8.0.19 MySQL Community Server - GPL

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> CREATE DATABASE example;
※データベースを作成

Query OK, 1 row affected (0.00 sec)

mysql> USE example;
※データベースを選択

Database changed
mysql> CREATE TABLE sample1 (id INT PRIMARY KEY AUTO_INCREMENT, str VARCHAR(100));
※テーブルを作成

Query OK, 0 rows affected (0.02 sec)

mysql> INSERT INTO sample1 SET str = 'line 1';
※データを挿入

Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO sample1 SET str = 'line 2';
※同上

Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO sample1 SET str = 'line 3';
※同上

Query OK, 1 row affected (0.00 sec)

データベースexampleにテーブルsample1を作成し、3 行のデータを挿入しました。

Slave(Reader) インスタンスで確認してみる

Slave(Reader) 側で挿入データの到達を確認します。

# mysql -u root -p
Enter password:
※パスワードを入力

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 13
Server version: 8.0.19 MySQL Community Server - GPL

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> SHOW DATABASES;
※データベースを確認

+-------------------------------+
| Database                      |
+-------------------------------+
| example                       |
| information_schema            |
| mysql                         |
| mysql_innodb_cluster_metadata |
| performance_schema            |
| sys                           |
+-------------------------------+
6 rows in set (0.00 sec)

mysql> USE example;
※データベースを選択

Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> SHOW TABLES;
※テーブルを確認

+-------------------+
| Tables_in_example |
+-------------------+
| sample1           |
+-------------------+
1 row in set (0.01 sec)

mysql> SELECT * FROM sample1;
※データを確認

+----+--------+
| id | str    |
+----+--------+
|  1 | line 1 |
|  2 | line 2 |
|  3 | line 3 |
+----+--------+
3 rows in set (0.00 sec)

確認できました。

おわりに

以前は大掛かりな作業が必要だった非同期レプリケーションクラスタ構築ですが、比較的簡単な操作で ReplicaSet として構築できることがわかりました。

また、今回は省略しましたが、AdminAPI では MySQL Router の初期設定も簡素化することが可能です。


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

count(*) するときに気をつけたい NULL

create table contents (id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY)
comment='コンテンツ';
insert into contents values (), (), (), (), (), ();
create table view_logs (id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, content_id BIGINT NOT NULL)
comment='コンテンツに対する閲覧ログ';
insert into view_logs (content_id) values (1), (2), (3);
select * from contents;
select * from view_logs;

今回は簡便さを優先して、外部キー制約は付けていません。

コンテンツ 閲覧ログ
image.png image.png
6レコード 3レコード。コンテンツIDが4,5,6は閲覧ログがありません。

各コンテンツの閲覧回数は何回でしょうか?

次のようになるはずです。

コンテンツID 閲覧回数
1 1
2 1
3 1
4 0
5 0
6 0

次にSQLを書いてみます。

失敗例

内部結合を使ってカウント

INNER JOINで閲覧ログとコンテンツを結合してしまってはいけません。閲覧ログの中にすべてのコンテンツIDが現れないためです。

閲覧ログにあるコンテンツIDしか閲覧数をカウントすることができません。

SELECT
    ct.id AS content_id,
    count(*) AS view_count
FROM
    contents AS ct
    JOIN view_logs AS vl ON ct.id = vl.content_id
GROUP BY
    content_id

image.png

外部結合を使ってカウント

閲覧ログとコンテンツIDを内部結合すると、閲覧ログに存在しないコンテンツIDがカウントできませんでした。

それなら、コンテンツIDを基準に外部結合をすれば、すべてのコンテンツIDが網羅できます。
しかし、安直に count(*) を使うと罠にハマります。

SELECT
    ct.id as content_id
    ,count(*) as view_count
FROM
    contents AS ct
    LEFT OUTER JOIN view_logs AS vl ON ct.id = vl.content_id
group by
    ct.id

閲覧ログがないはずのコンテンツID(4,5,6)が閲覧回数が1になっています。

image.png

これは、 count(*) によって行単位でカウントされてしまっていることが原因です。

COUNT(*) は行数をカウントするため、ペットの数をカウントするクエリーは次のようになります。

出典:https://dev.mysql.com/doc/refman/5.6/ja/counting-rows.html

COUNT 関数には NULL を件数に含めるために特別な アスタリスク(*)を使用する構文が用意されており NULL を含めて行数を取得することができる。

出典:https://www.shift-the-oracle.com/sql/aggregate-functions/count.html

count する前のレコードを観察する

countする前のレコードを見ると、レコードの数は6で、content_idが4,5,6にはlog_idがNULLになっています。

SELECT
    ct.id AS content_id,
    vl.id AS log_id
FROM
    contents AS ct
    LEFT OUTER JOIN view_logs AS vl ON ct.id = vl.content_id

image.png

count(*) はレコードが存在すればカウントするため、すべてのコンテンツIDについて 1 とカウントします。

成功例

カウントしたいカラムを指定して外部結合を使う

view_logs の content_id カラムか id カラムを count() に与えれば、求めていた結果が得られます。

count() には式が入るようになっていて、 NULL を与えた場合 0 とカウント するため、 * ではなく NULL になりうるカラム名を与えれば、正しくカウントできます。

SELECT
    ct.id as content_id,
    count(vl.content_id) as view_count
FROM
    contents AS ct
    LEFT OUTER JOIN view_logs AS vl ON ct.id = vl.content_id
group by
    ct.id

image.png

実験後の後始末

不要になったテーブルはサクッと消して終了です。

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

Rails Tutorialの知識から【ポートフォリオ】を作って勉強する話 #17.5 MySQL導入編

こんな人におすすめ

  • プログラミング初心者でポートフォリオの作り方が分からない
  • Rails Tutorialをやってみたが理解することが難しい

前回:#17 VPC環境構築編
次回:準備中

今回の流れ

  1. SQLite3からMySQLに変更する

この記事は、動画を観た時間を記録するアプリのポートフォリオです。
今回はRailsアプリのDBをSQLite3からAWSによるRDSのMySQLに変更します。
RDSはすでにAWSで作成しているものとします(詳しくは#17をご覧ください)。

SQLite3からRDSのMySQLに変更する

RailsアプリのDBをSQLite3からRDSのMySQLに変更します。

  • Gemfileを編集する
  • エンドポイントを確認する
  • database.ymlを編集する
  • RDS(MySQL)に接続する
  • DBを更新する

Gemfileを編集する

Gemfileから使っているDBを削除し、MySQLを入れます。

Gemfile
+ gem 'mysql2'

group :development, :test do
- gem 'sqlite3'
end

group :production do
- gem 'pg'
end
shell
$ bundle install

エンドポイントを確認する

RDSのエンドポイントはDBを設定する際に必要なので、確認します。

AWSにログイン → AWSマネジメントコンソール『RDS』
画面左のダッシュボード『データベース』 → 『自分のアプリ名』 → 下部タブ『接続とセキュリティ』のエンドポイントをコピー

database.ymlを編集する

DBの設定を編集します。

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  reconnect: false
  database: lantern(自分のアプリ名)
  pool: 5
  username: ①任意の名前
  password: ②任意のパスワード
  socket: /var/lib/mysql/mysql.sock
  host: コピーしたエンドポイント

development:
  <<: *default
  database: lantern_devlopment(自分のアプリ名)

test:
  <<: *default
  database: lantern_test(自分のアプリ名)

production:
  <<: *default
  database: lantern_production(自分のアプリ名)

RDS(MySQL)に接続する

RDS(MySQL)に接続します。

shell
$ mysql -h コピーしたエンドポイント -P 3306 -u ①任意の名前 -p
Enter password:②任意のパスワード

繋がらない場合は、一度MySQLを再起動します。

shell
$ sudo /etc/init.d/mysqld restart

その後はユーザーを作成し、ルート権限を与えます。

mysql
mysql> create user '①任意の名前'@'コピーしたエンドポイント' identified by '②任意のパスワード';
mysql> grant all on `%`.* to '①任意の名前'@'コピーしたエンドポイント' identified by '②任意のパスワード';

参考になりました↓
MySqlのソケットエラーを解決する
RailsのDBを(初めから| |後から)MySQLに変更する
RDSのMySQLでGRANT文が通らない

DBを更新する

後はいつも通り、DBを更新します。

shell
$ rails db:reset
$ rails db:migrate
$ rails db:seed

以上でMySQLの移行が完了しました。

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

Laravel: 複数のDBに接続

mysqlとSQLite3の同時使用例

php.iniの場所

$ php -r "echo phpinfo();" | grep "php.ini"
php.ini
//使用するDBをコメントアウト
extension=pdo_mysql
extension=sqlite3

httpdの再起動
データベースの作成

設定

.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE_1={データベース名}
DB_USERNAME=root
DB_PASSWORD=null

DB_CONNECTION=sqlite
DB_DATABASE_2={パス/データベースファイル名}
config/database.php
'default' => env('DB_CONNECTION_1', 'mysql'),
//略
'sqlite' => [
    'driver' => 'sqlite',
    'database' => env('DB_DATABASE_2', database_path({フォルダパス})),
    'prefix' => '',
],
'mysql' => [
    'driver' => 'mysql',
    'host' => env('DB_HOST', 'localhost'),
    'database' => env('DB_DATABASE_1', 'forge'),
    'username' => env('DB_USERNAME_1', 'forge'),
    'password' => env('DB_PASSWORD_1', ''),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
    'strict' => false,
    'engine' => null,
],
migration
//デフォルトDBにはconnectonメソッドは必要なし
Schema::connection('sqlite')->create({テーブル名}, function (Blueprint $table) {
}

データベースを指定してマイグレーション

$ php artisan migration --database=sqlite

Modelでの接続設定

Model
class Datas extends Model
{
    protected $connection = 'sqlite';
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSでDB(MySQL)を作成した後、A5:SQL Mk-2で接続して管理する方法

AWSでは、AWSを初めて使うという方向けに様々なチュートリアルを公式サイトで展開しており、そのチュートリアルに沿って進めていけば基本的なAWSサービスの操作や使用方法について理解することができます。
https://aws.amazon.com/jp/getting-started/tutorials/

DB作成もチュートリアルが展開されており、簡単にDB作成・クライアントソフトからの接続ができるのですが、如何せんチュートリアルで紹介されているクライアントソフト「MySQL Workbench」が使いずらいのなんの。
(「MySQL Workbench」を使ったことがないからかもしれませんが。)

しかも私のデスクトップPCではインストールしたのにも関わらず、なぜかプロセスが立ち上がらない事象に陥ってしまったのでこれはもうアカンと。
(おそらく「Visual C++ Redistributable for Visual Studio 2013」ランタイムがインストールされていないからと思われる。)

なので、よくエンジニアの間で使用されているDB操作フリーソフト「A5:SQL Mk-2」でAWSに作成したDB(MySQL)にアクセスをする方法をご紹介したいと思います。

参考サイト

今回参考にしたサイトはこちらです。
https://a1-style.net/amazon-web-service/rds-mysql-a5-sql-mk2/

(AWS)DB(MySQL)を作成

まずは、AWSでDB(MySQL)を作成していきます。
下記AWS公式サイトのDB作成チュートリアルのステップ1まで進めましょう。
ステップ2からは進めなくて大丈夫です。
https://aws.amazon.com/jp/getting-started/tutorials/create-mysql-db/

(AWS)セキュリティグループを変更して管理PCからのアクセスを許可する

下記のデータベース管理画面に移動します。
移動後DB識別子のリンクをクリックします。
AWS1.png

DBの概要や詳細情報が表示される画面になると思うので、画面中央のセキュリティグループのルールの欄を確認します。
恐らくセキュリティグループのルールとしてデフォルトで設定されている設定が2つ表示されていると思うので、上のセキュリティグループのリンクをクリックします。
AWS2.png

セキュリティグループの設定画面に移動したら、画面下のインバウンドタブをクリックし編集ボタンをクリックします。
(下記画像はすでに設定をしている項目が表示されているため気にしなくて良いです。)

AWS3.png

編集ボタンをクリックすると下記画面のポップアップが表示されるので、下記画像の項目通り「タイプ」「プロトコル」「ポート範囲」入力をしましょう。
ポート範囲はデフォルトでは「3306」です。変更をしている場合はこちらも変更する必要があります。

また、ソースの欄はDB接続をしたいPCのIPアドレスを入力するのですが、プルダウンでマイIPを選択すると自動的にIPアドレスが入力されるため特に調べる必要はありません。

すべて入力したら右下の保存ボタンをクリックして完了です。
これでAWSに作成したDBに外部(管理PC)からアクセスすることができるようになりました。
AWS4.png

A5:SQL Mk-2で作成したDB(MySQL)と接続をする

A5:SQL Mk-2のダウンロードについて

A5:SQL Mk-2は下記URLからダウンロードすることができます。
https://a5m2.mmatsubara.com/

DB接続方法

ダウンロードしたA5:SQL Mk-2を開き、上部メニューからデータベース>データベースの追加・削除を選択します。
AWS5.png

表示したウィンドウの下部にある追加をクリックします。
AWS6.png

下記赤枠の部分「MySQL/MariaDB(直接接続)」を選択します。
AWS7.png

下記画面が表示されるので、データベースの情報を入力していきます。
入力後、OKをクリックします。
ホスト名:エンドポイント名(※)
ユーザーID:masterUsername
(AWSのチュートリアルを進めているとmasterUsernameで作られているかと思います。別のユーザーIDで作成している場合はそのユーザーIDを入力する)
パスワード:DB作成時のパスワード
AWS8.png

※ホスト名に入力するエンドポイント名は下記画像の左側に表示されているエンドポイント名を入力します。
AWS10.png

接続が問題なく完了すると、A5:SQL Mk-2の左側の枠に接続したDBの情報が表示されます。
ちなみに今回は、データベース名を「aws_test」で作成しているため、aws_testというデータベースが作成されていることがわかります。(データベースのみのため、テーブルなどの情報はもちろん追加されていません。)
AWS9.png

これで、A5:SQL Mk-2でAWSのDB(MySQL)に接続することができました。

最後に

AWSでデータベースを作成するとこんなにも簡単なんだと思うと同時に、かなりさらにシステム開発としてできる幅が広がった気がします。
今回は、セキュリティグループのインバウンド設定で固定PCのIPアドレスを設定しましたが、このような設定は開発環境などだけにとどめておき、本番環境でのDB設定では行わないようにしましょう。当たり前ですが。

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

(初心者)MySQLのUDFを簡単に作ってみた

参考

作成した関数

#include <mysql.h>
#include <string>

extern "C" {
    my_bool factorial_init(UDF_INIT *initid, UDF_ARGS *args, char *message);

    char *factorial(UDF_INIT *initid, UDF_ARGS *args, char *result,
        unsigned long *length, char *is_null, char *error);
}

my_bool factorial_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
    if (args->arg_count != 1)
    {
        strcpy(message,"FACTORIAL() requires one arguments");
        return 1;
    }
    if (args->arg_type[0] != INT_RESULT)
    {
        strcpy(message,"FACTORIAL() require an integer");
        return 1;
    }
    return 0;
}

char *factorial(UDF_INIT *initid, UDF_ARGS *args, char *result,
    unsigned long *length, char *is_null, char *error) {

    long long val = *(long long*) args->args[0];
    int sum = 1;
    for (int i = 1; i <= val; ++i)
    {
        sum *= i;
    }

    strcpy(result, std::to_string(sum).c_str());
    *length = strlen(result);

    return result;
}

factorial_initfactorialを作成しています。
factorial_initが関数実行時に最初に実行されて、引数のチェックなどを行い、
factorialが実際に処理をする部分です。

コンパイル

$ mysql_config --cflags #mysql.hがあるパスを確認
-I/usr/local/opt/mysql@5.7/include/mysql 
$ mysql_config --plugindir #プラグインのパスを確認
/usr/local/opt/mysql@5.7/lib/plugin

$ g++ factorial.cpp -shared \
 -o /usr/local/opt/mysql@5.7/lib/plugin/factorial.so \
 -I /usr/local/opt/mysql@5.7/include/mysql/ #コンパイル
mysql> create function factorial returns string soname 'factorial.so';
Query OK, 0 rows affected (0.01 sec)

mysql> select factorial(6);
+--------------+
| factorial(6) |
+--------------+
| 720          |
+--------------+
1 row in set (0.00 sec)

UDFを動かせました。見ていただいてありがとうございました。m(_ _)m

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