Я пытаюсь вставить данные в таблицу базы данных MSSQL, выполняя хранимую процедуру с помощью скрипта Python.
Вот моя хранимая процедура -
SET ANSI_NULLS ON GO
SET QUOTED_IDENTIFIER ON GO
CREATE PROCEDURE [dbo].[Sp_Test]
(
@ProcessId INT
,@CreatedByUserName VARCHAR(12)
,@CreatedByMachineName VARCHAR(12)
--,@CreatedTime DATETIME
,@NumberOfDataRows INT
,@DataSource VARCHAR(300)
--,@RefId VARCHAR(8)
,@Postpone DATETIME2
,@Deadline DATETIME2
,@NewBatchId INT OUT
)
AS
BEGIN
INSERT INTO Test(ProcessId, CreatedByUserName,
CreatedByMachineName, CreatedTime, Postpone, Deadline,
NumberOfDataRows, DataSource, RefId)
VALUES(
@ProcessId,
@CreatedByUserName,
@CreatedByMachineName,
GETDATE(),
NULL,
NULL,
@NumberOfDataRows,
@DataSource,
CONVERT(VARCHAR(9),
CRYPT_GEN_RANDOM(4), 2)
)
SET @NewBatchId = SCOPE_IDENTITY();
RETURN 0;
END
GO
Вот мой фрагмент кода Python.
with engine.connect() as conn:
conn.execute(text("CALL Sp_Test(:ProcessId, :CreatedByUserName, :CreatedByMachineName, :NumberOfDataRows, :DataSource)"), ProcessId=process_code, CreatedByUserName=username,CreatedByMachineName=hostname,NumberOfDataRows=row_count,DataSource='Shared')
Но когда я пытаюсь запустить свой скрипт, он выдает следующую ошибку.
Ошибка типа: Connection.execute() получил неожиданный аргумент ключевого слова «ProcessId».
Я также пытался передать значения как словарь. Но ошибка та же.
params = {'ProcessId': process_code,'CreatedByUserName': username,'CreatedByMachineName': hostname,'NumberOfDataRows': row_count,'DataSource': 'Shared'}
with engine.connect() as conn:
conn.execute(text("CALL Sp_Test(:ProcessId, :CreatedByUserName, :CreatedByMachineName, :NumberOfDataRows, :DataSource)"),**params)
Благодарю, если кто-нибудь может помочь мне разобраться в этом?
Возможно, лучше попробовать вызвать процедуру следующим образом docs.sqlalchemy.org/en/20/core/… - но я не использую SQL-Server, поэтому, боюсь, не могу предоставить никаких подробных предложений. .
Для pyodbc вам нужно будет использовать анонимный блок кода, как описано здесь.
Также обратите внимание, что T-SQL (диалект MS SQL Server) не использует CALL
, он использует EXECUTE
(часто сокращается до EXEC
).
Вы заметили, что ни один из параметров хранимой процедуры не имеет значений по умолчанию? Это означает, что все они должны быть указаны при вызове хранимой процедуры. то есть: если вы попытаетесь EXEC dbo.sp_Test @ProcessId=1,@CreatedByUserName='username',@CreatedByMachineName='machinename',@NumberOfDataRows=1,@DataSource='shared';
из SSMS или DBeaver, вы получите сообщение об ошибке. Процедура или функция «Sp_Test» ожидает параметр «@Postpone», который не был предоставлен.
Как упоминалось в комментарии к вопросу, документация pyodbc для вызова хранимых процедур, которые возвращают ВЫХОДНЫЕ переменные и/или ВОЗВРАЩАЕМЫЕ значения, находится здесь. Однако в вашем случае вам не нужно прибегать к необработанному курсору DBAPI, поскольку ваш SP не возвращает наборы результатов, поэтому вам не нужно вызывать метод .nextset()
pyodbc. Вы можете использовать SQLAlchemy напрямую, как в следующем упрощенном примере.
Данный
CREATE TABLE [dbo].[Test](
[BatchId] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[ProcessId] [int] NOT NULL
)
CREATE PROCEDURE [dbo].[my_test_sp]
@ProcessId int, @NewBatchId int OUT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO Test (ProcessId) VALUES (@ProcessId);
SET @NewBatchId = SCOPE_IDENTITY();
RETURN 0;
END
Мы сможем
from sqlalchemy import create_engine, text
engine = create_engine("mssql+pyodbc://scott:tiger^5HHH@mssql_199")
with engine.begin() as conn:
sql = """\
SET NOCOUNT ON;
DECLARE @new_id int, @rtn int;
EXEC @rtn = dbo.my_test_sp @ProcessId = :process_id, @NewBatchId = @new_id OUTPUT;
SELECT @new_id AS new_batch_id, @rtn AS return_value;
"""
result = conn.execute(text(sql), dict(process_id=123)).mappings().one()
print(result) # {'new_batch_id': 2, 'return_value': 0}
Спасибо. Благодаря этому мой код запустился без ошибок, и похоже, что процедура вызывается, но данные не отображаются в таблице. Тем не менее, я получаю правильный результат NewBatchId
для каждой вставки. (тот же запрос работает нормально при прямом вызове). Этот ответ работает для меня, но тогда я не могу так элегантно использовать переменные параметры. Есть идеи, что я делаю неправильно?
Вы можете установить engine.echo = True
непосредственно перед блоком with
и проверить вывод журнала, чтобы увидеть, происходит ли COMMIT.
Спасибо за engine.echo = True
. Каким-то образом тот же код начал работать сегодня.
@snakecharmerb я удалил **. Но теперь я получаю ошибку программирования pyodbc как 'sqlalchemy.exc.ProgrammingError: (pyodbc.ProgrammingError) ('42000', "[42000] [Microsoft][ODBC Driver 17 для SQL Server][SQL Server]Неправильный синтаксис рядом с '@P1 '. (102) (SQLExecDirectW); [42000] [Microsoft][Драйвер ODBC 17 для SQL Server][SQL Server]Не удалось подготовить операторы (8180)")'.