Organizarea de contoare personalizate (generatoare) în serverul Microsoft sql

Microsoft este învinuit în mod regulat pentru faptul că în SQL Server nu există încă nicio implementare a unui lucru ca SEQUENCE în Oracle.

În curs de pregătire, am devenit interesat de această ofertă acest concurent și a dat seama că nimic altceva decât un vechi prieten de identitate, „dar într-un mod diferit“ pentru că știu de afaceri are nevoie de acolo pentru a raporta cu privire la toate tipurile de identificatori, contoare și alte în SQL Rusă Server User Group nr.







Să presupunem că există un ciclu. Acest lucru este bun, dar să-l vorbim blând, nu în primul rând, dacă este necesar.
Este bine ca SEQUENCE să poată fi folosită ca o funcție, nu doar ca o valoare DEFAULT pe câmpul de masă și nu să coreleze momentul obținerii valorii cu momentul adăugării fizice a înregistrării.
Dar cerințele cheie reale - numele secvenței de formare dinamică (sau orice alt mod de a pune în aplicare „Vreau numerotarea de la începutul anului / luna“) și cel puțin un mecanism de „re“ obtinerea „valori lipsă“ sunt absente și acolo.
Și dacă o afacere nu poate produce cel puțin anumite criterii de control astfel de secvențe, atunci de fapt, nu-mi pasă ce funcția de generare a secvenței - identitate, SUCCESIUNII sau NEWID - nu prea mare diferență, toate cele trei soluții sunt potrivite la fel de rau pentru numarul facturii si factura.

De obicei oferite în soluțiile de generare a numerelor de afaceri, scrise în TSQL și bazate pe tabelele cu valorile actuale ale contoarelor, au un dezavantaj teribil - blocarea. Într-adevăr, dacă "generăm" un nou număr, atunci la momentul generării, trebuie să blocăm contorul astfel încât cealaltă conexiune să nu primească aceeași valoare. Și, de regulă, numărul avem nevoie pentru a obține într-o tranzacție deja deschisă, care este plină de faptul că cei doi, inițial, independent de procesul de afaceri va fi, cel mai bun caz, un bloc lung unul de altul, iar în cel mai rău - nu va atât de independentă, pentru a evita blocaj (blocaj).

În plus, generarea de numere pe baza tabelului de contoare nu poate fi "înfășurată" în funcție, ceea ce ar fi foarte de dorit să pună în aplicare construcțiile formei:

inserați în MyTable (DocNum.DocDate.Comment)
selectați Generator. NextValue ('SequenceFor_DocNum'), IncomeDate. comentariu
din #SomeBuffer


deoarece funcțiile interzic modificările de date și nu putem schimba valoarea curentă.

Dar nu chiar nimeni! În funcții, este posibilă apelarea procedurilor memorate extins și a procedurilor și funcțiilor CLR.

Dar, în funcția CLR, avem capacitatea de a vă conecta la același server și la baza de date, dar într-o altă conexiune și apelați procedura care generează numărul de interes, fără a impune blocări îndelungate.






Pentru a face acest lucru, va trebui să faceți niște acțiuni suplimentare pe lângă scrierea funcției în sine:

Acum să trecem de la "multe broșuri" la un cod T-SQL mai ușor de înțeles.
Ce este interesant, în același TSQL vom face chiar adunarea - nu avem nevoie chiar de VisualStudio, ci mai mult despre asta mai târziu.

(Toate interogările sunt efectuate în baza de date unde avem nevoie de contoare)

Configurarea serverului - activați CLR:

dacă există (selectați * din configurațiile sys, unde name = 'clr enabled' și value_in_use = 0)

exec ('sp_configure' 'afișează opțiunile avansate' ', 1')

exec ('sp_configure' 'clr enabled' ', 1')

Configurează baza de date - permitem funcționarea ansamblurilor nesigure:

declara @sql nvarchar (max)

set @sql = baza de date N'alter '+ DB_NAME () + N' set de încredere pe '

Creăm schema _Generator, în care vor fi obiectele de bază necesare manipulării cu contoarele:
dacă SCHEMA_ID ('_Generator') este null exec ('create schema _Generator')

Creați un tabel în care vor fi stocați parametrii contoarelor:

creați tabelul _Generator. listă

ID unic identificator nu este null defaultid newid ().

Numele sysname nu este null,

StartValue int nu constrânge nul DF__Generator_List_StartValue implicit 0.

constrângere PK_List_ID cheie primară clustered (ID),

constrângere AK_List_Name unic (Nume)

Acum creați un declanșator, care pentru fiecare contor introdus în lista _Generator.List va crea un nume de cont în schema numită G $. Funcțiile NextValue și CurrentValue. Și, în funcție de parametrul IsWorkWithHoles, implementarea funcției NextValue este oarecum diferită.

creați declanșator [_Generator]. [TR_List_UpdateGenerator] pe [_Generator]. [List]

cu executarea ca proprietar

după inserare. actualizați. șterge

setați ansi_nulls pe

@ Nume nvarchar (128),

Acum, o mică problemă ca ansamblul CLR.

Bănuiesc că nu toți dezvoltatorii DB au relații prietenești cu C # și VisualStudio și reprezintă modul de compilare a ansamblului.

Cel mai probabil, de asemenea, foarte puțini oameni vor dori să aibă încredere în adunare, în formă de dll.

Prin urmare, compilam și creăm ansamblul direct în T-SQL. Singura cerință este ca SQL Server în sine trebuie să aibă .NET Framework 3.5 instalat:

Tabelul @t (txt varchar (255))

@temp varchar (255),

@sql varchar (8000),

@cs varchar (max)

-- Realizarea bazei de date de încredere

set @sql = 'alter bază de date' + db_name () + 'set de încredere pe'

exec xp_cmdshell 'set'

selectați @temp = substring (txt. 6. 255)

unde txt ca "TEMP%"

setați @cs = 'utilizând System;

publică PGenerator clasa parțială

statică publică SqlInt32 NextValue (secvența SqlString, SqlInt32 SPID, SqlString ServerName, SqlString DatabaseName)

utilizând (SqlConnection IsolatedConn

= New SqlConnection ( "Securitatea integrată = true; inițială de catalog =" + DatabaseName.ToString () + "; serverul =" + ServerName.ToString () + "; Application Name = _Generator_for_" + SPID.ToString () + „; Înrola = false "))

SqlCommand GenValue = noul SqlCommand ("_Generator.GenerateValue", IsolatedConn);

SqlParametrul ret = nou SqlParameter ();

SqlInt32 Val = (int) GenValue.Parameters ["ReturnValue"] Valoare;

public static SqlInt32 NextValueHole (SqlString Sequence, SqlInt32 SPID, SqlString ServerName, SqlString databasename)

utilizând (SqlConnection IsolatedConn

= New SqlConnection ( "Securitatea integrată = true; inițială de catalog =" + DatabaseName.ToString () + "; serverul =" + ServerName.ToString () + "; Application Name = _Generator_for_" + SPID.ToString () + „; Înrola = false "))

SqlCommand GenValue = noul SqlCommand ("_Generator.GenerateValueHole", IsolatedConn);

SqlParametrul ret = nou SqlParameter ();







Trimiteți-le prietenilor: