# Выполнение_запроса_по_списку_БД
## Настройка Python
В Python версии 3.7 и выше дополнительно необходимо установить `pip install oracledb, pip install pandas, pip install requests, pip install openpyxl`.
В Python версии 3.7 и выше cx_oracle не работает, нужен oracledb, при необходимости запуска на Python 3.0-3.7 заменить oracledb на cx_oracle

## Python 3.7+ Параллельный запуск запроса или скрипта
```python=
import oracledb
import threading
from pandas import DataFrame
import datetime
import os
import requests
##########################################################################################
###                Список переменных, которые нужно изменить                           ###
##########################################################################################
# Директория с клиентом Oracle
oracledb.init_oracle_client(lib_dir=r"D:\instantclient_21_11")
# Запрос или скрипт в txt файле, который нужно выполнять
QUERY = open('qry/hidden_par_val.sql').read()
# Режим выполнения:
# 0 - выполнение запроса, 1 - выполнение скрипта
execution_mode = 0
# Режим получения списка баз данных:
# 0 - получение из файла, 1 - получение всех актуальных БД через HTTP-запрос к CDMS
db_list_mode = 1
# Файл с списком БД для db_list_mode=0
BASELIST = open('bases/baselist.txt').readlines()
# Токен для подключения к CDMS для db_list_mode=1
api_token = "cdmt@pve!"
# Логин и пароль для подключения к БД Oracle
or_usr = 'sys'
or_pas = ''
##########################################################################################
###   Далее ничего менять не нужно. Результат выполнения будет сохранён в excel файл   ###
###   в директорию output  с именем - script_res или query_res и текущей датой         ###
##########################################################################################
# Функция для получения списка баз данных через HTTP-запрос к CDMS
def get_oracle_bases_from_http():
    db_url = "https://cdms.colvir.ru/api/getactualdb"
    headers = {
        "Content-Type": "application/json",
        "Accept": "application/json"
    }
     
    payload = {
        "api_token": api_token
    }
    try:
        print("Получаем список БД через API")
        response = requests.get(
            db_url,
            headers=headers,
            json=payload
        )
        response.raise_for_status()
         
        data = response.json()
        print(f"HTTP Status Code: {response.status_code}")
        return [item['con_str'] for item in data]
         
    except requests.exceptions.RequestException as e:
        print(f"Ошибка при выполнении HTTP-запроса: {e}")
        return []
    except ValueError as ve:
        print(f"Ошибка декодирования JSON: {ve}")
        return []
    except KeyError as ke:
        print(f"Ошибка в структуре ответа: отсутствует ключ {ke}")
        return []            
#Очистка экрана
def cls():
    os.system('cls' if os.name=='nt' else 'clear')
     
#выполнение запроса или скрипта
def exec_qry(idx, con_str):
    print(f"Поток {idx}: обработка {con_str}".ljust(80), end='\r')
    try:
        exc = ' '
        val = ' '
        # Условие для установки режима SYSDBA только для пользователя 'sys'
        if or_usr.lower() == 'sys':
            con = oracledb.connect(user=or_usr, password=or_pas, dsn=con_str, mode=oracledb.SYSDBA)
        else:
            con = oracledb.connect(user=or_usr, password=or_pas, dsn=con_str)
        cur = con.cursor()
        cur.execute(QUERY)
        # Выполнение запроса
        if execution_mode == 0:           
            for result in cur:
                val = ';'.join(map(str, result))
        # Выполнение скрипта
        else:
            val = 'Ok'
        cur.close()
        con.close()
    except oracledb.DatabaseError as e:
        error_obj, = e.args
        exc = error_obj.message
    finally:
        base_list.append(con_str)
        value_list.append(val)
        except_list.append(exc)
    return
#Основной код
cls()   
# Получение списка баз данных
BASES = BASELIST if db_list_mode == 0 else get_oracle_bases_from_http()
if len(BASES) == 0:
    print("Нет БД для обработки. Выходим")
    exit()
else:
    print(f"Список БД получен, количество БД в списке: {len(BASES)}")
    print("Запускаем многопоточную обработку")
base_list, value_list, except_list = [], [], []
# Запуск потоков для каждой базы данных
threads = list()
for BASE in BASES:
    idx = len(threads)
    x = threading.Thread(target=exec_qry, args=(idx, BASE.strip(),))
    threads.append(x)
    x.start()
print(f"Все потоки запущены. Количество запущенных потоков:{idx+1}")
for index, thread in enumerate(threads):
    print(f"Основной поток: ожидаем ответ потока #{index} для БД {BASES[index].strip()}".ljust(80), end='\r')
    thread.join()
print()
print(f"Закончили, формируем excel файл ")
# Генерация имени выходного файла и создание директории, если её нет
os.makedirs('output', exist_ok=True)
output_prefix = 'script_res' if execution_mode == 1 else 'query_res'
date_str = datetime.datetime.now().strftime('%Y%m%d')
output_filename = f"{output_prefix}-{date_str}-1.xlsx"
# Проверка на существование файла
counter = 1
while os.path.exists(f"output/{output_filename}"):
    counter += 1
    output_filename = f"{output_prefix}-{date_str}-{counter}.xlsx"
df = DataFrame({'База': base_list, 'Результат': value_list, 'Исключение': except_list})
df.to_excel(f'output/{output_filename}', sheet_name='sheet1', index=False)
print(f"Excel файл сформирован: output/{output_filename}")
```
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9