'Docker initialize database tables and records in SQL Server

How do you specify initialization database script for SQL Server via docker-compose using SQL script file?

docker-compose.yml

database:
    image: microsoft/mssql-server-linux:2017-latest
    container_name: database
    ports:
      - 1433:1433
    volumes:
      - /var/opt/mssql
    environment:
      SA_PASSWORD: "P@55w0rd"
      ACCEPT_EULA: "Y"

schema.sql

CREATE TABLE Department
(
    Id INT PRIMARY KEY IDENTITY (1, 1),
    Name VARCHAR (50) NOT NULL
);

CREATE TABLE Student
(
    Id INT PRIMARY KEY IDENTITY (1, 1),
    Name VARCHAR (50) NOT NULL
);

CREATE TABLE Course 
(
    Id INT PRIMARY KEY IDENTITY (1, 1),
    Name VARCHAR (50) NOT NULL
);

INSERT INTO Student (Id, Name) VALUES(1, "John Doe");
INSERT INTO Student (Id, Name) VALUES(2, "Jane Doe");


Solution 1:[1]

If you check the offical documentataion, it suggests to use mssql-docker-demo-app which contain entrypoint script like MySQL container.

import-data.sh

The import-data.sh script is a convenient way to delay the execution of the SQL commands until SQL Server is started. Typically SQL Server takes about 5-10 seconds to start up and be ready for connections and commands.

The next command uses the SQL Server command line utility sqlcmd to execte some SQL commands contained in the setup.sql file.

The setup.sql script will create a new database called DemoData and a table called Products in the default dbo schema.

setup.sql

The setup.sql defines some simple commands to create a database and some simple schema. You could use a .sql file like this for other purposes like creating logins, assigning permissions, creating stored procedures, and much more. When creating a database in production situations, you will probably want to be more specific about where the database files are created so that the database files are stored in persistent storage. This SQL script creates a table with two columns -

ID (integer) and ProductName (nvarchar(max)).

CREATE DATABASE DemoData;
GO
USE DemoData;
GO
CREATE TABLE Products (ID int, ProductName nvarchar(max));
GO

Solution 2:[2]

For those who like a little more specifics/examples.

As @Adiii suggested this is how you can initialise the database within a docker-compose file

docker-compose.yml

  test-database:
    build: ./mssql_database
    environment:
      - ACCEPT_EULA=Y
      - SA_PASSWORD=<YOUR PASSWORD>
      - TZ=UTC
    volumes:
      - database-data:/var/opt/mssql

volumes:
    database-data:

Directory mssql_database contains

Dockerfile

FROM mcr.microsoft.com/mssql/server

COPY setup.sql setup.sql
COPY setup_database.sh setup_database.sh
COPY entrypoint.sh entrypoint.sh

CMD /bin/bash ./entrypoint.sh

NOTE: If you want to initialise the database during build time instead of run time replace the CMD /bin/bash ./entrypoint.shwith RUN ./opt/mssql/bin/sqlservr & ./setup_database.sh

entrypoint.sh

#!/usr/bin/env bash
set -m
./opt/mssql/bin/sqlservr & ./setup_database.sh
fg

setup_database.sh

NOTE: Instead of using a sleep it would be better to use a healthcheck and once the database is confirmed green run your scripts.

#!/usr/bin/env bash
# Wait for database to startup 
sleep 20
./opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P <YOUR PASSWORD> -i setup.sql

setup.sql

INSERT your sql commands here 

Solution 3:[3]

A note if you do use docker-compose and not just bare docker. A snippet from my docker-compose file:

  sql-server:
    image: mcr.microsoft.com/mssql/server:2017-latest-ubuntu
    hostname: sql-server
    container_name: sql-server
    ports:
    - 1433:1433
#    Based on: https://www.softwaredeveloper.blog/initialize-mssql-in-docker-container or similarly: https://github.com/twright-msft/mssql-node-docker-demo-app
    command: bash -c "chmod -R 777 /usr/src/sql/ && /usr/src/sql/entrypoint.sh"
    environment:
      ACCEPT_EULA: Y
      SA_PASSWORD: Password123
      MSSQL_PID: Express
    volumes:
      - ./sql/:/usr/src/sql/

Now, since I don't use custom Dockerfile I'm obviously not mangling with workdir. And when I run:

docker inspect mcr.microsoft.com/mssql/server:2017-latest-ubuntu | grep -i "DIR"

I get:

    "WorkingDir": "",
    "WorkingDir": "",

Meaning working dir is not set and so you land in the root folder inside the container by default.

This will make scripts like: ./opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P <YOUR PASSWORD> -i setup.sql non-functioning because there's no setup.sql in the root dir. In that case you need to specify absolute path like:

/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P ${SA_PASSWORD} -d master -i /usr/src/sql/setup.sql

Solution 4:[4]

Another way of doing it, without having to build your own image (no Dockerfile):

# [Create and] Run the MSSQL container
docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=<sa_password>" --name <container_id> -p 1433:1433 -d mcr.microsoft.com/mssql/server

# Create a "sqlsetup" directory inside the container
docker exec -it <container_id> mkdir /usr/sqlsetup

# Copy your database setup script to the container
docker cp setup.sql <container_id>:/usr/sqlsetup/setup.sql

# Run the script on the database
docker exec -it <container_id> /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P <sa_password> -d master -i /usr/sqlsetup/setup.sql

Do not run all the commands at once. SQL Server takes some time to be up and running, so you need some delay to allow it to be running before trying to execute the script.

Solution 5:[5]

I had the the same issue, I got inspired by this post https://www.softwaredeveloper.blog/initialize-mssql-in-docker-container

but I made a script that init the database only if not existing: 1- I COPY the sql and sh script in the dockerfile 2- I change the command in the DockeFile: CMD /bin/bash ./entrypoint.sh 3- I create an entrypoint.sh that will call the init script and start the server 4- I create my init script

#!/bin/bash
# Wait to be sure that SQL Server came up
sleep 30s
# DATABSE initialisation
echo "Database initialisation"
# if the table does not exsit it will create the table
# get "1" if the database exist : 'tr' get only the integer, 'cut' only the first integer (the second is the number of row affected)
data=$(/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P $SA_PASSWORD -Q "SELECT COUNT(*)  FROM master.dbo.sysdatabases WHERE name = N'$DB_NAME'" | tr -dc '0-9'| cut -c1 )
if [ ${data} -eq "0" ]; then
        echo 'create database user'
        /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P $SA_PASSWORD -Q "CREATE LOGIN $DB_USER WITH PASSWORD='${SA_PASSWORD}', CHECK_POLICY = OFF"

        echo 'create database'
        /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P $SA_PASSWORD -Q "DROP DATABASE IF EXISTS $DB_NAME"
        /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P $SA_PASSWORD -Q "CREATE DATABASE $DB_NAME"
        # RUN YOUR SQL FILE HERE

        echo ' give to the user the access to the database'
        /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P $SA_PASSWORD -Q "CREATE USER $DB_USER FOR LOGIN $DB_USER " -d $DB_NAME
else
        echo "database already existing, nothing to do"
fi

# manual cleaning command
# /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P $SA_PASSWORD -Q "DROP DATABASE $DB_NAME"
# /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P $SA_PASSWORD -Q "DROP  LOGIN  $DB_USER"

I hope it can help someone, you can find the github repo where I did it here : https://github.com/openimis/openimis-db_dkr/blob/release/202204/Dockerfile

I use the same approach for the health check:

data=$(/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P $SA_PASSWORD -Q "SELECT COUNT(*)  FROM master.dbo.sysdatabases WHERE name = N'$DB_NAME'" | tr -dc '0-9'| cut -c1 )
if [ ${data} -eq "1" ]; then
    exit 0
else
    exit 1
fi

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 yuranos
Solution 4 Daniel Ribeiro
Solution 5 Delcroip