sql >> データベース >  >> RDS >> Access

DAOでのMicrosoftSQLストアドプロシージャの大きなパラメータの使用

    DAOでのMicrosoftSQLストアドプロシージャの大きなパラメータの使用

    多くの方がすでにご存知のように、SQL Serverチームは、SQL Serverデータベースエンジン用のOLEDBの非推奨を発表しました(読み取り:ADOはOLEDBを使用しているため、ADOは使用できません)。さらに、SQL Azureは正式にはADOをサポートしていませんが、SQLServerネイティブクライアントを使用してそれを回避することはできます。ただし、新しい13.1 ODBCドライバーには、SQL Server Native Clientでは利用できない多くの機能が付属しており、さらに多くの機能が追加される可能性があります。

    結論:純粋なDAOを使用する必要があります。 Access/ODBCまたはAccess/SQL Serverのテーマに触れている複数のユーザー音声アイテムがすでにあります…例:

    データコネクタSQLServer
    SQLServerとのより良い統合
    SQLAzureとのより良い統合
    サーバーデータベースで一般的に使用されるより多くのデータタイプをAccessが処理できるようにしてください
    Accessをより良くするODBCクライアント

    (access.uservoice.comに投票またはアクセスしたことがない場合は、そこにアクセスして、Accessチームにお気に入りの機能を実装してもらいたい場合は投票してください)

    ただし、Microsoftが次のバージョンでDAOを拡張したとしても、お客様の既存のアプリケーションに対処する必要があります。 ODBC over OLEDBプロバイダー(MSDASQL)の使用を検討しましたが、それは死にかけている馬にポニーをまたぐことに似ていると感じました。うまくいくかもしれませんが、すぐに死んでしまうかもしれません。

    ほとんどの場合、パススルークエリは必要な処理を実行し、DAOパススルークエリを使用してADOの機能を模倣する関数を簡単にまとめることができます。ただし、簡単に修正できない重要なギャップが1つあります。それは、ストアドプロシージャの大きなパラメータです。以前に書いたように、大量のデータを渡す方法としてXMLパラメータを使用することがあります。これは、Accessが実際にすべてのデータを1つずつ挿入するよりもはるかに高速です。ただし、DAOクエリはSQLコマンドで約64K文字に制限されており、実際にはさらに少なくすることができます。 64K文字を超える可能性のあるパラメーターを渡す方法が必要だったため、回避策を検討する必要がありました。

    tblExecuteStoredProcedureテーブルに入る

    新しいODBCドライバーまたはSQLServerNative Clientを使用する場合、DAOはテーブルに直接挿入することで大量のテキスト(別名メモ)を簡単に処理できるため、選択したアプローチはテーブルを使用することでした。したがって、大きなXMLパラメータを実行するには、実行するプロシージャとそのパラメータをテーブルに記述し、トリガーにそれを取得させます。テーブル作成スクリプトは次のとおりです。

    CREATE TABLE dbo.tblExecuteStoredProcedure (
    ExecuteID int NOT NULL IDENTITY
    CONSTRAINT PK_tblExecuteStoredProcedure PRIMARY KEY CLUSTERED,
    ProcedureSchema sysname NOT NULL
    CONSTRAINT DF_tblExecuteStoredProcedure DEFAULT 'dbo',
    ProcedureName sysname NOT NULL,
    Parameter1 nvarchar(MAX) NULL,
    Parameter2 nvarchar(MAX) NULL,
    Parameter3 nvarchar(MAX) NULL,
    Parameter4 nvarchar(MAX) NULL,
    Parameter5 nvarchar(MAX) NULL,
    Parameter6 nvarchar(MAX) NULL,
    Parameter7 nvarchar(MAX) NULL,
    Parameter8 nvarchar(MAX) NULL,
    Parameter9 nvarchar(MAX) NULL,
    Parameter10 nvarchar(MAX) NULL,
    RV rowversion NOT NULL
    );

    もちろん、これを実際のテーブルのように使用するつもりはありません。また、ストアドプロシージャにはさらに多くのパラメータを含めることができますが、10個のパラメータを任意に設定します。ただし、私たちの経験では、特にXMLパラメータを処理している場合、10をはるかに超えることは非常にまれです。それ自体では、テーブルはあまり役に立ちません。トリガーが必要です:

    CREATE TRIGGER dbo.tblExecuteStoredProcedureAfterInsert
    ON dbo.tblExecuteStoredProcedure AFTER INSERT AS
    BEGIN
    --Throw if multiple inserts were performed
    IF 1 < (
    SELECT COUNT(*)
    FROM inserted
    )
    BEGIN
    ROLLBACK TRANSACTION;
    THROW 50000, N'Cannot perform multiple-row inserts on the table `tblExecuteStoredProcedure`.', 1;
    RETURN;
    END;

    –最後に挿入する必要がある単一のレコードのみを処理します
    DECLARE @ProcedureSchema sysname、
    @ProcedureName sysname、
    @FullyQualifiedProcedureName nvarchar(MAX)、
    @ Parameter1 nvarchar(MAX)、
    @ Parameter2 nvarchar(MAX)、
    @ Parameter3 nvarchar(MAX)、
    @ Parameter4 nvarchar(MAX)、
    @ Parameter5 nvarchar(MAX)、
    @ Parameter6 nvarchar(MAX)、
    @ Parameter7 nvarchar(MAX)、
    @ Parameter8 nvarchar(MAX)、
    @ Parameter9 nvarchar(MAX)、
    @ Parameter10 nvarchar(MAX)、
    @Params nvarchar(MAX)、
    @ParamCount int、
    @ParamList nvarchar(MAX)、
    @Sql nvarchar(MAX);

    SELECT
    @ProcedureSchema =p.ProcedureSchema、
    @ProcedureName =p.ProcedureName、
    @FullyQualifiedProcedureName =CONCAT(QUOTENAME(p.ProcedureSchema)、N'。'、QUOTENAME(p.ProcedureName) )、
    @ Parameter1 =p.Parameter1、
    @ Parameter2 =p.Parameter2
    FROM挿入ASp
    WHERE p.RV =(
    SELECT MAX(x。 RV)
    FROM挿入ASx
    );

    SET @Params =STUFF((
    SELECT
    CONCAT(
    N'、'、
    p.name、
    N'='、
    p。 name

    FROM sys.parameters AS p
    INNER JOIN sys.types AS t
    ON p.user_type_id =t.user_type_id
    WHERE p.object_id =OBJECT_ID( @FullyQualifiedProcedureName)
    FOR XML PATH(N”)
    )、1、1、N”);

    SET @ParamList =STUFF((
    SELECT
    CONCAT(
    N'、'、
    p.name、
    N''、
    t.name 、
    CASE
    WHEN t.name LIKE N'%char%' OR t.name LIKE'%binary%'
    THEN CONCAT(N'('、IIF(p.max_length =- 1、N'MAX'、CAST(p.max_length AS nvarchar(11)))、N')')
    WHEN t.name ='decimal'OR t.name ='numeric'
    THEN CONCAT(N'('、p.precision、N'、'、p.scale、N')')
    ELSE N”
    END

    FROM sys.parameters AS p
    INNER JOIN sys.types AS t
    ON p.user_type_id =t.user_type_id
    WHERE p.object_id =OBJECT_ID(@FullyQualifiedProcedureName)
    FOR XML PATH(N ")
    )、1、1、N”);

    SET @ParamCount =(
    SELECT COUNT(*)
    FROM sys.parameters AS p
    WHERE p.object_id =OBJECT_ID(@FullyQualifiedProcedureName)
    );

    SET @ParamList + =((
    SELECT
    CONCAT(N'、'、p.ParameterName、N'nvarchar(1)')
    FROM(VALUES
    (1、N '@ Parameter1')、
    (2、N'@ Parameter2')、
    (3、N'@ Parameter3')、
    (4、N'@ Parameter4')、
    (5、N'@ Parameter5')、
    (6、N'@ Parameter6')、
    (7、N'@ Parameter7')、
    (8、N'@ Parameter8')、
    (9、N' @ Parameter9')、
    (10、N' @ Parameter10')
    )AS p(ParameterID、ParameterName)
    WHEREp。 ParameterID> @ParamCount
    FOR XML PATH(N”)
    ));

    SET @Sql =CONCAT(N’EXEC‘、@FullyQualifiedProcedureName、N’‘、@Params、N’;’);

    –トリガーから結果セットが返されないようにする(非推奨)
    –ストアドプロシージャがトリガーを返す場合、トリガーはエラーで終了します
    EXECUTE sys.sp_executesql @ Sql、@ ParamList、@ Parameter1、@ Parameter2、@ Parameter3、@ Parameter4、@ Parameter5、@ Parameter6、@ Parameter7、@ Parameter8、@ Parameter9、@ Parameter10
    結果セットなし;

    DELETE FROM dbo.tblExecuteStoredProcedure
    WHERE EXISTS(
    SELECT NULL
    FROM insert
    WHERE insert.ExecuteID =tblExecuteStoredProcedure.ExecuteID
    );
    END;

    かなり一口、そのトリガー。基本的には、1回の挿入で、テーブルtblExecuteStoredProcedureで定義されているnvarchar(MAX)から、ストアドプロシージャで必要な実際の型にパラメータを変換する方法を理解します。暗黙的な変換が使用され、sys.sp_executesqlにラップされているため、パラメータ値自体が有効である限り、さまざまなデータ型で適切に機能します。ストアドプロシージャが結果セットを返さないようにする必要があることに注意してください。 Microsoftは、トリガーが結果セットを返すことを許可していますが、前述のように、これは非標準であり、非推奨になっています。したがって、SQL Serverの将来のバージョンでの問題を回避するために、その可能性をブロックします。最後に、テーブルをクリアするので、常に空になります。結局のところ、私たちはテーブルを悪用しています。データは保存していません。

    AccessとSQLServerの間のラウンドトリップの数を減らすため、トリガーを使用することにしました。ストアドプロシージャを使用してトリガーの本体からT-SQLを処理した場合、テーブルに挿入した後にT-SQLを呼び出す必要があり、2人のユーザーが同時に挿入するなどの潜在的な副作用にも対処する必要があります。レコードを残すエラーなど。

    OK、しかし「テーブル」とそのトリガーをどのように使用するのでしょうか?ここで、全体の配置を設定するために少しのVBAコードが必要になります…

    Public Sub ExecuteWithLargeParameters( _
    ProcedureSchema As String, _
    ProcedureName As String, _
    ParamArray Parameters() _
    )
    Dim db As DAO.Database
    Dim rs As DAO.Recordset

    Dim i As Long
    Dim l As Long
    Dim u As Long

    Set db =CurrentDb
    Set rs =db.OpenRecordset(“ SELECT * FROM tblExecuteStoredProcedure;”、dbOpenDynaset、dbAppendOnly Or dbSeeChanges)

    rs.AddNew
    rs.Fields( "ProcedureSchema")。Value =ProcedureSchema
    rs.Fields( "ProcedureName")。Value =ProcedureName

    l =LBound(Parameters)
    u =UBound(Parameters)
    For i =l To u
    rs.Fields( "Parameter"&i).Value =Parameters(i)
    次へ

    rs.Update
    End Sub

    ストアドプロシージャに実際に必要な数のパラメータを指定できるParamArrayを使用していることに注意してください。夢中になり、さらに20個のパラメーターが必要な場合は、テーブルにフィールドを追加してトリガーを更新するだけで、VBAコードは引き続き機能します。あなたはこのようなことをすることができるでしょう:

    ExecuteWithLargeParameters "dbo", "uspMyStoredProcedure", dteStartDate, dteEndDate, strSomeBigXMLDocument

    うまくいけば、回避策は長い間必要ありません(特に、Access UserVoiceにアクセスして、Access + SQL / ODBCに関連するさまざまな項目に賛成する場合)が、私たちがいる状況で自分自身を見つけた場合に役立つことを願っていますまた、このソリューションまたはより良いアプローチのためにあなたが持っているかもしれない改善について聞いてみたいです!


    1. docker-composeを使用してpostgresqlデータベースにテーブルを作成する

    2. Oracleスキーマ(スクリプト可能)のDDL全体を生成するにはどうすればよいですか?

    3. SQLServerデータベースのすべてのデフォルト制約を削除する方法-SQLServer/TSQLチュートリアルパート94

    4. Oracle用のマクロメディアドライバでtnsエントリを使用する方法