mssql to clickhouse
RN APK Build / build (push) Has been cancelled

This commit is contained in:
NishantRajputRN
2026-05-13 16:49:56 +05:30
parent e4349e8459
commit a5c3ae1343
+328
View File
@@ -0,0 +1,328 @@
#!/usr/bin/env bash
# Migrate SQL Server tables into ClickHouse using sqlcmd + clickhouse-client.
#
# Recommended paths:
# - Full database (creates Nullable(String) columns, safe TSV): migrate-db
# - One table via temp file (same safety as migrate-db): migrate-table
#
# Optional "stream" mode pipes sqlcmd straight into ClickHouse (like a manual
# one-liner). Use only when data has no tabs/newlines and NULL handling matches
# your expectations; prefer migrate-table otherwise.
#
# Environment (examples — set in your shell or a sourced file):
# export MSSQL_HOST="43.242.212.54"
# export MSSQL_PORT="21443"
# export MSSQL_USER="Nishant_Dev"
# export MSSQL_PASSWORD='nishant@dev'
# export MSSQL_DATABASE="CPMIndiaBusinessInsight_test"
# export MSSQL_TRUST_SERVER_CERT="1"
# # export MSSQL_ENCRYPT="optional"
#
# export CH_DOCKER_CONTAINER="clickhouse"
# export CH_USER="default"
# export CH_PASSWORD=""
# export CH_PORT="9000"
# export CH_HOST="127.0.0.1"
# export CH_DATABASE="cpm"
#
# export MSSQL_CH_EXPORT_DIR="./mssql_export_all"
#
set -euo pipefail
SCRIPT_NAME=$(basename "$0")
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CORE="${ROOT_DIR}/mssql_to_clickhouse.sh"
die() {
echo "$SCRIPT_NAME: $*" >&2
exit 1
}
usage() {
cat <<EOF
$SCRIPT_NAME — MSSQL → ClickHouse migration (wraps ${CORE##*/}).
Commands:
migrate-db [--mssql-schema SCHEMA] [--tables-file PATH] [--out-dir DIR]
Create ClickHouse database/tables and copy all discoverable MSSQL base tables.
Uses safe export (NULL as \\N, tabs/newlines stripped in text).
migrate-table --mssql-table SCHEMA.TABLE [--ch-database DB] [--out-dir DIR]
Create table if needed and copy one MSSQL table (delegates to migrate-db with a one-line list).
export-import --mssql-table SCHEMA.TABLE [--ch-database DB] [--ch-table NAME] [--out FILE]
Export then import one table (ClickHouse table must already exist with matching columns).
list-tables [--mssql-schema SCHEMA]
Print SCHEMA<TAB>TABLE rows from SQL Server discovery.
stream-table --mssql-table SCHEMA.TABLE [--ch-database DB] [--ch-table NAME]
Pipe: sqlcmd SELECT * | clickhouse-client INSERT FORMAT TabSeparated.
Less safe than migrate-table; for quick tests or clean numeric/text data.
print-env
Print current MSSQL/CH-related environment (passwords masked).
Environment defaults this wrapper applies before delegating:
MSSQL_PORT=\${MSSQL_PORT:-1433}
CH_PORT=\${CH_PORT:-9000}
CH_USER=\${CH_USER:-default}
CH_HOST=\${CH_HOST:-127.0.0.1}
CH_DATABASE (or pass --ch-database) — ClickHouse database name, e.g. cpm
Examples:
export CH_DATABASE=cpm CH_DOCKER_CONTAINER=clickhouse MSSQL_TRUST_SERVER_CERT=1
$SCRIPT_NAME migrate-table --mssql-table dbo.Category_Execution
$SCRIPT_NAME migrate-db --ch-database cpm --out-dir ./ch_export
EOF
}
apply_defaults() {
export MSSQL_PORT="${MSSQL_PORT:-1433}"
export CH_PORT="${CH_PORT:-9000}"
export CH_USER="${CH_USER:-default}"
export CH_HOST="${CH_HOST:-127.0.0.1}"
}
ensure_core() {
[[ -x "$CORE" || -f "$CORE" ]] || die "Missing core script: $CORE"
}
delegate() {
apply_defaults
ensure_core
bash "$CORE" "$@"
}
mask() {
local v="${1:-}"
if [[ -z "$v" ]]; then
printf "(empty)"
else
printf "***"
fi
}
cmd_print_env() {
apply_defaults
cat <<EOF
MSSQL_HOST=${MSSQL_HOST:-}
MSSQL_PORT=${MSSQL_PORT:-}
MSSQL_USER=${MSSQL_USER:-}
MSSQL_PASSWORD=$(mask "${MSSQL_PASSWORD:-}")
MSSQL_DATABASE=${MSSQL_DATABASE:-}
MSSQL_TRUST_SERVER_CERT=${MSSQL_TRUST_SERVER_CERT:-}
MSSQL_ENCRYPT=${MSSQL_ENCRYPT:-}
MSSQL_TABLES_FILE=${MSSQL_TABLES_FILE:-}
CH_DOCKER_CONTAINER=${CH_DOCKER_CONTAINER:-}
CH_HOST=${CH_HOST:-}
CH_PORT=${CH_PORT:-}
CH_USER=${CH_USER:-}
CH_PASSWORD=$(mask "${CH_PASSWORD:-}")
CH_SECURE=${CH_SECURE:-}
CH_DATABASE=${CH_DATABASE:-}
MSSQL_CH_EXPORT_DIR=${MSSQL_CH_EXPORT_DIR:-}
EOF
}
ensure_sqlcmd() {
if command -v sqlcmd >/dev/null 2>&1; then
return 0
fi
if [[ -x /opt/mssql-tools18/bin/sqlcmd ]]; then
export PATH="/opt/mssql-tools18/bin:$PATH"
return 0
fi
die "sqlcmd not found (install mssql-tools18 or add to PATH)"
}
ensure_mssql_env() {
[[ -n "${MSSQL_HOST:-}" ]] || die "Set MSSQL_HOST"
[[ -n "${MSSQL_USER:-}" ]] || die "Set MSSQL_USER"
[[ -n "${MSSQL_PASSWORD:-}" ]] || die "Set MSSQL_PASSWORD"
[[ -n "${MSSQL_DATABASE:-}" ]] || die "Set MSSQL_DATABASE"
}
sqlcmd_conn_args() {
local port="${MSSQL_PORT:-1433}"
local server="${MSSQL_HOST},${port}"
local args=(
-S "$server"
-d "$MSSQL_DATABASE"
-U "$MSSQL_USER"
-P "$MSSQL_PASSWORD"
)
if [[ "${MSSQL_TRUST_SERVER_CERT:-0}" == "1" ]]; then
args+=(-C)
fi
if [[ -n "${MSSQL_ENCRYPT:-}" ]]; then
args+=(-N "$MSSQL_ENCRYPT")
fi
printf '%s\n' "${args[@]}"
}
ch_default_database() {
printf '%s' "${CH_DATABASE:-default}"
}
escape_ch_ident() {
local ident="${1:-}"
ident="${ident//\`/\`\`}"
printf "%s" "$ident"
}
cmd_stream_table() {
apply_defaults
ensure_sqlcmd
ensure_mssql_env
local mssql_table=""
local ch_db=""
local ch_table=""
while [[ $# -gt 0 ]]; do
case "$1" in
--mssql-table)
mssql_table="$2"
shift 2
;;
--ch-database)
ch_db="$2"
shift 2
;;
--ch-table)
ch_table="$2"
shift 2
;;
*)
die "Unknown option: $1"
;;
esac
done
[[ -n "$mssql_table" ]] || die "stream-table requires --mssql-table SCHEMA.TABLE"
ch_db="${ch_db:-$(ch_default_database)}"
if [[ -z "$ch_table" ]]; then
if [[ "$mssql_table" == *.* ]]; then
ch_table="${mssql_table#*.}"
else
ch_table="$mssql_table"
fi
fi
[[ -n "${CH_DOCKER_CONTAINER:-}" ]] || die "stream-table expects CH_DOCKER_CONTAINER for docker exec -i"
command -v docker >/dev/null 2>&1 || die "docker not found"
local conn_args=()
mapfile -t conn_args < <(sqlcmd_conn_args)
local insert_q
insert_q="INSERT INTO \`$(escape_ch_ident "$ch_db")\`.\`$(escape_ch_ident "$ch_table")\` FORMAT TabSeparated"
echo "$SCRIPT_NAME: streaming SELECT * from $mssql_table${ch_db}.${ch_table} (ensure table exists and types match)." >&2
local ch_args=(
--host "${CH_HOST:-127.0.0.1}"
--port "${CH_PORT:-9000}"
--user "${CH_USER:-default}"
--query "$insert_q"
)
[[ -n "${CH_PASSWORD:-}" ]] && ch_args+=(--password "$CH_PASSWORD")
[[ "${CH_SECURE:-0}" == "1" ]] && ch_args+=(--secure)
sqlcmd "${conn_args[@]}" \
-Q "SET NOCOUNT ON; SELECT * FROM ${mssql_table}" \
-h -1 -W -s "$(printf '\t')" -w 65535 -f i:65001,o:65001 -b \
| docker exec -i "$CH_DOCKER_CONTAINER" clickhouse-client "${ch_args[@]}"
}
cmd_migrate_table() {
apply_defaults
ensure_core
local mssql_table=""
local ch_db=""
local out_dir=""
while [[ $# -gt 0 ]]; do
case "$1" in
--mssql-table)
mssql_table="$2"
shift 2
;;
--ch-database)
ch_db="$2"
shift 2
;;
--out-dir)
out_dir="$2"
shift 2
;;
*)
die "Unknown option: $1"
;;
esac
done
[[ -n "$mssql_table" ]] || die "migrate-table requires --mssql-table SCHEMA.TABLE"
ch_db="${ch_db:-$(ch_default_database)}"
out_dir="${out_dir:-${MSSQL_CH_EXPORT_DIR:-./mssql_export_all}}"
local tmp
tmp="$(mktemp "${TMPDIR:-/tmp}/${SCRIPT_NAME}.tables.XXXXXX")"
trap 'rm -f "$tmp"' RETURN
printf '%s\n' "$mssql_table" >"$tmp"
bash "$CORE" migrate-db \
--ch-database "$ch_db" \
--out-dir "$out_dir" \
--tables-file "$tmp"
}
main() {
[[ $# -ge 1 ]] || {
usage
exit 1
}
local cmd="$1"
shift || true
if [[ "$cmd" == "-h" || "$cmd" == "--help" ]]; then
usage
exit 0
fi
case "$cmd" in
print-env)
cmd_print_env
;;
migrate-db)
delegate migrate-db \
--ch-database "$(ch_default_database)" \
--out-dir "${MSSQL_CH_EXPORT_DIR:-./mssql_export_all}" \
"$@"
;;
list-tables)
delegate list-tables "$@"
;;
migrate-table)
cmd_migrate_table "$@"
;;
export-import)
delegate export-import \
--ch-database "$(ch_default_database)" \
"$@"
;;
stream-table)
cmd_stream_table "$@"
;;
*)
usage
die "Unknown command: $cmd"
;;
esac
}
main "$@"