Docker上でSQL Serverを動かしてCSVをbulk importする

Docker上でMicrosoft SQL Serverを動かしてCSVをインポートしたので、その方法をメモしておきます。ちょっと手こずりました。

# 環境

* Windows 10 Pro
* Docker Desktop 2.1.0.4
* サクラエディタ Ver. 2.2.0.1
* A5:SQL Mk-2 Version 2.14.4 (x64 edition)

※テーブルのスキーマ情報とインポートするCSVは別のSQL ServerのDBからエクスポートしているものとします。

# やってみよう

## 手順

1. docker-compose.ymlの作成
2. CSVファイルをUTF-16 LEに変換
3. DBとテーブルを作成するSQLファイルの作成
4. データインポート用batファイルの作成と実行
5. DBにアクセス

## docker-compose.ymlの作成

以下のような`docker-compose.yml`を作成します。主に[Microsoftのドキュメント](https://docs.microsoft.com/ja-jp/sql/linux/quickstart-install-connect-docker?view=sql-server-ver15&pivots=cs1-bash)を参考にしています。
`<PASSWORD>`には好きな文字列を入れてください。ただし、[SQL Serverのパスワードポリシー](https://docs.microsoft.com/ja-jp/sql/relational-databases/security/password-policy?view=sql-server-ver15)に従う必要がありますのでご注意ください。
`docker-compose.yml`のあるディレクトリに`tmp/db`というディレクトリ（docker-compose実行時に自動で生成されます）にSQL ServerのDB内容を格納するディレクトリがマウントされるのでデータは永続化されます。

```yaml:docker-compose.yml
version: '3'

services:
  sqlserver:
    image: mcr.microsoft.com/mssql/server:2019-CTP3.2-ubuntu
    container_name: sqlserver
    ports:
      - 1433:1433
    restart: always
    environment:
      ACCEPT_EULA: Y
      SA_PASSWORD: <PASSWORD>
      MSSQL_PID: Express
      MSSQL_LCID: 1041
      MSSQL_COLLATION: Japanese_CI_AS
    volumes:
      - ./tmp/db/:/var/opt/mssql/data
```

## CSVファイルをUTF-16 LEに変換

元のDBからエクスポートしたCSVファイルをUTF-16 LE形式に変換します。サクラエディタを使う場合は「名前を付けて保存」で「Unicode」を選択し、BOM付きにして改行コードをCRLFにして保存します。
ここでは`docker-compose.yml`のあるディレクトリに`data`というディレクトリを作成、そのなかに`dbo.MYTABLE.csv`という名前で保存したとします。

## DBとテーブルを作成するSQLファイルの作成

以下のようなSQLファイルを`data`ディレクトリのなかに作成します。`MyDB`という名前のDBを作成し、`MYTABLE`という名前のテーブルを作成しています。`MYTABLE`を作成するSQLは元のDBからエクスポートしておいたものです。`BULK INSERT`でCSVファイルをインポートするように記述します。`DATAFILETYPE`を`widechar`に指定するのが重要です。また、インポートするCSVの1行目はヘッダなので`FIRSTROW`を2にしています。

```sql:data/init.sql
CREATE DATABASE MyDB
GO

USE MyDB
GO

create table [dbo].MYTABLE (
  ID int not null
  , TITLE nvarchar(20) not null
  , ENABLE nvarchar(1) not null
  , NUM int not null

  ...(snip)...

  , primary key (ID)
);

BULK INSERT MYTABLE
FROM '/tmp/dbo.MYTABLE.csv'
WITH
(
   FORMAT='csv',
   FIRSTROW=2,
   ROWTERMINATOR = '\r\n',
   DATAFILETYPE='widechar'
);
```

## データインポート用batファイルの作成と実行

まず、コマンドプロンプトで先ほど作成した`docker-compose.yml`を元にbuildし、コンテナを起動します。

```bash
docker-compose build
docker-compose up -d
```

以下のようなbatファイルを`docker-compose.yml`と同階層に作成します。ここでは`setup.bat`というファイル名にしました。
やってることは、起動したコンテナ内に`init.sql`を転送し、SQL Serverコンテナ内でそのSQLを実行です。`<PASSWORD>`は`docker-compose.yml`に記述したものと同じにしてください。

```bat:setup.bat
docker cp  .\data\init.sql sqlserver:/tmp/init.sql
docker cp  .\data\dbo.MYTABLE.csv sqlserver:/tmp/dbo.MYTABLE.csv
docker-compose exec sqlserver /opt/mssql-tools/bin/sqlcmd  -i /tmp/init.sql -U sa -P "<PASSWORD>"
```

`setup.bat`を実行します。

```
Changed database context to 'MyDB'.
(XXXX rows affected)
```

と出れば成功です！
再度、コンテナを停止・起動してください。

```bash
docker-compose stop
docker-compose up -d
```

## DBにアクセス

コンテナが起動している状態なので、ホストPCからDBにアクセスできます。A5:SQL Mk-2というDBクライアントからアクセスする場合は、メニューバーの「データベース」→「データベースの追加と削除」→[追加]→[Microsoft SQL Server と SQL Server Compact]→「基本」タブを開きます。
接続タイプを「SQL Server」に、サーバ名を「localhost」に、ポート番号を「1433」に、「SQL Server 認証を使用する」を選択し、ユーザIDを「sa」、パスワードを`docker-compose.yml`に書いたものと同じにします。データベース名は「MyDB」と入力して[OK]を押します。
あとは適当に名前を付けて保存すればDBにアクセスできます。

以上！