2012-11-14 45 views
2

我必須調用帶有表變量參數的MS SQLServer存儲過程。pyodbc,調用帶表變量的存儲過程

/* Declare a variable that references the type. */ 
DECLARE @TableVariable AS [AList]; 

/* Add data to the table variable. */ 
INSERT INTO @TableVariable (val) VALUES ('value-1'); 
INSERT INTO @TableVariable (val) VALUES ('value-2'); 


EXEC [dbo].[sp_MyProc] 
      @param = @TableVariable 

在SQL Sv管理工作室中工作良好。我在python中使用PyOdbc嘗試了以下內容:

cursor.execute("declare @TableVariable AS [AList]") 
for a in mylist: 
    cursor.execute("INSERT INTO @TableVariable (val) VALUES (?)", a) 
cursor.execute("{call dbo.sp_MyProc(@TableVariable)}") 

出現以下錯誤:錯誤42000:必須聲明表變量。變量不能在不同的執行步驟中存活。 我也試過:

sql = "DECLARE @TableVariable AS [AList]; " 
for a in mylist: 
    sql = sql + "INSERT INTO @TableVariable (val) VALUES ('{}'); ".format(a) 
sql = sql + "EXEC [dbo].[sp_MyProc] @param = @TableVariable" 
cursor.execute(sql) 

出現以下錯誤:沒有結果。以前的SQL不是查詢。 沒有更多的機會與

sql = sql + "{call dbo.sp_MyProc(@TableVariable)}" 

不會有人知道如何使用Pyodbc來處理呢?

回答

0

我不知道,如果這個工程,我不能測試它,因爲我沒有MS SQL服務器,但你想在一個單獨的語句執行的一切:

cursor.execute(""" 
DECLARE @TableVariable AS [AList]; 

INSERT INTO @TableVariable (val) VALUES ('value-1'); 
INSERT INTO @TableVariable (val) VALUES ('value-2'); 

EXEC [dbo].[sp_MyProc] @param = @TableVariable; 
"""); 
+0

嗯是的確,與相同的錯誤消息=未處理的編程錯誤:沒有結果。以前的SQL不是查詢 –

1

我相信這個錯誤與sql忘記table變量無關。我最近經歷了這種情況,問題是pyodbc不知道如何從存儲過程中得到結果集,如果SP也返回受影響事件的計數。

在我的情況下,解決這個問題的方法是在SP開始處簡單地放置「SET NOCOUNT ON」。

我希望這會有所幫助。

+0

這絕對是其中的一部分,但您也可以使用遊標上的nextset來逐步計數。此外,你需要意識到批次。我添加了一個解釋它的答案。 – TimothyAWiseman

1

現在你的問題的根源是一個SQL Server variable一批它被定義的範圍。每次調用cursor.execute是一個獨立的批次,即使他們是在同一個事務。

有幾種方法可以解決此問題。最直接的是重寫你的Python代碼,以便它將所有內容作爲一個批處理髮送出去。 (我在我的測試服務器上進行了測試,只要你添加set nocount或者通過nextset跨越中間結果,它就會工作。)

更爲間接的方法是重寫過程以查找temp表而不是表變量,然後創建並填充臨時表而不是表變量。沒有在存儲過程內創建的temp table具有創建它的會話範圍。

0

我有同樣的問題,但這裏沒有答案解決它。我無法使用「SET NOCOUNT ON」工作,而且我也無法使用表變量進行單個批處理操作。做了什麼工作是分兩批使用臨時表,但整天找到正確的語法。下面的代碼在第一批創建並填充一個臨時表,然後在第二個代碼中,使用數據庫名稱後面跟着兩個點執行一個存儲的proc,直到存儲的proc名稱前面。此語法對於避免錯誤非常重要,「找不到存儲過程x」(2812)(SQLExecDirectW))「。

def create_incidents(db_config, create_table, columns, tuples_list, upg_date): 
    """Executes trackerdb-dev mssql stored proc. 
    Args: 
     config (dict): config .ini file with mssqldb conn. 
     create_table (string): temporary table definition to be inserted into 'CREATE TABLE #TempTable()' 
     columns (tuple): columns of the table table into which values will be inserted. 
     tuples_list (list): list of tuples where each describes a row of data to insert into the table. 
     upg_date (string): date on which the items in the list will be upgraded. 
    Returns: 
     None 
    """ 

    sql_create = """IF OBJECT_ID('tempdb..#TempTable') IS NOT NULL 
      DROP TABLE #TempTable; 
     CREATE TABLE #TempTable ({}); 
     INSERT INTO #TempTable ({}) VALUES {}; 
     """ 
    columns = '"{}"'.format('", "'.join(item for item in columns)) 
    # this "params" variable is an egregious offense against security professionals everywhere. Replace it with parameterized queries asap. 
    params = ', '.join([str(tupl) for tupl in tuples_list]) 
    sql_create = sql_create.format(
     create_table 
     , columns 
     , params) 
    msconn.autocommit = True 
    cur = msconn.cursor() 
    try: 
     cur.execute(sql_create) 
     cur.execute("DatabaseName..TempTable_StoredProcedure ?", upg_date) 
    except pyodbc.DatabaseError as err: 
     print(err) 
    else: 
     cur.close() 
    return 

create_table = """ 
    int_column int 
    , name varchar(255) 
    , datacenter varchar(25) 
    """ 

create_incidents(
    db_config = db_config 
, create_table = create_table 
, columns  = ('int_column', 'name', 'datacenter') 
, cloud_list = tuples_list 
, upg_date  = '2017-09-08') 

存儲過程使用IF OBJECT_ID('tempdb..#TempTable') IS NULL語法來驗證臨時表是否已創建。如果有,程序將從中選擇數據並繼續。如果臨時表尚未創建,則proc將中止。這會強制存儲的proc使用在存儲過程本身之外創建的#TempTable副本,但在同一個會話中。 pyodbc會話會持續到遊標或連接關閉,並且由pyodbc創建的臨時表具有整個會話的範圍。

IF OBJECT_ID('tempdb..#TempTable') IS NULL 
BEGIN 
    -- #TempTable gets created here only because SQL Server Management Studio throws errors if it isn't. 
    CREATE TABLE #TempTable (
     int_column int 
     , name varchar(255) 
     , datacenter varchar(25) 
    ); 

    -- This error is thrown so that the stored procedure requires a temporary table created *outside* the stored proc 
    THROW 50000, '#TempTable table not found in tempdb', 1; 
END 
ELSE 
BEGIN 
    -- the stored procedure has now validated that the temporary table being used is coming from outside the stored procedure 
    SELECT * FROM #TempTable; 
END; 

最後,請注意,「tempdb」不是佔位符,就像我第一次看到它時那樣。 「tempdb」是一個實際的MS SQL Server數據庫系統對象。