sql >> Database >  >> RDS >> Access

Grote parameters gebruiken voor Microsoft SQL-opgeslagen procedure met DAO

Grote parameters gebruiken voor Microsoft SQL-opgeslagen procedure met DAO

Zoals velen van jullie al weten, heeft het SQL Server-team de afschaffing van OLEDB voor de SQL Server-database-engine aangekondigd (Lees:we kunnen ADO niet gebruiken omdat ADO OLEDB gebruikt). Bovendien ondersteunt SQL Azure officieel ADO niet, hoewel je er nog steeds mee weg kunt komen met SQL Server Native Client. Het nieuwe 13.1 ODBC-stuurprogramma wordt echter geleverd met een aantal functies die niet beschikbaar zullen zijn in de SQL Server Native Client, en mogelijk komen er nog meer.

Waar het op neerkomt:we moeten werken met pure DAO. Er zijn al meerdere spraakitems van gebruikers over het onderwerp Access / ODBC of Access / SQL Server ... bijvoorbeeld:

Gegevensconnector SQL Server
Betere integratie met SQL Server
Betere integratie met SQL Azure
Zorg ervoor dat Access meer gegevenstypen kan verwerken die vaak worden gebruikt in Serverdatabases
Maak Access een betere ODBC-client

(Als je niet hebt gestemd of access.uservoice.com hebt bezocht, ga dan daarheen en stem als je wilt dat het Access-team je favoriete functie implementeert)

Maar zelfs als Microsoft DAO in de volgende versie verbetert, hebben we nog steeds te maken met de bestaande applicaties van onze klant. We hebben overwogen om ODBC via OLEDB-provider (MSDASQL) te gebruiken, maar we vonden dat het leek op schrijlingswerking op een pony op een stervend paard. Het zou kunnen werken, maar het zou een korte weg naar beneden kunnen sterven.

Voor het grootste deel zal een passthrough-query doen wat we moeten doen en het is gemakkelijk om een ​​functie samen te stellen om de functionaliteit van ADO na te bootsen met behulp van een DAO pass-through-query. Maar er is één belangrijke lacune die niet gemakkelijk kan worden verholpen:grote parameters voor opgeslagen procedures. Zoals ik eerder schreef, gebruiken we soms de XML-parameter als een manier om grote hoeveelheden gegevens door te geven, wat veel sneller is dan dat Access alle gegevens één voor één invoegt. Een DAO-query is echter beperkt tot ongeveer 64K tekens voor het SQL-commando en kan in de praktijk zelfs nog minder zijn. We hadden een manier nodig om parameters door te geven die groter konden zijn dan 64K tekens, dus moesten we nadenken over een tijdelijke oplossing.

Voer de tabel tblExecuteStoredProcedure in

De benadering die we kozen was om een ​​tabel te gebruiken, omdat wanneer we nieuwere ODBC-stuurprogramma's of SQL Server Native Client gebruiken, DAO gemakkelijk grote hoeveelheden tekst (ook wel Memo) kan verwerken door deze rechtstreeks in de tabel in te voegen. Om een ​​grote XML-parameter uit te voeren, zullen we daarom de uit te voeren procedure en de bijbehorende parameter naar de tabel schrijven en de trigger deze vervolgens laten oppakken. Hier is het script voor het maken van een 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
);

We zijn natuurlijk niet van plan om dit als een echte tafel te gebruiken. We stellen ook willekeurig 10 parameters in, hoewel een opgeslagen procedure er veel meer kan hebben. In onze ervaring is het echter vrij zeldzaam om veel meer dan 10 te hebben, vooral als we te maken hebben met XML-parameters. Op zichzelf zou de tabel niet erg handig zijn. We hebben een trigger nodig:

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;

–Verwerk alleen een enkele record die als laatste moet worden ingevoegd
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
VANAF ingevoegd ALS p
WHERE p.RV =(
SELECT MAX(x. RV)
VAN ingevoegd AS x
);

SET @Params =STUFF((
SELECT
CONCAT(
N',',
p.naam,
N' =',
p. name
)
FROM sys.parameters AS p
BINNENLANDSE JOIN sys.types AS t
ON p.user_type_id =t.user_type_id
WAAR p.object_id =OBJECT_ID( @FullyQualifiedProcedureName)
VOOR 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 ='decimaal' OF t.name ='numeriek'
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)
WAAR p. ParameterID> @ParamCount
VOOR XML PATH(N”)
));

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

–Voorkomen dat resultaatsets worden geretourneerd door een trigger (die verouderd is)
–Als een opgeslagen procedure er een retourneert, eindigt de trigger in een fout
EXECUTE sys.sp_executesql @Sql, @ParamList, @ Parameter1, @Parameter2, @Parameter3, @Parameter4, @Parameter5, @Parameter6, @Parameter7, @Parameter8, @Parameter9, @Parameter10
MET RESULTAAT ZET GEEN;

VERWIJDEREN VAN dbo.tblExecuteStoredProcedure
WAAR BESTAAT (
SELECT NULL
FROM inserted
WHERE inserted.ExecuteID =tblExecuteStoredProcedure.ExecuteID
);
EINDE;

Een hele mondvol, die trigger. In principe is er een enkele invoeging nodig en vervolgens wordt uitgezocht hoe de parameters van hun nvarchar (MAX) zoals gedefinieerd in de tabel tblExecuteStoredProcedure kunnen worden geconverteerd naar het daadwerkelijke type dat vereist is door de opgeslagen procedure. Impliciete conversies worden gebruikt, en aangezien het is verpakt in een sys.sp_executesql werkt het goed voor een verscheidenheid aan gegevenstypen, zolang de parameterwaarden zelf geldig zijn. Merk op dat we vereisen dat de opgeslagen procedure GEEN resultatensets retourneert. Microsoft staat triggers toe om resultatensets te retourneren, maar zoals opgemerkt, is het niet-standaard en is het verouderd. Dus om problemen met toekomstige versies van SQL Server te voorkomen, blokkeren we die mogelijkheid. Ten slotte ruimen we de tafel af, zodat deze altijd leeg is. We misbruiken tenslotte de tafel; we slaan geen gegevens op.

Ik heb ervoor gekozen om een ​​trigger te gebruiken omdat het het aantal retourvluchten tussen Access en SQL Server vermindert. Als ik een opgeslagen procedure had gebruikt om de T-SQL uit het lichaam van de trigger te verwerken, zou dat hebben betekend dat ik het had moeten bellen nadat ik het in de tabel had ingevoegd en ook had moeten omgaan met mogelijke bijwerkingen zoals twee gebruikers die tegelijkertijd invoegen of een fout waardoor een record achterblijft, enzovoort.

OK, maar hoe gebruiken we de "tabel" en de trigger? Dat is waar we een beetje VBA-code nodig hebben om het hele arrangement op te zetten ...

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 of 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)
Volgende

rs.Update
Sub beëindigen

Merk op dat we ParamArray gebruiken waarmee we zoveel parameters kunnen specificeren als we nodig hebben voor een opgeslagen procedure. Als je gek wilt worden en nog 20 parameters wilt hebben, kun je gewoon meer velden aan de tabel toevoegen en de trigger bijwerken en de VBA-code zou nog steeds werken. Je zou zoiets als dit kunnen doen:

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

Hopelijk is de tijdelijke oplossing niet lang nodig (vooral als u naar Access UserVoice gaat en verschillende items met betrekking tot Access + SQL / ODBC up-stemt), maar we hopen dat u het nuttig vindt als u zich in de situatie bevindt waarin we ons bevinden. in. We horen ook graag over verbeteringen die u mogelijk heeft voor deze oplossing of een betere aanpak!


  1. Postgresql Huidige tijdstempel op Update

  2. LOG() Functie in Oracle

  3. Hoe pas je een functie toe op elk element van een matrixkolom in Postgres?

  4. Fix "ERROR 1136 (21S01):Kolomtelling komt niet overeen met waardetelling in rij 2" bij gebruik van de VALUES-instructie in MySQL