Использование хранимых процедур SQL Server из Python (pyodbc)

#sql-server

Вопрос:

У меня есть хранимая процедура, код:

 DECLARE @RC int 
DECLARE @id varchar(13) 
DECLARE @pw varchar(13) 
DECLARE @depart varchar(32) 
DECLARE @class varchar(12) 
DECLARE @name varchar(12) 
DECLARE @birthday varchar(10) 
DECLARE @grade int 
DECLARE @subgrade int 
SELECT @id = 'test' 
SELECT @pw = '12345' 
SELECT @depart = 'none' 
SELECT @class = 'GM' 
SELECT @name = 'name' 
SELECT @birthday = 'None' 
SELECT @grade = 3 
SELECT @subgrade = 2 
EXEC @RC = [my_database].[dbo].[my_table] @id, @pw, @depart, @class, @name, @birthday, @grade, @subgrade 
DECLARE @PrnLine nvarchar(4000) 
PRINT 'Stored Procedure: my_database.dbo.my_table' 
SELECT @PrnLine = ' Return Code = '   CONVERT(nvarchar, @RC)
 

Как я могу сделать необработанный sql-запрос для создания учетной записи с помощью этой процедуры?
Я использую колбу и pyodbc.

Ответ №1:

Из документации pyodbc

Чтобы вызвать хранимую процедуру прямо сейчас, передайте вызов методу execute, используя либо формат, который распознает ваша база данных, либо формат escape-вызова ODBC. (Затем драйвер ODBC переформатирует вызов для вас в соответствии с данной базой данных.)

Для SQL Server вы бы использовали что — то вроде этого:

 # SQL Server format
cursor.execute("exec sp_dosomething(123, 'abc')")

# ODBC format
cursor.execute("{call sp_dosomething(123, 'abc')}")
 

Итак, назовем вашу процедуру

 id_ = 'test' 
pw = '12345' 
depart = 'none' 
class_ = 'GM' 
name = 'name' 
birthday = 'None' 
grade = 3 
subgrade = 2 

sql = 'exec [my_database].[dbo].[my_table](?, ?, ?, ?, ?, ?, ?, ?)'
values = (id_, pw, depart, class_, name, birthday, grade, subgrade)

cursor.execute(sql, (values))
 

Комментарии:

1. здесь проблема: ошибка программирования sqlalchemy.exc.: (Ошибка программирования) (‘42000’, «[42000] [Microsoft][Драйвер ODBC SQL Server][SQL Server]Неправильный синтаксис рядом с ‘@P1’. (102) (SQLExecDirectW)») ‘exe my_procedure(?, ?, ?)’ (‘5215125’, ‘125151’, ‘31231’)

2. Это похоже на синтаксическую ошибку в хранимой процедуре. Является @P1 ли частью настоящего?

3. Спасибо, sql = ‘exec [my_database]. [dbo]. [my_table] ?, ?, ?, ?, ?, ?, ?, ?’ работает нормально(без скобок).

4. Без скобок работает, если в именах нет пробелов, если у вас есть пробелы, вам нужны скобки…..

5. Всем привет, пожалуйста, как получить возвращаемое значение если моя хранимая процедура имеет идентификатор SELECT IDENT_CURRENT («ТАБЛИЦА») в качестве идентификатора, я хочу получить идентификатор.

Ответ №2:

Принятый ответ не решает проблему получения возвращаемого значения из хранимой процедуры, что можно сделать следующим образом:

 id_ = 'test' 
pw = '12345' 
depart = 'none' 
class_ = 'GM' 
name = 'name' 
birthday = 'None' 
grade = 3 
subgrade = 2 

sql = """
SET NOCOUNT ON;
DECLARE @RC int;
EXEC @RC = [my_database].[dbo].[my_sp] ?, ?, ?, ?, ?, ?, ?, ?;
SELECT @RC AS rc;
"""
values = (id_, pw, depart, class_, name, birthday, grade, subgrade)
cursor.execute(sql, values)
rc = cursor.fetchval()  # pyodbc convenience method similar to cursor.fetchone()[0]
 

Ответ №3:

Не забудьте включить NOCOUNT в вашей хранимой процедуре.

Комментарии:

1. Это действительно помогло нам справиться с проблемой, когда SP не работает. И все же я не понимаю, как это может быть фактором

Ответ №4:

Другой вариант ответа Горда заключается в использовании выходных и именованных параметров (которые должны быть определены в хранимой процедуре) для ясности.

 id_ = 'test' 
pw = '12345' 
depart = 'none' 
class_ = 'GM' 
name = 'name' 
birthday = 'None' 
grade = 3 
subgrade = 2 

sql = """
DECLARE @RC int;
EXEC [my_database].[dbo].[my_sp] @RC OUTPUT, @id_=?, @pw=?, @depart=?, @class_=?, @name=?, @birthday=?, @grade=?, @subgrade=?;
SELECT @RC AS rc;
"""
values = (id_, pw, depart, class_, name, birthday, grade, subgrade)
cursor.execute(sql, values)
rc = cursor.fetchval()
 

Ответ №5:

С помощью курсора, инициализированного вашим подключением, sp можно вызвать напрямую следующим образом

 sql = " exec your_SP @codemp = ?, @fecha = ? "
prm = (dict['param1'], dict['param2'])
cursor.execute(qry, params)
 

Ответ №6:

После того, как я повсюду искал это решение, я не смог найти упрощенную версию. Все результаты, по-видимому, чрезмерно усложняют это, что должно быть так легко сделать. Вот мое решение.

  import pyodbc
 import pandas as pd
 import datetime as d


  conn = pyodbc.connect('Driver=;'
                  'Server=;'
                  'Database=;'
                  'UID=;'
                  'PWD=;')


     # define parameters to be passed in and out

     quarter_date = d.date(year=2020, month=10, day=1)

     SQL = r'exec TERRITORIES_SP @quarterStart = '   "'"   str(quarter_date)   "'"

     print(SQL)

     try:
         cursor = conn.cursor()
          cursor.execute(SQL)
          cursor.close()
          conn.commit()
    finally:
          conn.close()
 

Комментарии:

1. Используя динамический SQL, вы оставляете себя открытым для уязвимостей SQL — инъекций.

2. Спасибо. Каков был бы лучший подход?

3. Что-то вроде sql = r'exec TERRITORIES_SP @quarterStart = ?' последовало за cursor.execute(sql, str(quarter_date)) этим . Я признаю, что в этом случае риск внедрения SQL невелик, но все равно было бы дурным тоном продвигать динамический SQL как «упрощенное» решение.

Ответ №7:

Для MSSQL правильным форматом является следующий:

 SQL = 'exec sp_UpdateUserGoogleAuthenticated ''?'', ''?'''
 

Попробуйте запустить хранимую процедуру в MSSQL в окне SQL-запроса, и она будет завершаться ошибкой каждый раз, когда () окружает ? Метки. Если вы уберете одинарные кавычки, это позволит использовать переменные с пробелами в них.