Menggunakan Parameter Besar untuk Prosedur tersimpan Microsoft SQL dengan DAO
Seperti yang sudah Anda ketahui, tim SQL Server telah mengumumkan penghentian OLEDB untuk mesin database SQL Server (Baca:kami tidak dapat menggunakan ADO karena ADO menggunakan OLEDB). Selain itu, SQL Azure tidak secara resmi mendukung ADO, meskipun seseorang masih dapat menggunakannya menggunakan SQL Server Native Client. Namun, driver 13.1 ODBC baru hadir dengan sejumlah fitur yang tidak akan tersedia di SQL Server Native Client, dan mungkin akan ada lebih banyak lagi yang akan datang.
Intinya:kita perlu bekerja dengan DAO murni. Sudah ada beberapa item suara pengguna yang menyentuh subjek Access / ODBC atau Access / SQL Server… misalnya:
Konektor data SQL Server
Integrasi yang lebih baik dengan SQL Server
Integrasi yang lebih baik dengan SQL Azure
Harap buat Access dapat menangani lebih banyak tipe data seperti yang biasa digunakan di database Server
Jadikan Access menjadi lebih baik Klien ODBC
(Jika Anda belum memilih atau mengunjungi access.uservoice.com, pergi ke sana dan pilih jika Anda ingin tim Access menerapkan fitur favorit Anda)
Tetapi bahkan jika Microsoft meningkatkan DAO di versi berikutnya, kami masih harus berurusan dengan aplikasi pelanggan kami yang sudah ada. Kami mempertimbangkan untuk menggunakan ODBC melalui penyedia OLEDB (MSDASQL) tetapi kami merasa bahwa itu mirip dengan mengangkangi kuda poni di atas kuda yang sekarat. Ini mungkin berhasil, tetapi mungkin akan mati sebentar lagi.
Untuk sebagian besar, kueri passthrough akan melakukan apa yang perlu kita lakukan dan mudah untuk menggabungkan fungsi untuk meniru fungsionalitas ADO menggunakan kueri pass-through DAO. Tetapi ada satu celah signifikan yang tidak mudah diperbaiki — parameter besar untuk prosedur tersimpan. Seperti yang saya tulis sebelumnya, kami terkadang menggunakan parameter XML sebagai cara untuk melewatkan sejumlah besar data, yang jauh lebih cepat daripada Access benar-benar memasukkan semua data satu per satu. Namun, kueri DAO terbatas pada sekitar 64 ribu karakter untuk perintah SQL dan dalam praktiknya bisa lebih sedikit. Kami membutuhkan cara untuk meneruskan parameter yang bisa lebih besar dari 64 ribu karakter, jadi kami harus memikirkan solusinya.
Masukkan tabel tblExecuteStoredProcedure
Pendekatan yang kami pilih adalah menggunakan tabel karena ketika kami menggunakan driver ODBC yang lebih baru atau SQL Server Native Client, DAO dengan mudah dapat menangani sejumlah besar teks (alias Memo) dengan memasukkan langsung ke dalam tabel. Oleh karena itu, untuk mengeksekusi parameter XML besar, kami akan menulis prosedur untuk mengeksekusi dan parameternya ke tabel, lalu biarkan pemicu mengambilnya. Berikut script pembuatan tabel:
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
);
Tentu saja, kami sebenarnya tidak bermaksud menggunakan ini seperti tabel nyata. Kami juga secara sewenang-wenang menetapkan 10 parameter meskipun prosedur tersimpan dapat memiliki lebih banyak lagi. Namun, dalam pengalaman kami, sangat jarang memiliki lebih dari 10, terutama ketika kami berurusan dengan parameter XML. Dengan sendirinya, tabel tidak akan terlalu berguna. Kami membutuhkan pemicu:
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;
–Proses hanya satu record yang harus disisipkan terakhir
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 dimasukkan AS p
WHERE p.RV =(
SELECT MAX(x. RV)
DARI dimasukkan AS x
);
SET @Params =STUFF((
PILIH
CONCAT(
N',',
p.name,
N' =',
p. nama
)
DARI sys.parameters AS p
INNER JOIN sys.types AS t
PADA p.user_type_id =t.user_type_id
WHERE p.object_id =OBJECT_ID( @FullyQualifiedProcedureName)
UNTUK XML PATH(N")
), 1, 1, N”);
SET @ParamList =STUFF((
PILIH
CONCAT(
N',',
p.name,
N' ',
t.name ,
KASUS
KETIKA t.name LIKE N'%char%' ATAU 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' ATAU t.name ='numeric'
THEN CONCAT(N'(', p.presisi, 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)
UNTUK 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)
WHERE p. ParameterID> @ParamCount
UNTUK XML PATH(N")
));
SET @Sql =CONCAT(N'EXEC ', @FullyQualifiedProcedureName, N' ', @Params, N';');
–Mencegah set hasil apa pun dikembalikan dari pemicu (yang sudah tidak digunakan lagi)
–Jika prosedur tersimpan mengembalikan apa pun, pemicu akan berakhir dengan kesalahan
EXECUTE sys.sp_executesql @Sql, @ParamList, @ Parameter1, @Parameter2, @Parameter3, @Parameter4, @Parameter5, @Parameter6, @Parameter7, @Parameter8, @Parameter9, @Parameter10
WITH RESULT SETS NONE;
HAPUS DARI dbo.tblExecuteStoredProcedure
WHERE EXISTS (
SELECT NULL
FROM insert
WHERE dimasukkan.ExecuteID =tblExecuteStoredProcedure.ExecuteID
);
END;
Sebuah cukup suap, pemicu itu. Pada dasarnya dibutuhkan satu sisipan, kemudian mencari cara untuk mengubah parameter dari nvarchar(MAX) mereka seperti yang didefinisikan pada tabel tblExecuteStoredProcedure ke tipe aktual yang diperlukan oleh prosedur tersimpan. Konversi implisit digunakan, dan karena dibungkus dengan sys.sp_executesql berfungsi dengan baik untuk berbagai tipe data selama nilai parameter itu sendiri valid. Perhatikan bahwa kami mengharuskan prosedur tersimpan TIDAK mengembalikan set hasil apa pun. Microsoft memang mengizinkan pemicu untuk mengembalikan kumpulan hasil tetapi seperti yang dicatat, ini tidak standar dan sudah tidak digunakan lagi. Jadi untuk menghindari masalah dengan versi SQL Server yang akan datang, kami memblokir kemungkinan itu. Akhirnya, kami mengosongkan meja, jadi selalu kosong. Lagi pula, kami menyalahgunakan meja; kami tidak menyimpan data apa pun.
Saya memilih untuk menggunakan pemicu karena memotong jumlah perjalanan pulang pergi antara Access dan SQL Server. Seandainya saya menggunakan prosedur tersimpan untuk memproses T-SQL dari badan pemicu, itu berarti saya harus memanggilnya setelah saya memasukkan ke dalam tabel dan juga menangani potensi efek samping seperti dua pengguna memasukkan pada waktu yang sama atau kesalahan meninggalkan catatan dan sebagainya.
Oke, tapi bagaimana kita menggunakan "tabel" dan pemicunya? Di situlah kita membutuhkan sedikit kode VBA untuk mengatur seluruh pengaturan…
Public Sub ExecuteWithLargeParameters( _
ProcedureSchema As String, _
ProcedureName As String, _
ParamArray Parameters() _
)
Dim db As DAO.Database
Dim rs As DAO.Recordset
Redupkan i Selama
Redupkan Selama
Redupkan Selama
Atur db =CurrentDb
Atur rs =db.OpenRecordset(“SELECT * FROM tblExecuteStoredProcedure;”, dbOpenDynaset, dbAppendOnly Atau dbSeeChanges)
rs.AddNew
rs.Fields(“ProcedureSchema”).Value =ProcedureSchema
rs.Fields(“ProcedureName”).Value =ProcedureName
l =LBound(Parameter)
u =UBound(Parameters)
Untuk i =l Ke u
rs.Fields(“Parameter” &i).Nilai =Parameter(i)
Selanjutnya
rs.Update
End Sub
Perhatikan bahwa kami menggunakan ParamArray yang memungkinkan kami untuk menentukan parameter sebanyak yang kami butuhkan untuk prosedur tersimpan. Jika Anda ingin menjadi gila dan memiliki 20 parameter lagi, Anda bisa menambahkan lebih banyak bidang ke tabel dan memperbarui pemicu dan kode VBA akan tetap berfungsi. Anda akan dapat melakukan sesuatu seperti ini:
ExecuteWithLargeParameters "dbo", "uspMyStoredProcedure", dteStartDate, dteEndDate, strSomeBigXMLDocument
Mudah-mudahan, solusi tidak akan diperlukan untuk waktu yang lama (terutama jika Anda pergi ke Access UserVoice dan upvote berbagai item yang berkaitan dengan Access + SQL / ODBC), tapi kami harap Anda merasa berguna jika Anda menemukan diri Anda dalam situasi kita masuk. Kami juga ingin mendengar tentang peningkatan yang mungkin Anda miliki untuk solusi ini atau pendekatan yang lebih baik!