Blame
|
1 | # Выполнение_запроса_по_списку_БД |
||||||
| 2 | ## Настройка Python |
|||||||
| 3 | В Python версии 3.7 и выше дополнительно необходимо установить `pip install oracledb, pip install pandas, pip install requests, pip install openpyxl`. |
|||||||
| 4 | В Python версии 3.7 и выше cx_oracle не работает, нужен oracledb, при необходимости запуска на Python 3.0-3.7 заменить oracledb на cx_oracle |
|||||||
| 5 | ||||||||
| 6 | ## Python 3.7+ Параллельный запуск запроса или скрипта |
|||||||
| 7 | ```python |
|||||||
| 8 | import oracledb |
|||||||
| 9 | import threading |
|||||||
| 10 | from pandas import DataFrame |
|||||||
| 11 | import datetime |
|||||||
| 12 | import os |
|||||||
| 13 | import requests |
|||||||
| 14 | ########################################################################################## |
|||||||
| 15 | ### Список переменных, которые нужно изменить ### |
|||||||
| 16 | ########################################################################################## |
|||||||
| 17 | # Директория с клиентом Oracle |
|||||||
| 18 | oracledb.init_oracle_client(lib_dir=r"D:\instantclient_21_11") |
|||||||
| 19 | # Запрос или скрипт в txt файле, который нужно выполнять |
|||||||
| 20 | QUERY = open('qry/hidden_par_val.sql').read() |
|||||||
| 21 | # Режим выполнения: |
|||||||
| 22 | # 0 - выполнение запроса, 1 - выполнение скрипта |
|||||||
| 23 | execution_mode = 0 |
|||||||
| 24 | # Режим получения списка баз данных: |
|||||||
| 25 | # 0 - получение из файла, 1 - получение всех актуальных БД через HTTP-запрос к CDMS |
|||||||
| 26 | db_list_mode = 1 |
|||||||
| 27 | # Файл с списком БД для db_list_mode=0 |
|||||||
| 28 | BASELIST = open('bases/baselist.txt').readlines() |
|||||||
| 29 | # Токен для подключения к CDMS для db_list_mode=1 |
|||||||
| 30 | api_token = "cdmt@pve!" |
|||||||
| 31 | # Логин и пароль для подключения к БД Oracle |
|||||||
| 32 | or_usr = 'sys' |
|||||||
| 33 | or_pas = '' |
|||||||
| 34 | ########################################################################################## |
|||||||
| 35 | ### Далее ничего менять не нужно. Результат выполнения будет сохранён в excel файл ### |
|||||||
| 36 | ### в директорию output с именем - script_res или query_res и текущей датой ### |
|||||||
| 37 | ########################################################################################## |
|||||||
| 38 | # Функция для получения списка баз данных через HTTP-запрос к CDMS |
|||||||
| 39 | def get_oracle_bases_from_http(): |
|||||||
| 40 | db_url = "https://cdms.colvir.ru/api/getactualdb" |
|||||||
| 41 | headers = { |
|||||||
| 42 | "Content-Type": "application/json", |
|||||||
| 43 | "Accept": "application/json" |
|||||||
| 44 | } |
|||||||
| 45 | ||||||||
| 46 | payload = { |
|||||||
| 47 | "api_token": api_token |
|||||||
| 48 | } |
|||||||
| 49 | try: |
|||||||
| 50 | print("Получаем список БД через API") |
|||||||
| 51 | response = requests.get( |
|||||||
| 52 | db_url, |
|||||||
| 53 | headers=headers, |
|||||||
| 54 | json=payload |
|||||||
| 55 | ) |
|||||||
| 56 | response.raise_for_status() |
|||||||
| 57 | ||||||||
| 58 | data = response.json() |
|||||||
| 59 | print(f"HTTP Status Code: {response.status_code}") |
|||||||
| 60 | return [item['con_str'] for item in data] |
|||||||
| 61 | ||||||||
| 62 | except requests.exceptions.RequestException as e: |
|||||||
| 63 | print(f"Ошибка при выполнении HTTP-запроса: {e}") |
|||||||
| 64 | return [] |
|||||||
| 65 | except ValueError as ve: |
|||||||
| 66 | print(f"Ошибка декодирования JSON: {ve}") |
|||||||
| 67 | return [] |
|||||||
| 68 | except KeyError as ke: |
|||||||
| 69 | print(f"Ошибка в структуре ответа: отсутствует ключ {ke}") |
|||||||
| 70 | return [] |
|||||||
| 71 | #Очистка экрана |
|||||||
| 72 | def cls(): |
|||||||
| 73 | os.system('cls' if os.name=='nt' else 'clear') |
|||||||
| 74 | ||||||||
| 75 | #выполнение запроса или скрипта |
|||||||
| 76 | def exec_qry(idx, con_str): |
|||||||
| 77 | print(f"Поток {idx}: обработка {con_str}".ljust(80), end='\r') |
|||||||
| 78 | try: |
|||||||
| 79 | exc = ' ' |
|||||||
| 80 | val = ' ' |
|||||||
| 81 | # Условие для установки режима SYSDBA только для пользователя 'sys' |
|||||||
| 82 | if or_usr.lower() == 'sys': |
|||||||
| 83 | con = oracledb.connect(user=or_usr, password=or_pas, dsn=con_str, mode=oracledb.SYSDBA) |
|||||||
| 84 | else: |
|||||||
| 85 | con = oracledb.connect(user=or_usr, password=or_pas, dsn=con_str) |
|||||||
| 86 | cur = con.cursor() |
|||||||
| 87 | cur.execute(QUERY) |
|||||||
| 88 | # Выполнение запроса |
|||||||
| 89 | if execution_mode == 0: |
|||||||
| 90 | for result in cur: |
|||||||
| 91 | val = ';'.join(map(str, result)) |
|||||||
| 92 | # Выполнение скрипта |
|||||||
| 93 | else: |
|||||||
| 94 | val = 'Ok' |
|||||||
| 95 | cur.close() |
|||||||
| 96 | con.close() |
|||||||
| 97 | except oracledb.DatabaseError as e: |
|||||||
| 98 | error_obj, = e.args |
|||||||
| 99 | exc = error_obj.message |
|||||||
| 100 | finally: |
|||||||
| 101 | base_list.append(con_str) |
|||||||
| 102 | value_list.append(val) |
|||||||
| 103 | except_list.append(exc) |
|||||||
| 104 | return |
|||||||
| 105 | #Основной код |
|||||||
| 106 | cls() |
|||||||
| 107 | # Получение списка баз данных |
|||||||
| 108 | BASES = BASELIST if db_list_mode == 0 else get_oracle_bases_from_http() |
|||||||
| 109 | if len(BASES) == 0: |
|||||||
| 110 | print("Нет БД для обработки. Выходим") |
|||||||
| 111 | exit() |
|||||||
| 112 | else: |
|||||||
| 113 | print(f"Список БД получен, количество БД в списке: {len(BASES)}") |
|||||||
| 114 | print("Запускаем многопоточную обработку") |
|||||||
| 115 | base_list, value_list, except_list = [], [], [] |
|||||||
| 116 | # Запуск потоков для каждой базы данных |
|||||||
| 117 | threads = list() |
|||||||
| 118 | for BASE in BASES: |
|||||||
| 119 | idx = len(threads) |
|||||||
| 120 | x = threading.Thread(target=exec_qry, args=(idx, BASE.strip(),)) |
|||||||
| 121 | threads.append(x) |
|||||||
| 122 | x.start() |
|||||||
| 123 | print(f"Все потоки запущены. Количество запущенных потоков:{idx+1}") |
|||||||
| 124 | for index, thread in enumerate(threads): |
|||||||
| 125 | print(f"Основной поток: ожидаем ответ потока #{index} для БД {BASES[index].strip()}".ljust(80), end='\r') |
|||||||
| 126 | thread.join() |
|||||||
| 127 | print() |
|||||||
| 128 | print(f"Закончили, формируем excel файл ") |
|||||||
| 129 | # Генерация имени выходного файла и создание директории, если её нет |
|||||||
| 130 | os.makedirs('output', exist_ok=True) |
|||||||
| 131 | output_prefix = 'script_res' if execution_mode == 1 else 'query_res' |
|||||||
| 132 | date_str = datetime.datetime.now().strftime('%Y%m%d') |
|||||||
| 133 | output_filename = f"{output_prefix}-{date_str}-1.xlsx" |
|||||||
| 134 | # Проверка на существование файла |
|||||||
| 135 | counter = 1 |
|||||||
| 136 | while os.path.exists(f"output/{output_filename}"): |
|||||||
| 137 | counter += 1 |
|||||||
| 138 | output_filename = f"{output_prefix}-{date_str}-{counter}.xlsx" |
|||||||
| 139 | df = DataFrame({'База': base_list, 'Результат': value_list, 'Исключение': except_list}) |
|||||||
| 140 | df.to_excel(f'output/{output_filename}', sheet_name='sheet1', index=False) |
|||||||
| 141 | print(f"Excel файл сформирован: output/{output_filename}") |
|||||||
| 142 | ``` |
|||||||