Sử dụng Tham số Lớn cho Quy trình được lưu trữ trong Microsoft SQL với DAO
Như nhiều người trong số các bạn đã biết, nhóm SQL Server đã thông báo ngừng sử dụng OLEDB cho công cụ cơ sở dữ liệu SQL Server (Đọc:chúng tôi không thể sử dụng ADO vì ADO sử dụng OLEDB). Ngoài ra, SQL Azure không chính thức hỗ trợ ADO, mặc dù người ta vẫn có thể sử dụng nó bằng cách sử dụng SQL Server Native Client. Tuy nhiên, trình điều khiển ODBC 13.1 mới đi kèm với một số tính năng sẽ không có sẵn trong SQL Server Native Client và có thể còn nhiều tính năng sắp ra mắt.
Điểm mấu chốt:chúng ta cần làm việc với DAO thuần túy. Đã có nhiều mục thoại của người dùng chạm vào chủ đề Access / ODBC hoặc Access / SQL Server… ví dụ:
Trình kết nối dữ liệu SQL Server
Tích hợp tốt hơn với SQL Server
Tích hợp tốt hơn với SQL Azure
Hãy làm cho Access có thể xử lý nhiều kiểu dữ liệu hơn như thường được sử dụng trong cơ sở dữ liệu Máy chủ
Làm cho Access tốt hơn Ứng dụng khách ODBC
(Nếu bạn chưa bỏ phiếu hoặc chưa truy cập access.uservoice.com, hãy vào đó và bỏ phiếu nếu bạn muốn nhóm Access triển khai tính năng yêu thích của bạn)
Nhưng ngay cả khi Microsoft có cải tiến DAO trong phiên bản tiếp theo, chúng tôi vẫn phải xử lý các ứng dụng hiện có của khách hàng. Chúng tôi đã cân nhắc sử dụng ODBC thay vì nhà cung cấp OLEDB (MSDASQL) nhưng chúng tôi cảm thấy rằng nó giống như cưỡi ngựa trên một con ngựa sắp chết. Nó có thể hoạt động nhưng nó có thể chết trong một khoảng thời gian ngắn.
Đối với hầu hết các phần, một truy vấn chuyển qua sẽ thực hiện những gì chúng ta cần làm và thật dễ dàng kết hợp một hàm để bắt chước chức năng của ADO bằng cách sử dụng truy vấn chuyển qua DAO. Nhưng có một lỗ hổng đáng kể không dễ khắc phục - các tham số lớn cho các thủ tục được lưu trữ. Như tôi đã viết trước đó, đôi khi chúng ta sử dụng tham số XML như một cách để truyền một lượng lớn dữ liệu, nhanh hơn nhiều so với việc Access thực sự chèn tất cả dữ liệu một. Tuy nhiên, một truy vấn DAO được giới hạn trong khoảng 64K ký tự cho lệnh SQL và trong thực tế có thể ít hơn. Chúng tôi cần một cách để chuyển các tham số có thể lớn hơn 64K ký tự, vì vậy chúng tôi phải suy nghĩ về một giải pháp.
Nhập bảng tblExecuteStoredProcedure
Phương pháp chúng tôi chọn là sử dụng bảng vì khi chúng tôi sử dụng trình điều khiển ODBC mới hơn hoặc SQL Server Native Client, DAO có thể dễ dàng xử lý lượng lớn văn bản (còn gọi là Bản ghi nhớ) bằng cách chèn trực tiếp vào bảng. Do đó, để thực thi một tham số XML lớn, chúng ta sẽ viết thủ tục để thực thi và tham số của nó vào bảng, sau đó để trình kích hoạt nhận nó. Đây là tập lệnh tạo bảng:
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
);
Tất nhiên, chúng tôi không thực sự có ý định sử dụng nó như một chiếc bàn thực. Chúng tôi cũng tùy ý đặt 10 tham số mặc dù một thủ tục được lưu trữ có thể có nhiều tham số khác. Tuy nhiên, theo kinh nghiệm của chúng tôi, rất hiếm khi có nhiều hơn 10, đặc biệt là khi chúng tôi đang xử lý các tham số XML. Tự nó, bảng sẽ không hữu ích cho lắm. Chúng tôi cần một trình kích hoạt:
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;
–Chỉ xử lý một bản ghi duy nhất sẽ là bản ghi cuối cùng được chèn
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 được chèn AS p
WHERE p.RV =(
SELECT MAX (x. RV)
FROM được chèn AS x
);
SET @Params =STUFF ((
CHỌN
CONCAT (
N ',',
p.name,
N '=',
p. tên
)
TỪ sys.parameters AS p
INNER THAM GIA 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 ((
CHỌN
CONCAT (
N ',',
p.name,
N '',
t.name ,
TRƯỜNG HỢP
KHI t.name THÍCH N '% char%' HOẶC t.name THÍCH '% binary%'
THEN CONCAT (N '(', IIF (p.max_length =- 1, N'MAX ', CAST (p.max_length AS nvarchar (11))), N') ')
KHI t.name =' decimal 'HOẶC t.name =' numeric '
THÌ CONCAT (N '(', p.pre precision, N ',', p.scale, N ')')
ELSE N ”
HẾT
)
TỪ 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)
CHO XML PATH (N ”)
), 1, 1, N ”);
SET @ParamCount =(
CHỌN ĐẾM (*)
TỪ sys.parameters AS p
WHERE p.object_id =OBJECT_ID (@FullyQualifiedProcedureName)
);
SET @ParamList + =((
CHỌN
CONCAT (N ',', p.ParameterName, N 'nvarchar (1)')
TỪ (GIÁ TRỊ
(1, N '@ Tham số1 ′),
(2, N' @ Tham số2 ′),
(3, N '@ Tham số3 ′),
(4, N' @ Tham số4 ′),
/> (5, N '@ Tham số5 ′),
(6, N' @ Tham số6 ′),
(7, N '@ Tham số7 ′),
(8, N' @ Parameter8 ′),
(9, N '@ Parameter9 ′),
(10, N' @ Parameter10 ′)
) AS p (ParameterID, ParameterName)
WHERE p. ParameterID> @ParamCount
FOR XML PATH (N ”)
));
SET @Sql =CONCAT (N’EXEC ‘, @FullyQualifiedProcedureName, N’ ‘, @Params, N’; ’);
– Ngăn chặn bất kỳ bộ kết quả nào được trả về từ trình kích hoạt (không được dùng nữa)
–Nếu một thủ tục được lưu trữ trả về bất kỳ bộ nào, trình kích hoạt sẽ kết thúc bằng lỗi
EXECUTE sys.sp_executesql @Sql, @ParamList, @ Tham số1, @ Tham số2, @ Tham số3, @ Tham số4, @ Tham số5, @ Tham số6, @ Tham số7, @ Tham số8, @ Tham số9, @ Tham số10
VỚI BỘ KẾT QUẢ KHÔNG CÓ;
DELETE FROM dbo.tblExecuteStoredProcedure
WHERE TỒN TẠI (
CHỌN ĐẦY ĐỦ
TỪ được chèn
Đã chèn ở đâu.ExecuteID =tblExecuteStoredProcedure.ExecuteID
);
HẾT;
P>
Một khá mồm, đó là cò súng. Về cơ bản, nó cần một lần chèn duy nhất, sau đó tìm ra cách chuyển đổi các tham số từ nvarchar (MAX) của chúng như được định nghĩa trên bảng tblExecuteStoredProcedure thành kiểu thực tế được yêu cầu bởi thủ tục được lưu trữ. Chuyển đổi ngầm được sử dụng và vì nó được bao bọc trong sys.sp_executesql hoạt động tốt cho nhiều loại dữ liệu miễn là bản thân các giá trị thông số hợp lệ. Lưu ý rằng chúng tôi yêu cầu thủ tục được lưu trữ KHÔNG trả về bất kỳ bộ kết quả nào. Microsoft cho phép trình kích hoạt trả về tập hợp kết quả nhưng như đã lưu ý, nó không phải là tiêu chuẩn và đã không được dùng nữa. Vì vậy, để tránh sự cố với các phiên bản SQL Server trong tương lai, chúng tôi chặn khả năng đó. Cuối cùng, chúng tôi dọn sạch bàn, vì vậy nó luôn trống. Rốt cuộc, chúng ta đang lạm dụng bảng; chúng tôi không lưu trữ bất kỳ dữ liệu nào.
Tôi đã chọn sử dụng trình kích hoạt vì nó cắt giảm số lượng các chuyến đi vòng giữa Access và SQL Server. Nếu tôi sử dụng một thủ tục được lưu trữ để xử lý T-SQL từ phần thân của trình kích hoạt, điều đó có nghĩa là tôi cần phải gọi nó sau khi tôi chèn vào bảng và cũng phải đối phó với các tác dụng phụ tiềm ẩn chẳng hạn như hai người dùng chèn cùng một lúc hoặc lỗi để lại hồ sơ, v.v.
Được rồi, nhưng chúng ta sử dụng “bảng” và trình kích hoạt của nó như thế nào? Đó là lúc chúng tôi cần một chút mã VBA để thiết lập toàn bộ sự sắp xếp…
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
Đặt db =CurrentDb
Đặt rs =db.OpenRecordset (“CHỌN * TỪ tblExecuteStoredProcedure;”, dbOpenDynaset, dbAppendOnly Hoặc dbSeeChanges)
rs.AddNew
rs.Fields (“Thủ tụcSchema”). Giá trị =Thủ tụcSchema
rs.Fields (“Tên thủ tục”). Giá trị =Tên thủ tục
l =LBound (Tham số)
u =UBound (Tham số)
For i =l To u
rs.Fields (“Tham số” &i) .Value =Tham số (i)
Tiếp theo
rs.Update
End Sub
Lưu ý rằng chúng tôi sử dụng ParamArray cho phép chúng tôi chỉ định nhiều tham số mà chúng tôi thực sự cần cho một thủ tục được lưu trữ. Nếu bạn muốn phát điên và có thêm 20 tham số, bạn chỉ có thể thêm các trường khác vào bảng và cập nhật trình kích hoạt và mã VBA sẽ vẫn hoạt động. Bạn sẽ có thể làm điều gì đó như sau:
ExecuteWithLargeParameters "dbo", "uspMyStoredProcedure", dteStartDate, dteEndDate, strSomeBigXMLDocument
Hy vọng rằng cách giải quyết này sẽ không cần thiết trong một thời gian dài (đặc biệt nếu bạn truy cập Access UserVoice và tán thành các mục khác nhau liên quan đến Access + SQL / ODBC), nhưng chúng tôi hy vọng bạn thấy nó hữu ích nếu bạn thấy mình ở trong tình huống của chúng tôi. trong. Chúng tôi cũng muốn biết về những cải tiến mà bạn có thể có đối với giải pháp này hoặc cách tiếp cận tốt hơn!