Как я могу использовать CREATE PROCEDURE (и связанные команды) внутри скрипта, переданного pyodbc в SQL?
У меня есть чистая процедура T-SQL (названная «#CreateCsvResultSet»), которая создает результирующий набор «Формат CSV» вместе со строкой заголовка CSV.Мне НЕ разрешается использовать хранимые процедуры SQL, поэтому каждый сценарий должен содержать (и использовать) определение этой временной процедуры.
Сценарии, которые содержат и используют это временное определение процедуры, выполняются без проблем при запуске из Microsoft SSMS 2012.
После внесения простых изменений, необходимых для получения параметров из pydobc (см. Пример ниже).) сценарии копируются в собственные файлы и запускаются по мере необходимости.Этот подход работал в течение многих лет, единственной новостью является моя попытка включить и использовать процедуру #CreateCsvResultSet в примере сценария.
При вызове pyodbc этот сценарий завершается ошибкой в строке "CREATE PROCEDURE #CreateCsvResultSet"(даже когда это первая строка скрипта), возвращая эту «однострочную» ошибку (разбитую на части для ясности):
pyodbc.ProgrammingError: ('42000', '
Incorrect syntax near the keyword \'PROCEDURE\'. (156) (SQLExecDirectW);
Must declare the scalar variable "@TableName". (137);
Must declare the scalar variable "@TableName". (137);
Must declare the scalar variable "@IncludeHeaderLine". (137);
Statement(s) could not be prepared. (8180)
')
со всеми 5 строками ошибок с префиксом «[42000] [Microsoft] [Драйвер ODBC SQL Server] [SQL Server] ".
3 ошибки «Должен объявить ...» объяснимы, поскольку они являются входными параметрами для процедуры #CreateCsvResultSet.
Если SQL (вызывается через pyodbc) разрешит «СОЗДАТЬ ПРОЦЕДУРУ» и т. Д.. (по крайней мере для временных таблиц) У меня не было бы проблемы.Я что-то упускаю из виду?
Справочная информация
Среда:
- 64-битная Win 7 Enterprise SP1
- 64-битнаяPython 3.6.4 (v3.6.4: d48eceb, 19 декабря 2017, 06:54:40) [MSC v.1900 64 бит (AMD64)] с плагином pyodbc: 4.0.23
- База данных: MS SQL Server2012
- 64-битный Eclipse используется для запуска программы на Python.Это информация о версии: Eclipse Oxygen.3a Release (4.7.3a), идентификатор сборки: 20180405-1200, с использованием плагина pydev: 6.3.3.201805051638
Я знаю, как вызывать хранимые процедуры из pyodbc(вики-страница: https://github.com/mkleehammer/pyodbc/wiki/Calling-Stored-Procedures) и прочитал расследование, упомянутое на этой странице: (https://github.com/mkleehammer/pyodbc/issues/184). Для полноты, инструкции Microsoft OBDC здесь: https://docs.microsoft.com/en-us/sql/relational-databases/native-client-odbc-how-to/running-stored-procedures-call-stored-procedures
Моя ситуация немногоотличается от того, что описано в вики. В моем случае использование предложенного подхода приводит к «Catch-22».
Использование cursor.execute ("{CALL #CreateCsvResultSet (?,?,?)} ", ParameterList) в приведенном ниже коде Python не будет работать, потому что #CreateCsvResultSet НЕ будет существовать, пока сценарий не будет выполнен SQL.
И при таком подходе, похоже, нет способапередать сценарий (содержимое SQL_COMMANDS), который определяет процедуру!
Я даже пытался преобразовать основную линию переданного сценария в процедуру, а затем вызвать ее с помощью wiМетод ki - но это только вызывает еще одно возникновение моей первоначальной проблемы.
Код Python 3.X, который загружает и вызывает сценарий SQL, прост:
Database = pyodbc.connect( Driver = SQL_Driver, # = '{SQL Server}'
Server = SQL_Server, # = 'ServerId.---.ca\\ai1'
Database = SQL_Database, # = 'DatabaseName' (= SQL's "USE DatabaseName" command )
Trusted_Connection = TrustWorthyNess # = 'yes'
, APP = 'Python report generator' # suggested as good idea by https://code.google.com/archive/p/pyodbc/wikis/FAQs.wiki
, autocommit = True # see https://github.com/mkleehammer/pyodbc/wiki/Calling-Stored-Procedures
)
cursor = Database.cursor( )
ParameterList = ( 'National', '2018-05-01 00:00:00.000', 2018-05-31 23:59:59.000 )
Textfile = open( SQL_QueryName, 'r' )
SQL_COMMANDS = Textfile.read()
''' At this point the definition of #CreateCsvResultSet (kept in it's own file) can be merged into SQL_COMMANDS '''
cursor.execute( SQL_COMMANDS, ParameterList )
Процедура #CreateCsvResultSetструктурированы в соответствии с принятым ответом на этот вопрос: Использование временных функций или процедур в скрипте .(См. Также принятый ответ на эту Python - хранимую процедуру вызова Pyodbc с именем параметра и эту вики-страницу: https://code.google.com/archive/p/pyodbc/wikis/Cursor.wiki.)
Вот скелет всего сценария, переданного в SQL (внутри SQL_COMMANDSпараметр):
CREATE PROCEDURE #CreateCsvResultSet -- very first line of the script
(
@TableName as nvarchar( MAX ), -- produce a "CSV file format" version of this result set
@OrderedBy as nvarchar( MAX ) = '', -- ORDER BY clause applied to @TableName
@IncludeHeaderLine as nvarchar( 1 ) = 'Y' -- default to including a CSV column header line
)
AS
BEGIN
-- Details omitted. Note that this procedure uses dynamic sql invoked by using EXECUTE( @SqlCommandString )
-- as well as the CREATE TABLE #X, ALTER TABLE #X, DELETE FROM #X, INSERT INTO #X and SELECT INTO #X statements
RETURN
END
-------------------------------------------
--
-- Mainline of script (has worked for years, still works if above procedure and
-- the EXECUTE line below are commented out.
--
SET NOCOUNT ON -- supress the unwanted affected row counts
-- These 3 variables receive the parameters received from Python
DECLARE @target_Site nvarchar( 8 )
DECLARE @StartAtString nvarchar( 30 )
DECLARE @EndAtString nvarchar( 30 )
SET @target_Site = ? -- e.g. 'Montreal'
SET @StartAtString = ? -- e.g. '2018-05-01 00:00:00.000'
SET @EndAtString = ? -- e.g. '2018-05-31 23:59:59.000'
.... ( details of query definition omitted, note that it uses UPDATE @<a temporary table>
-- Get (a much simplified version of) the final result set; results in random order
select day_of_month,
@target_location as Site,
CONVERT( nvarchar( 19 ), @LowLimit, 120 ) as ReportPeriodStart,
CONVERT( nvarchar( 19 ), @HighLimit, 120 ) as ReportPeriodEnd
INTO #ResultSet
from @monthly_counts
-- Returns the "CSV formatted" result set in proper order but WITHOUT the CSV header line.
--SELECT * from #ResultSet -- returns the result set in correct order
--ORDER BY day_of_month ASC
-- (When invoked from within SQL server) the next line produces the same result
-- set as the above query, optionally including a CSV hdader line.
EXECUTE #CreateCsvResultSet #ResultSet, 'ORDER BY day_of_month ASC', 'Y'
Еще одно замечание:
По этому вопросу Python: Pyodbc выполняет хранимую процедуру с параметрами По умолчанию pyodbc имеет значение "autocommit = False"тогда как все, что я прочитал, говорит, что "autocommit = True" является настройкой по умолчанию в MS-SQL. Чтобы обойти эту разницу, я добавил "autocommit = True" к вызову pyodbc.connect.
Кстати, единственный очевидно связанный вопрос, предложенный stackoverflow, был SQL-запрос не выполняется при использовании pyodbc, но работает в SQL .В принятом ответе предлагалось разбить сценарии на несколько частей и выполнить каждую по отдельности.Это работало в этом случае, потому что постоянная база данных создавалась и затем использовалась.В моем случае такой подход не будет работать, потому что я должен использовать временных объектов, которые сразу становятся неопределенными после выхода из сценария SQL.