4

テーブル変数パラメーターを使用して MS SQLServer ストアド プロシージャを呼び出す必要があります。

/* 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 管理スタジオでうまく機能します。PyOdbcを使用してpythonで次のことを試しました:

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 を使用してこれを処理する方法を知っていますか?

4

6 に答える 6

5

問題の根本は、SQL Server変数が定義されたバッチのスコープを持っていることです。cursor.execute への各呼び出しは、同じトランザクション内にある場合でも、個別のバッチです。

これを回避するには、いくつかの方法があります。最も直接的な方法は、すべてを 1 つのバッチとして送信するように Python コードを書き直すことです。(テストサーバーでこれをテストしましたが、 set nocount on を追加するか、 nextset で中間結果をステップオーバーする限り、機能するはずです。)

より間接的な方法は、テーブル変数の代わりに一時テーブルを検索するように手順を書き直してから、テーブル変数の代わりに一時テーブルを作成して入力することです。ストアド プロシージャ内で作成されていない一時テーブルには、それが作成されたセッションのスコープがあります。

于 2012-12-20T17:36:40.840 に答える
4

このエラーは、sql がテーブル変数を忘れたこととは何の関係もないと思います。私は最近これを経験しましたが、問題は、SPが影響を受けるもののカウントも返す場合、pyodbcがストアドプロシージャから結果セットを取得する方法を知らないことでした。

私の場合、これを修正するには、SP の先頭に「SET NOCOUNT ON」を配置するだけでした。

これが役立つことを願っています。

于 2012-12-20T17:07:38.927 に答える
1

私はこれと同じ問題を抱えていましたが、ここでの答えはどれもそれを修正しませんでした。「SET NOCOUNT ON」を機能させることができませんでした。また、テーブル変数を操作する単一のバッチ操作を行うこともできませんでした。うまくいったのは、一時テーブルを 2 つのバッチで使用することでしたが、正しい構文を見つけるのに一日中かかりました。次のコードは、最初のバッチで一時テーブルを作成してデータを設定し、次に 2 番目のバッチで、ストアド プロシージャ名の前に 2 つのドットを付けたデータベース名を使用してストアド プロシージャを実行します。この構文は、「ストアド プロシージャ '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 は中止されます。これにより、ストアド プロシージャは、ストアド プロシージャ自体の外部で同じセッション内に作成された #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 データベース システム オブジェクトです。

于 2017-09-09T04:06:28.407 に答える
1

これが機能するかどうかはわかりませんが、MS SQL Server がないためテストできませんが、単一のステートメントですべてを実行しようとしましたか:

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;
""");
于 2012-11-14T11:46:54.197 に答える