Вопрос или проблема
Этот скрипт bash делает извлечение данных из Oracle DB (другой сервер), обрабатывает их и вставляет в MariaDB (мой сервер), но вставка неправильно выровнена и оставляет некоторые столбцы пустыми.
Вот код:
#!/bin/bash
ORACLE_USER="user"
ORACLE_PASSWORD="password"
ORACLE_DB="IP/SID"
MYSQL_USER="user"
MYSQL_PASSWORD="password"
MYSQL_DB="DB"
echo "Извлечение данных из Oracle..."
ORACLE_DATA=$(sqlplus -s "$ORACLE_USER/$ORACLE_PASSWORD@$ORACLE_DB" <<EOF
SET PAGESIZE 50000
SET COLSEP '|'
SET LINESIZE 1000
SET HEADING OFF
SET FEEDBACK OFF
SET TERMOUT OFF
select TRIM(REGION), TRIM(CENTRAL), TRIM(NOMBRE_BANCO), TRIM(MODELO), TRIM(BATERIA), TRIM(TECNOLOGIA_ID), TRIM(AMPERAJE_CA), TO_CHAR(MEDIDO, 'YYYY-MM-DD') AS MEDIDO, TRIM(PORCENTAJE), TRIM(VOLTAJE), TRIM(VOLTAJE_AC), TO_CHAR(CREADO, 'YYYY-MM-DD') AS CREADO
from SIMBA_BD.VIEW_CENTRAL_MEDICIONES_PLOMO where rownum<=10;
EXIT;
EOF
)
echo "Данные извлечены из Oracle:"
echo "$ORACLE_DATA"
echo "Очистка таблицы MariaDB..."
mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DB" <<EOF
TRUNCATE TABLE SIMBA;
EOF
echo "Вставка данных в MariaDB..."
while IFS='|' read -r REGION CENTRAL NOMBRE_BANCO MODELO BATERIA TECNOLOGIA_ID AMPERAJE_CA MEDIDO PORCENTAJE VOLTAJE VOLTAJE_AC CREADO; do
REGION=$(echo "$REGION" | tr -d '\r\n')
CENTRAL=$(echo "$CENTRAL" | tr -d '\r\n')
NOMBRE_BANCO=$(echo "$NOMBRE_BANCO" | tr -d '\r\n')
MODELO=$(echo "$MODELO" | tr -d '\r\n')
BATERIA=$(echo "$BATERIA" | tr -d '\r\n')
TECNOLOGIA_ID=$(echo "$TECNOLOGIA_ID" | tr -d '\r\n')
AMPERAJE_CA=$(echo "$AMPERAJE_CA" | tr -d '\r\n')
MEDIDO=$(echo "$MEDIDO" | tr -d '\r\n')
PORCENTAJE=$(echo "$PORCENTAJE" | tr -d '\r\n')
VOLTAJE=$(echo "$VOLTAJE" | tr -d '\r\n')
VOLTAJE_AC=$(echo "$VOLTAJE_AC" | tr -d '\r\n')
CREADO=$(echo "$CREADO" | tr -d '\r\n')
REGION=${REGION:-""}
CENTRAL=${CENTRAL:-""}
NOMBRE_BANCO=${NOMBRE_BANCO:-""}
MODELO=${MODELO:-""}
BATERIA=${BATERIA:-""}
TECNOLOGIA_ID=${TECNOLOGIA_ID:-""}
AMPERAJE_CA=${AMPERAJE_CA:-0}
MEDIDO=${MEDIDO:-"NULL"}
PORCENTAJE=${PORCENTAJE:-0}
VOLTAJE=${VOLTAJE:-0}
VOLTAJE_AC=${VOLTAJE_AC:-0}
CREADO=${CREADO:-"NULL"}
echo "Регион: $REGION"
echo "Центральный: $CENTRAL"
echo "Название_банка: $NOMBRE_BANCO"
echo "Модель: $MODELO"
echo "Батарея: $BATERIA"
echo "Технология: $TECNOLOGIA_ID"
echo "Амперная_сила: $AMPERAJE_CA"
echo "Измерено: $MEDIDO"
echo "Процент: $PORCENTAJE"
echo "Напряжение: $VOLTAJE"
echo "Напряжение_AC: $VOLTAJE_AC"
echo "Создано: $CREADO"
if ! [[ "$MEDIDO" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
MEDIDO="NULL"
fi
if ! [[ "$CREADO" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
CREADO="NULL"
fi
if ! [[ "$AMPERAJE_CA" =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then
AMPERAJE_CA=0
fi
if ! [[ "$PORCENTAJE" =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then
PORCENTAJE=0
fi
if ! [[ "$VOLTAJE" =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then
VOLTAJE=0
fi
if ! [[ "$VOLTAJE_AC" =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then
VOLTAJE_AC=0
fi
mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DB" <<EOF
INSERT INTO SIMBA (Region, Central, Nombre_Banco, Modelo, Bateria, Tecnologia, Amperaje_CA, Medido, Porcentaje, Voltaje, Voltaje_AC, Creado) VALUES ('$REGION', '$CENTRAL', '$NOMBRE_BANCO', '$MODELO', '$BATERIA', '$TECNOLOGIA_ID', '$AMPERAJE_CA', IF('$MEDIDO' = 'NULL', NULL, '$MEDIDO'), '$PORCENTAJE', '$VOLTAJE', '$VOLTAJE_AC', IF('$CREADO' = 'NULL', NULL, '$CREADO'));
EOF
done <<< "$ORACLE_DATA" >> SALIDA.TXT
echo "Вставка данных завершена."
А результаты выглядят так:
Регион:
Центральный:
Название_банка:
Модель:
Батарея:
Технология:
Амперная_сила:
Измерено:
Процент:
Напряжение:
Напряжение_AC:
Создано:
Регион: REGION 7
Центральный: LERFIC
Название_банка: 7LERFICCT1B6
Модель: БАТАРЕЯ GNB ABSOLYTE, 100A-19 896AH
Батарея:
Технология:
Амперная_сила:
Измерено:
Процент:
Напряжение:
Напряжение_AC:
Создано:
Регион: J10B
Центральный: TYPHOON(TY1)
Название_банка: 0
Модель: 2016-12-30
Батарея: 54.45
Технология: 2.25
Амперная_сила: 0
Измерено: 2017-01-10
Процент:
Напряжение:
Напряжение_AC:
Создано:
Я посмотрел на это, и это полная неразбериха; нам не следует это исправлять; идея о том, что вы берете несогласованные данные из системы баз данных, позволяете базе данных вывести их в виде таблицы, а затем парсите эту таблицу в скрипте оболочки, чтобы затем сгенерировать SQL-вставки, слишком сложна и подвержена ошибкам.
Это может быть гораздо меньшей программой почти на любом другом языке сценариев, где вам не нужно получать строки из результата запроса к базе данных как текст, а затем разделять их обратно на значения в наименее полезном синтаксисе.
Например, на python вы могли бы просто выполнить запрос, а затем буквально вставить все результаты после фильтрации сразу. Одна строка кода.
Я боюсь, что иногда честный ответ на вопрос «как мне исправить мой скрипт» — это то, что вам нужно перестать использовать оболочку для задач, для которых она на самом деле не предназначена.
Существуют и другие подводные камни, которых вы избежите, делая это, и используя подготовленные операторы вместо того, чтобы создавать свою собственную строку insert(…) … VALUES($VARIABLE1, $VARIABLE2,…)
. Все любят Бобби Таблицы:
XKCD 237: “Эксплойты мамы” Рэндалла Манро; по лицензии CC-by-NC 2.5
Ответ или решение
Для выполнения извлечения данных из базы данных Oracle и их встраивания в MariaDB через Bash-скрипт, важно отметить, что описанный вами процесс может быть подвержен ошибкам, что приводит к неполной вставке данных. Рассмотрим предложенный код и его потенциальные проблемы, а затем обсудим оптимизацию этого процесса.
Анализ существующего скрипта
-
Извлечение данных: Данные извлекаются с помощью sqlplus, затем обрабатываются через
while read
, что может быть трудоемким и необязательным. -
Проблемы с пропущенными колонками:
- Судя по представленным результатам, некоторые значения не извлекаются должным образом. Это может происходить из-за неправильного разделения данных. Если значение в одном из столбцов содержит символ ‘|’ (разделитель), это нарушит формат.
- Ошибки также могут возникать при использовании
tr -d '\r\n'
, особенно если ожидание было, что данные в строке остаются в исходном виде.
-
Проблемы с вставкой: Создание SQL-запроса для вставки значений ведет к потенциальным уязвимостям, связанным с SQL-инъекциями, поскольку значение переменных не экранируется должным образом.
Рекомендация по улучшению
Поскольку Bash-скрипты могут быть недостаточно надежными для обработки данных и вставки их в базу данных, есть смысл рассмотреть более подходящие инструменты, такие как Python. Это поможет избежать проблем, связанных с разбором строк и SQL-инъекциями. Вот как можно реализовать этот процесс на Python:
Пример на Python
import cx_Oracle
import mysql.connector
# Заключаем параметры подключения
oracle_config = {
'user': 'user',
'password': 'password',
'dsn': 'IP/SID'
}
mysql_config = {
'user': 'user',
'password': 'password',
'database': 'DB'
}
# Извлечение данных из Oracle
with cx_Oracle.connect(**oracle_config) as oracle_conn:
cursor = oracle_conn.cursor()
query = """
SELECT TRIM(REGION), TRIM(CENTRAL), TRIM(NOMBRE_BANCO), TRIM(MODELO),
TRIM(BATERIA), TRIM(TECNOLOGIA_ID), TRIM(AMPERAJE_CA),
TO_CHAR(MEDIDO, 'YYYY-MM-DD'), TRIM(PORCENTAJE),
TRIM(VOLTAJE), TRIM(VOLTAJE_AC),
TO_CHAR(CREADO, 'YYYY-MM-DD')
FROM SIMBA_BD.VIEW_CENTRAL_MEDICIONES_PLOMO WHERE ROWNUM <= 10
"""
cursor.execute(query)
result_set = cursor.fetchall()
# Сохранение данных в MariaDB
with mysql.connector.connect(**mysql_config) as mysql_conn:
cursor = mysql_conn.cursor()
cursor.execute("TRUNCATE TABLE SIMBA")
insert_query = """
INSERT INTO SIMBA (Region, Central, Nombre_Banco, Modelo, Bateria,
Tecnologia, Amperaje_CA, Medido, Porcentaje,
Voltaje, Voltaje_AC, Creado)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
for row in result_set:
cursor.execute(insert_query, row)
mysql_conn.commit()
cursor.close()
Заключение
Использование языка программирования, более подходящего для обработки данных, упрощает процесс и позволяет избежать сложных манипуляций со строками, что делает код более читаемым и менее подверженным ошибкам. Кроме того, использование подготовленных выражений значительно повышает безопасность при работе с базами данных.
Если вы продолжаете использовать Bash, убедитесь, что все данные корректно экранируются и разделяются, однако на практике это часто оказывается более сложным, чем использование другого языка программирования.