Access
 sql >> база данни >  >> RDS >> Access

Използване на големи параметри за съхранена процедура на Microsoft SQL с DAO

Използване на големи параметри за съхранена процедура на Microsoft SQL с DAO

Както много от вас вече знаят, екипът на SQL Server обяви оттегляне на OLEDB за SQL Server база данни (Прочетете:не можем да използваме ADO, защото ADO използва OLEDB). Освен това SQL Azure не поддържа официално ADO, въпреки че все още може да се размине с помощта на SQL Server Native Client. Въпреки това, новият 13.1 ODBC драйвер идва с редица функции, които няма да са налични в SQL Server Native Client и може да има още.

Изводът:трябва да работим с чист DAO. Вече има множество потребителски гласови елементи, засягащи темата за Access / ODBC или Access / SQL Server... например:

Конектор за данни SQL Server
По-добра интеграция със SQL Server
По-добра интеграция със SQL Azure
Моля, направете Access в състояние да обработва повече типове данни, както обикновено се използват в сървърните бази данни
Направете Access по-добър ODBC клиент

(Ако не сте гласували или не сте посетили access.uservoice.com, отидете там и гласувайте, ако искате екипът на Access да приложи любимата ви функция)

Но дори ако Microsoft подобри DAO в следващата версия, ние все още трябва да се справим със съществуващите приложения на нашите клиенти. Обмисляхме да използваме ODBC над доставчика на OLEDB (MSDASQL), но смятахме, че това е подобно на престояване на пони върху умиращ кон. Може да работи, но може просто да умре за кратко.

В по-голямата си част заявката за преминаване ще направи това, което трябва да направим, и е лесно да съберем заедно функция, която да имитира функционалността на ADO, използвайки DAO транзитна заявка. Но има една значителна празнина, която не се отстранява лесно — големи параметри за съхранените процедури. Както писах по-рано, понякога използваме XML параметър като начин за предаване на голямо количество данни, което е много по-бързо, отколкото ако Access действително вмъква всички данни една по една. Въпреки това, една DAO заявка е ограничена до около 64K знака за SQL командата и на практика може да бъде дори по-малко. Имахме нужда от начин да предаваме параметри, които могат да бъдат по-големи от 64K знака, така че трябваше да помислим за заобикаляне.

Въведете таблицата tblExecuteStoredProcedure

Подходът, който избрахме, беше да използваме таблица, защото когато използваме по-нови ODBC драйвери или SQL Server Native Client, DAO лесно може да обработва голямо количество текст (известен още като Memo), като се вмъква директно в таблицата. Следователно, за да изпълним голям 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 параметъра, въпреки че една съхранена процедура може да има много повече. Въпреки това, според нашия опит, доста рядко е да имаме много повече от 10, особено когато имаме работа с XML параметри. Сама по себе си таблицата не би била много полезна. Нуждаем се от тригер:

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;

–Обработвайте само един запис, който трябва да бъде последния вмъкнат
ДЕКЛАРИРАЙТЕ @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 вмъкнат AS p
WHERE p.RV =(
SELECT MAX(x. RV)
ОТ вмъкнат AS x
);

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)
ЗА XML ПЪТ(N”)
), 1, 1, N”);

SET @ParamList =STUFF((
SELECT
CONCAT(
N',',
p.name,
N' ',
t.name ,
CASE
КОГАТО t.name КАТО N'%char%' ИЛИ ​​t.name КАТО '%binary%'
THEN CONCAT(N'(', IIF(p.max_length =- 1, N'MAX', CAST(p.max_length AS nvarchar(11))), N')')
КОГАТО t.name ='десетично' ИЛИ ​​t.name ='числово'
ТОГАВА CONCAT(N'(', p.precision, N',', p.scale, N')')
ELSE N”
END
)
ОТ sys.parameters AS p
INNER JOIN sys.types AS t
ON p.user_type_id =t.user_type_id
КЪДЕ p.object_id =OBJECT_ID(@FullyQualifiedProcedureName)
ЗА XML ПЪТ(N”)
), 1, 1, N”);

SET @ParamCount =(
SELECT COUNT(*)
ОТ sys.parameters КАТО p
WHERE p.object_id =OBJECT_ID(@FullyQualifiedProcedureName)
);

SET @ParamList +=((
SELECT
CONCAT(N',', p.ParameterName, N' nvarchar(1)')
ОТ (СТОЙНОСТИ
(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
ЗА XML ПЪТ(N”)
));

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

–Предотвратяване на връщането на каквито и да било набори от резултати от тригер (който е отхвърлен)
–Ако съхранена процедура върне някакъв, тригерът ще завърши с грешка
EXECUTE sys.sp_executesql @Sql, @ParamList, @ Параметър1, @Параметър2, @Параметър3, @Параметър4, @Параметър5, @Параметър6, @Параметър7, @Параметър8, @Параметър9, @Параметър10
С НИКАКВИ РЕЗУЛТАТИ;

ИЗТРИВАНЕ ОТ dbo.tblExecuteStoredProcedure
КЪДЕ СЪЩЕСТВУВА (
ИЗБЕРЕТЕ NULL
ОТ вмъкнато
WHERE inserted.ExecuteID =tblExecuteStoredProcedure.ExecuteID
);
КРАЙ; P>

Доста хапка, този спусък. По принцип отнема едно вмъкване, след което измисля как да преобразува параметрите от техния nvarchar(MAX), както е дефинирано в таблицата tblExecuteStoredProcedure, към действителния тип, изискван от съхранената процедура. Използват се неявни преобразувания и тъй като е обвит в sys.sp_executesql работи добре за различни типове данни, стига самите стойности на параметрите да са валидни. Имайте предвид, че изискваме съхранената процедура НЕ връща никакви резултативни набори. Microsoft позволява на тригери да връщат набори от резултати, но както беше отбелязано, това е нестандартно и е отхвърлено. Така че, за да избегнем проблеми с бъдещите версии на SQL Server, ние блокираме тази възможност. Накрая изчистваме масата, така че тя винаги е празна. В крайна сметка злоупотребяваме с масата; не съхраняваме никакви данни.

Избрах да използвам тригер, защото намалява броя на двупосочните пътувания между Access и SQL Server. Ако използвах съхранена процедура за обработка на T-SQL от тялото на тригера, това би означавало, че ще трябва да я извикам, след като вмъкна в таблицата и също така да се справя с потенциални странични ефекти, като например двама потребители, които вмъкват едновременно или грешка, оставяйки запис след себе си и така нататък.

Добре, но как да използваме „таблицата“ и нейния тригер? Точно там ни трябва малко 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

Задайте db =CurrentDb
Задайте rs =db.OpenRecordset(“SELECT * FROM tblExecuteStoredProcedure;”, dbOpenDynaset, dbAppendOnly или dbSeeChanges)

rs.AddNew
rs.Fields(“ProcedureSchema”).Стойност =ProcedureSchema
rs.Fields(“ProcedureName”).Стойност =ProcedureName

l =LBound(Parameters)
u =UBbound(Parameters)
За i =l To u
rs.Fields(“Parameter” &i).Стойност =Параметри(i)
Следващ

rs.Update
Край под

Обърнете внимание, че използваме ParamArray, който ни позволява да посочим толкова параметри, колкото всъщност са ни необходими за съхранена процедура. Ако искате да се побъркате и да имате още 20 параметъра, можете просто да добавите още полета към таблицата и да актуализирате тригера и VBA кодът ще продължи да работи. Ще можете да направите нещо подобно:

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

Надяваме се, че решението няма да е необходимо за дълго време (особено ако отидете на Access UserVoice и гласувате за различни елементи, свързани с Access + SQL / ODBC), но се надяваме, че ще ви бъде полезно, ако се окажете в ситуация, в която сме in. Също така бихме искали да чуем за подобрения, които може да имате за това решение или по-добър подход!


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Бързи и мръсни за цикли в непосредствения прозорец

  2. Как да създадете формуляр за навигация в Microsoft Access

  3. Масови вмъквания на Salesforce от Microsoft Access

  4. Защо всеки малък бизнес се нуждае от база данни

  5. 5 знака, че сте надраснали Excel