SQL

Azure SQLをエクスポートしたbacpacファイルを、ローカルのMSSQLサーバにインポートする手順

こんばんは、ウチイダです。

最近、Azureクラウドの案件に携わっています。

クライアントから報告されたバグの事象を再現するために、データベースをローカルに取り込むのに苦労したので、手順をまとめておきます。

Visual Studioのインストールが必要です。

Azure SQLデータベースをエクスポートする

まずはデータベースをエクスポートします。

直接ダウンロードはできず、いったんAzure ストレージに保存する仕様です。

ストレージがない場合は作成する必要があります。

エクスポートが完了したら、指定したストレージからbacpacファイルをダウンロードします。

インポート時にこのファイルのパスを利用するので、フルパスで控えておいてください。

ローカルSQLサーバの設定を変更する

Azure のSQLをエクスポートすると、bacpacという種類のファイルが出力されます。

SQLクエリのダンプファイルができるわけではなく、そのままではローカルに取り込みできません。

まず、bacpacファイルをインポートできるように設定を変更します。

Visual Studio などMSSQL サーバに接続できるソフトウェアから、以下のクエリを実行します。

sp_configure 'contained database authentication', 1;
GO
RECONFIGURE;
GO

SqlPackage.exeでインポート処理を行う

bacpac ファイルをインポートできる機能を持ったプログラム SqlPaclage.exe が、Visual Studioに同梱されています。

Visual Studioをインストールしたくない方は、以下のページからSqlPackageだけインストールできます。

https://docs.microsoft.com/ja-jp/sql/tools/sqlpackage/sqlpackage-download?view=sql-server-ver15

Visual Studioをデフォルトの設定でインストールした場合、以下のフォルダにSqlPackage.exeが入っています。

Program Files Microsoft Visual Studio 2022 Community Common7 IDE Extensions Microsoft SQLDB DAC

PowerShellやcmdでこのフォルダを開いて、以下のコマンドを実行します。

> .\SqlPackage.exe /a:Import /sf:"*** bacpacファイルのパス ***" /tcs:"Server=(localdb)\\MSSQLLocalDB;Database=データベース名; Integrated Security=true;"
サーバー '(LocalDB)\MSSQLLocalDB' のデータベース '(入力したデータベース名)' にインポートしています。
配置計画を作成しています
配置の初期化中
配置計画を検証しています
# ... 中略
データベースのインポートが完了しました。
経過時間 0:00:16.29

これで完了です。

覚えてしまえば大した手間ではないですが、ひと手間かかりますね。

以上です。あなたのお役に立てれば幸いです。

別サーバのMariaDBに接続すると、ERROR 2002が発生する

MariaDBを動かしているDockerコンテナに別のコンテナから接続をしたら、以下のエラーが発生しました。

root@a90917286208:/# mysql -h mariadb                        
ERROR 2002 (HY000): Can't connect to MySQL server on 'mariadb' (115)

MariaDBの設定項目にはbind-addressというものがあり、指定した接続元のみアクセスを許可することができるようです。

初期設定では、127.0.0.1 からの接続のみが許可されています。

ウチイダのコンテナでは、/etc/mysql/mariadb.conf.d/50-server.cnf に設定がありました。

#
# These groups are read by MariaDB server.
# Use it for options that only the server (but not clients) should see
#
# See the examples of server my.cnf files in /usr/share/mysql

# this is read by the standalone daemon and embedded servers
[server]

# this is only for the mysqld standalone daemon
[mysqld]

#
# * Basic Settings
#
user                    = mysql
pid-file                = /run/mysqld/mysqld.pid
socket                  = /run/mysqld/mysqld.sock
#port                   = 3306
basedir                 = /usr
datadir                 = /var/lib/mysql
tmpdir                  = /tmp
lc-messages-dir         = /usr/share/mysql
#skip-external-locking

# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
#  bind-address            = 127.0.0.1 # <- 初期設定されているので、コメントアウト

~ (以下略) ~

設定を変更して、Mariadb を再起動します。

再度、接続しなおしてみます。

root@a90917286208:/# mysql -h mariadb
ERROR 1698 (28000): Access denied for user 'root'@'app'

エラー内容が ERROR 1698 に変わりました。

MariaDBのcliは、指定しないとrootユーザーで接続を試みます。

初期設定ではrootはlocalhostからのみ接続を許可するので、権限がないためエラーになっています。

MariaDBのuser テーブルに、外部ホストからの接続を許可するユーザーを作成します。

CREATE USER 'app-user'@'app' IDENTIFIED BY 'app-user-password';
GRANT ALL PRIVILEGES ON appdb.* TO 'app-user'@'app' WITH GRANT OPTION;

新たに作成し他ユーザーを指定して接続してみます。

root@a90917286208:/# mysql -h mariadb -u appuser -p 
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 9
Server version: 10.3.29-MariaDB-0+deb10u1 Debian 10

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

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

MariaDB [(none)]> 

MariaDBに、外部から接続することができました。

Dockerを利用する場合、MySQL のイメージを使えば最初から良い感じに設定してくれているようなので、通常はあまりつまづくことのない問題かと思いますが…

以上です。あなたのお役に立てればうれしいです。

プリペアドステートメントでLIKE検索

つい忘れて何度も悩むんですよね。プリペアドステートメントでLIKEするときの注意点。

PHPでプリペアードステートメントを作るとき、検索ワードをそのままクエリに入れるとうまくいきません。

$word = "a"
$stmt->dbh.prepare("SELECT * FROM fruits WHERE name LIKE '%?%'"); /* これはダメ */
$stmt.execute([$word]);

?に展開されるときに、値をシングルクオートで囲むためのようです。

あらかじめワイルドカードとくっつけておいてから、クエリに渡します。

$word = "%a%" /* 検索文字列に、ワイルドカードを含める */
$stmt->dbh.prepare("SELECT * FROM fruits WHERE name LIKE ?");
$stmt.execute([$word]);

以上です。あなたのお役に立てればうれしいです。