Может ли одна и та же временная таблица SQL Server сохраняться и использоваться несколькими независимо выполняемыми сценариями Python без повторного создания каждый раз? - PullRequest
0 голосов
/ 26 марта 2020

Я извлекаю данные из базы данных SQL Сервера и сохраняю их в файлы для последующей обработки в Python.

Я использую Make для автоматизации выборки и повторного получения данных (в случае некоторых настроек изменение, только затронутая часть запросов запускается заново, а не все). Итак, у меня есть простой Makefile:

rawdata: datafile1.h5, datafile2.h5 # ... more files like this
datafile1.h5: data1_specific_config.py, common_config.py
    python fetch_data1.py
datafile2.h5: data2_specific_config.py, common_config.py
    python fetch_data2.py
# ... similar rules for other files

и при необходимости я просто запускаю make rawdata.

Теперь все SQL запросы, выполняемые сценариями fetch_dataN.py, имеют значимая общая часть . Схематически queryN, который запускается fetch_dataN.py, выглядит следующим образом:

select ... into ##common_tmp_table ... /*this is identical for all queries*/
select ... from (... ##common_tmp_table ...) /*this is queryN specific; but the same ##common_tmp_table is used*/

Вот проблема: когда я сейчас запускаю make rawdata в ситуации, когда, скажем, нужно перестроить пять разных файлов данных, затем один и тот же запрос select ... into ##common_tmp_table ... выполняется пять раз с идентичным выводом в ##common_tmp_table. Выполнение запроса занимает довольно много времени, поэтому повторное его выполнение в пять раз значительно замедляет все.

Но временная таблица всегда удаляется, когда завершается один сценарий fetch_dataN.py, поскольку соединение с БД, которое его создало, завершается .

Вопрос:

Есть ли способ заставить таблицу ##common_tmp_table создать только один раз и сохранить ее между всеми сценариями fetch_dataN.py, которые выполняется make rawdata?

В частности, есть ли способ использовать одно и то же соединение с БД во всех сценариях, запускаемых make rawdata? Или, может быть, открыть одно дополнительное соединение, которое будет сохраняться, пока будут работать все сценарии, и которое предотвратит удаление глобальной временной таблицы?

Обход, который я знаю: I Я могу обойти это, вручную создав ##common_tmp_table (например, в MS SQL Server Management Studio) перед запуском make rawdata и оставив соединение, используемое для этого, открытым до тех пор, пока все сценарии не будут завершены sh. Но это явно уродливо и раздражает.

Если make rawdata может открыть отдельный процесс, который откроет соединение, создаст таблицу tmp и будет ждать, пока все остальное не закончится, это было бы решением. Но я не знаю, возможно ли это.

Ограничения:

  • Я не могу внести изменения в базу данных (например, создать постоянную таблицу) вместо временного)
  • Мне нужно, чтобы скрипты были отделены друг от друга, чтобы они могли выполняться независимо от make (наличие всего в одном скрипте с тем же соединением базы данных и, следовательно, той же самой таблицей tmp не помогло бы - перестройка всех файлов данных всякий раз, когда один или два из них должны быть извлечены, будет еще медленнее)

Примечания:

  • MS SQL Server 2008 R2
  • pyodbc 4.0.28 (для подключения к базе данных)
  • python 3.7.6
  • make 4.3
  • conda 4.7.12

Спасибо.

1 Ответ

1 голос
/ 26 марта 2020

Итак, я нашел решение, которое работает очень хорошо: идея состоит в том, чтобы make rawdata мог выполнить скрипт python, который

  1. открывает соединение БД и сохраняет его открытым
  2. создает ##common_tmp_table
  3. прогонов make rawdata_, который заботится о перестройке файлов данных (так же, как make rawdata в коде, размещенном в вопросе, но теперь без select ... into ##common_tmp_table ... в запросах )
  4. закрывает соединение

В коде:

Makefile:

#THIS IS NEW
.PHONY rawdata # to always rebuild rawdata target
rawdata:
    python fetch_all_non_uptodate.py # just call a script that (among other stuff) runs `make rawdata_`

#THE REST IS AS BEFORE (just added underscore)
rawdata_: datafile1.h5, datafile2.h5 # ... more files like this
datafile1.h5: data1_specific_config.py, common_config.py
    python fetch_data1.py
datafile2.h5: data2_specific_config.py, common_config.py
    python fetch_data2.py
# ... similar rules for other files

fetch_all_non_uptodate.py:

import subprocess
import pyodbc

conn = pyodbc.connect(...) #open db connection

# simulate the run of make with the -q flag to find out whether all the datafiles are up-to-date (return code 0) or not (return code 1); nothing is re-fetched as yet
uptodate = (subprocess.run(['make', '-q', 'rawdata_']).returncode == 0)

# if the raw datafiles are not up-to-date
if not uptodate:    
    create_common_tmp_table(conn) # create the ##common_tmp_table in the db and keep it while conn is open
    conn.commit() #commit the creation of the tmp table (Important! - otherwise the other connections won't see it!)
    subprocess.run(['make', 'rawdata_']) # run make to re-fetch whatever datafiles need to be re-fetched
                                         # the queries can make use of the existing tmp table
# otherwise we just simulate the make output telling that all is up-to-date
else:
    print("make: Nothing to be done for 'rawdata'.")

conn.close()

queryN:

/*keep just the specific part - the ##common_tmp_table already exists*/
select ... from (... ##common_tmp_table ...)
...