internetul Windows. Android

SQL Oracle Balance de la diviziune. Ghidul de referință MySQL.

Aceasta este o altă sarcină comună. Principiul de bază constă în acumularea valorilor unui atribut (element agregat) bazat pe ordonarea unui alt atribut sau atribut (element de comandă), eventual în prezența liniilor specificate pe baza unui alt atribut sau atribute (element de partiție). Există multe exemple de calcul al rezultatelor crescânde, cum ar fi calculul soldurilor în conturile bancare, urmărind disponibilitatea bunurilor în stoc sau cifrele curente de vânzări etc.

Pentru soluțiile SQL Server 2012 bazate pe seturi și utilizate pentru a calcula rezultatele incidentelor au fost exclusiv resurse intensive. Prin urmare, oamenii au apelat de obicei la deciziile iterative care lucrează neprietenos, dar în unele situații încă mai repede decât soluțiile bazate pe seturi. Datorită extinderii caracteristicilor ferestrelor de susținere în SQL Server 2012, rezultatele crescânde pot fi calculate utilizând un cod simplu bazat pe set, al cărui performanță este mult mai mare decât în \u200b\u200bsoluțiile vechi bazate pe T-SQL - ambele bazate pe seturi și iterative . Aș putea prezenta o soluție nouă și să procedăm la următoarea secțiune; Dar, astfel încât să înțelegeți cu adevărat amploarea schimbărilor, voi descrie căile vechi și voi compara performanța lor cu o nouă abordare. Firește, aveți dreptul să citiți doar prima parte care descrie o nouă abordare și să săriți restul articolului.

Pentru a demonstra diferite soluții, voi folosi solduri pe conturi. Iată un cod care creează și completează tabelul de tranzacții cu o cantitate mică de date de testare:

Stabiliți nocount; Utilizați TSQL2012; Dacă obiectul obiectelor ("dbo.transactions", "u") nu este null drop tabel dbo.Transactions; Creați tabelul dbo.Transactions (ACTIDID INT NU NULL - TRANID INT NU NULL Coloana de partiționare, - Coloana Val Bany nu NULL, - Măsura constrângerii PK_Transactions Cheie primară (atrimică, tranid)); Go este un set mic de introducere a datelor de testare în valorile DBO.Transaction (Acdicată, Tranid, Val) (1, 1, 4,00), (1, 2, -2,00), (1, 3, 5,00), (1 , 4, 2,00), (1, 5, 1,00), (1, 6, 3,00), (1, 8, -1,00), (1, 9, -2,00), (1, 9, -2,00); 1, 10, -3,00), (2, 1, 2,00), (2, 2, 1,00), (2, 3, 5,00), (2, 4, 1,00), (2, 5, -5,00), (2, 5, -5,00); 2, 6, 4,00), (2, 7, 2,00), (2, 9, -5,00), (2, 10, 4,00), (3, 1, -3,00), (3, 3, -2,00), (3, 4, 1,00), (3, 5, 4,00), (3, 6, -1,00), (3, 7, 5,00), (3, 8, 3.00), (3, 9, 5,00), (3, 10, -3,00);

Fiecare șir de linie reprezintă o operațiune bancară în cont. Depozitele sunt notate ca tranzacții cu o valoare pozitivă în coloana Val și eliminarea fondurilor - ca o valoare negativă a tranzacției. Sarcina noastră este de a calcula echilibrul contului în orice moment prin acumularea cantităților de operațiuni din linia Val atunci când comandați în coloana Tranid și acest lucru trebuie făcut pentru fiecare cont separat. Rezultatul dorit ar trebui să arate astfel:

Pentru a testa ambele soluții, aveți nevoie de o cantitate mai mare de date. Acest lucru se poate face cu o astfel de solicitare:

Declară @num_partiții ca int \u003d 10, @rows_per_partition ca int \u003d 10000; Trunchate tabel dbo.Transactions; Introduceți în DBO.Transactions cu (tablock) Selectați NP.N, RPP.N, (ABS (kicksum (nou)% 2) * 2-1) * (1 + ABS (suma de control (Newid ())% 5) ) de la dbo.getnums (1, @num_partiții) ca NP Cross Alăturați-vă dbo.getnums (1, @rows_per_partition) ca RPP;

Puteți specifica datele dvs. de intrare pentru a modifica numărul de secțiuni (conturi) și linii (tranzacții) în secțiune.

Soluție bazată pe caracteristicile ferestrei

Voi începe o poveste cu soluții bazate pe seturi, care utilizează funcția ferestrei de sumă de agregare. Definirea ferestrei aici este destul de clară: trebuie să împărtășiți fereastra ACTIDID, să sortați trandafirul și filtrul pentru a selecta șirurile din cadru de la partea superioară (precedentă nelegată) la curent. Iată cererea corespunzătoare:

Selectați ACTID, TRANID, VAL, Sum (Val) peste (partiție prin ordinul adid de rândurile de trandafiri între rândul anterior și curentul nelegat) ca echilibru de la dbo.Transactions;

Acest cod nu este doar simplu și simplu - se efectuează rapid. Planul acestei solicitări este prezentat în figura:

Tabelul are un indice grupat care îndeplinește cerințele POC și este potrivit pentru utilizarea funcțiilor ferestrei. În particular, lista tastelor index se bazează pe un element de partiționare (ACTID), urmată de un element de comandă (TRANID), de asemenea pentru a asigura acoperirea, indicele include toate celelalte coloane din interogare (val). Planul conține o vizionare ordonată, urmată de calcularea numărului de linie pentru nevoile interne și apoi unitatea ferestrei. Deoarece există un indice POC, optimizatorul nu trebuie să adauge o declarație de sortare la plan. Acesta este un plan foarte eficient. În plus, este scalat liniar. Mai târziu, când prezintă rezultatele comparației de performanță, veți vedea cât de eficientă această metodă în comparație cu soluțiile vechi.

SQL Server 2012 a folosit fie cereri sau conexiuni investite. Când utilizați cererea încorporată, rezultatele crescânde sunt calculate prin filtrarea tuturor rândurilor cu aceeași valoare a acționată ca în linia externă și valoarea tranidului, care este mai mică sau egală cu valoarea din linia exterioară. Apoi, agregarea se aplică rândurilor filtrate. Iată cererea corespunzătoare:

O abordare similară poate fi implementată utilizând compuși. Același predicat este utilizat ca în clauza în care în cererea atașată din conexiune. În acest caz, pentru tranzacția N-OH a aceluiași cont A, în instanța desemnată ca T1, veți găsi n corespondențe în instanța T2, în timp ce numerele de tranzacție rulează de la 1 la N. ca urmare a potrivirii Linia în T1, sunt repetate, deci aveți nevoie de șiruri de grupuri în toate elementele cu T1 pentru a obține informații despre tranzacția curentă și aplicați agregarea la atributul Val de la T2 pentru a calcula rezultatul crescând. Cererea finalizată arată astfel:

Selectați T1xid, T1.Tranid, T1.val, suma (t.2.val) ca echilibru de la dbo.Transactions ca T1 Alăturați-vă dbo.Transactions ca T2 pe t2.activ \u003d t1.activ și t2.trarid<= T1.tranid GROUP BY T1.actid, T1.tranid, T1.val;

Figura de mai jos prezintă planurile ambelor soluții:

Rețineți că în ambele cazuri din instanța T1 se efectuează indicele grupat. Apoi, pentru fiecare rând, operațiunea de căutare este furnizată în indicele de la începutul partițiilor de cont curent pe pagina de sfarsit a indexului, iar toate tranzactiile in care se citesc mai putin sau egal cu T1.Tranid. Punctul în care apare agregarea rândului, este puțin diferită în planuri, dar numărul de șiruri este în mod egal.

Pentru a înțelege câte linii sunt văzute, este necesar să se țină seama de numărul de elemente de date. Fie P numărul de secțiuni (conturi) și R este numărul de rânduri din secțiune (tranzacție). Apoi, numărul de rânduri din tabel este aproximativ egal cu P * R, dacă presupunem că tranzacțiile sunt distribuite prin conturi uniform. Astfel, vederea prezentată în partea superioară acoperă rândurile p * r. Dar, mai presus de toate, suntem interesați de ceea ce se întâmplă în iteratorul iterativ.

Planul prevede citirea 1 + 2 + ... + R Strings, care în cantitate este (R + R * 2) / 2. Numărul total de rânduri tratate în planuri este P * R + P * (R + R2) / 2. Aceasta înseamnă că numărul de operațiuni din plan crește într-un pătrat cu o creștere a dimensiunii secțiunii, adică dacă creșteți dimensiunea secțiunii în f mai multe ori, domeniul de aplicare de lucru va crește în aproximativ 2 ori. Asta e rău. De exemplu, 100 de rânduri corespund la 10 mii rânduri, iar un milion de rânduri corespunde unui milion etc. Pur și simplu pune acest lucru duce la o încetinire puternică a interogărilor într-o secțiune destul de mică a secțiunii, deoarece funcția patrată crește foarte repede. Astfel de soluții funcționează în mod satisfăcător la câteva zeci de linii pe secțiune, dar nu mai mult.

Soluții cursor

Soluțiile bazate pe cursor sunt implementate "în frunte". Cursorul este declarat în funcție de solicitare, comandând date privind contractul ACTID și TRANID. După aceea, se efectuează trecerea iterativă a înregistrărilor cursorului. Când este descoperit un cont nou, o variabilă care conține unitatea este resetată. În fiecare iterație, cantitatea de noua tranzacție este adăugată la variabilă, după care șirul este stocat într-o variabilă tabară cu informații despre tranzacția curentă plus valoarea curentă a rezultatului incremental. După trecerea iterativă, rezultatul variabilei tabulare este returnat. Iată codul deciziei finalizate:

Declară @result ca tabel (ACTID INT, TRANID INT, VAL bani, soldul banilor); Declarați @Actid ca int, @Prcactid ca int, @Tranid ca Int, @ valabil ca bani, @balance ca bani; Declarați C Cursor Fast_Forward pentru SELECT ACTID, TRANID, VAL de la DBO.Transactions Ordine de către ACTID, TRANID; Deschideți C Fetch Next de la C în @Actid, @Tranid, @val; Selectați @Prvactid \u003d @Actid, @Balance \u003d 0; În timp ce @@tch_status \u003d 0 începe dacă @Actid<> @Prcactid Selectați @Prvactid \u003d @Actid, @Balance \u003d 0; Set @balance \u003d @balance + @val; Introduceți în valorile @result (@activ, @Tranid, @val, @balance); FETCH NEXT DE LA C INTID @Actid, @Tranid, @val; Sfârșitul închiderii c; Dealocați C; Selectați * de la @result;

Planul de interogare care utilizează cursorul este prezentat în figura:

Acest plan este scalat liniar, deoarece datele din index sunt vizualizate doar o dată într-o anumită ordine. De asemenea, fiecare operație de obținere a unui rând din cursor aproximativ același cost pe linie. Dacă luați sarcina creată la procesarea unui rând de cursor, egal cu G, costul acestei soluții poate fi estimat ca P * R + P * R * G (după cum vă amintiți, P este numărul de secțiuni și R este Numărul de rânduri în secțiune). Deci, dacă creșteți numărul de rânduri pe secțiune în f mai multe ori, sarcina de pe sistem va fi p * r * f + p * r * f * g, adică va crește liniar. Costul procesării pe baza șirului este ridicat, dar datorită caracterului liniar al scalării, de la o anumită dimensiune a secțiunii, această soluție va demonstra o scalabilitate mai bună decât soluțiile bazate pe interogările și compușii imbricați datorită scalării patrate a acestor soluții . Măsurarea performanței arătată de mine a arătat că numărul atunci când soluția cu cursorul funcționează mai repede, egală cu câteva sute de linii pe secțiune.

În ciuda câștigurilor de performanță oferite de soluțiile bazate pe cursor, în general, acestea ar trebui evitate, deoarece acestea nu sunt relaționale.

Soluții bazate pe CLR

O posibilă soluție bazată pe CLR (runtime de limbă comună) De fapt, este una dintre formele de rezolvare a cursorului. Diferența este că, în loc să folosească cursorul T-SQL, care petrece multe resurse pentru a primi următoarea linie și executarea iterației, iterații sunt folosite.net sqldativader i.net, care funcționează mult mai repede. Una dintre caracteristicile CLR care face ca această opțiune să fie mai rapidă, este că șirul rezultat din tabelul temporar nu este necesar - rezultatele sunt trimise direct procesului de apel. Logica soluției bazată pe CLR este similară cu logica soluțiilor care utilizează cursorul și T-SQL. Aici este codul C # care determină procedura de soluționare stocată:

Folosind sistemul; Folosind sistemul.Data; Folosind sistemul.Data.sqlcient; folosind sistemul.Data.sqltypes; folosind Microsoft.sqlserver.Server; Clasa publică parțială stocată (contabilitate statică statică statică () (utilizând (SQLConnection Conn \u003d nou sqlconnection ("Conexiune context \u003d true; "+" Selectați Accid, Tranid, Val "+" de la DBO.Transactions "+" comanda prin actid, tranid; Sqlmedatata ("tranid", sqldbtype.int); coloane \u003d nou sqlmedatata ("val", sqldbtype.money); coloane \u003d noul sqlmedatata ("echilibru", sqldbtype.money); sqldatarycord record \u003d nou sqldatarecord (coloane); sqlcontext. Țeavă. ; Sqlmoney val \u003d cititor.getsqlmoney (2); dacă (ACTID \u003d\u003d PRGATID) (echilibru + \u003d val;) altcineva (echilibru \u003d val;) prvactid \u003d actid; rec ord.setsqlint32 (0, cititor.getsqlint32 (0)); record.setsqlint32 (1, cititor.getsqlint32 (1)); Înregistrare.setsqlmoney (2, val); Record.setsqlmoney (3, echilibru); Sqlcontext.pipe.sendresultsrow (record); ) Sqlcontext.pipe.sendresultind (); ))

Pentru a putea executa această procedură stocată în SQL Server, trebuie mai întâi să construiți un nume de asamblare bazat pe contabilitate și implementați în baza de date TSQL2012. Dacă nu sunteți familiarizați cu implementarea clădirilor din SQL Server, puteți citi secțiunea "Proceduri stocate și mediul CLR" în articolul "Proceduri stocate".

Dacă ați sunat la asamblarea contabilității, iar calea către fișierul de asamblare este "C: \\ Proiecte \\ AccountBalances \\ Bin \\ Debug \\ AccountBalances.dll", descărcați ansamblul în baza de date și înregistrați procedura stocată după cum urmează:

Crearea de acțiuni de asamblare de la "C: \\ Proiecte \\ AccountBalances \\ Bin \\ Debug \\ AccountBalances.dll"; GO Creare procedură dbo.accountbalances ca contabilitate de nume extern.storedroceds.accountbalances;

După implementarea asamblării și înregistrarea procedurii, o puteți efectua după cum urmează:

Exec dbo.accountbalances;

Așa cum am spus, SQLDATADEADER este doar o altă formă a cursorului, dar în această versiune costul citirii șirurilor este semnificativ mai mic decât atunci când se utilizează cursorul tradițional în T-SQL. De asemenea, iterațiile V.Net sunt efectuate mult mai repede decât în \u200b\u200bT-SQL. Astfel, soluțiile bazate pe CLR sunt, de asemenea, scalabile liniar. Testarea a arătat că performanța acestei soluții devine mai mare decât performanța soluțiilor care utilizează subqueries și conexiuni atunci când numărul de rânduri din secțiune trece prin 15.

La finalizare, trebuie să efectuați următorul cod de curățare:

Procedura de picătură dbo.accountbalances; Accesul la asamblare;

Iterații iterative

Până la acest punct, am arătat soluții și soluții iterative bazate pe seturi. Următoarea soluție se bazează pe iterații imbricate, care sunt un hibrid de iterativ și bazat pe seturi de abordări. Ideea este de a pre-copia rândurile din tabelul sursă (în cazul nostru, acestea sunt conturi bancare) într-o masă temporară împreună cu noul atribut numit Rownum, care este calculat folosind funcția ROW_Number. Numerele de rând sunt împărțite prin ACTID și sunt comandate de Tranid, deci prima tranzacție din fiecare cont bancar este atribuită numărul 1, cea de-a doua tranzacție - 2 etc. Apoi, în tabelul temporar creează un indice grupat cu o listă de chei (Rownum, ACTID). Exprimarea CTE recursivă este utilizată sau un ciclu special creat pentru procesarea unei linii pentru iterație în toate conturile. Apoi rezultatul incremental se calculează prin însumarea valorii corespunzătoare liniei curente, cu valoarea asociată cu șirul anterior. Iată implementarea acestei logici utilizând CTE recursivă:

Selectați ACTID, TRANID, VAL, ROW_NUMBER () peste (partiție prin ordinul de la Tranid) ca Rownum în # traduceri de la DBO.Transactions; Creați un indice unic clusterd Idx_Rownum_activ pe # traducătoare (Rownum, ACTID); Cu c ca (selectați 1 ca Rownum, Acdic ACTID, TRANID, VAL, VAL ca sumty de la # traseu în cazul în care Rownum \u003d 1 Union Alion Select Prv.rownum + 1, Prv.Actid, cur.trarid, cur.val, Prv.sumqty + Curs.val de la C ca PRV se alătură #Transactions ca cur.rownum \u003d prv.rownum + 1 și cur.actid \u003d prv.Actid) Selectați ACTID, TRANID, VAL, Sumty de la C (Maxrecursion 0); Drop tabelul # traduceri;

Și aceasta este o realizare folosind un ciclu explicit:

Selectați Row_Number () peste (partiție prin ordinul acrid de Tranid) ca Rownum, ACTID, TRANID, VAL, distribuția (VAL ca mare) ca sumty în # traduceri de la DBO.Transactions; Creați un indice unic clusterd Idx_Rownum_activ pe # traducătoare (Rownum, ACTID); Declară @rownum ca int; Set @Rownum \u003d 1; În timp ce 1 \u003d 1 începe setul @Rownum \u003d @rownum + 1; Update Cur set Sumqy \u003d Prv.sumqty + Cur.val de la # traseuptibile ca curte se alătură #Transactions ca PRV pe cur.rownum \u003d @rownum și Prv.rownum \u003d @Rownum - 1 și cur.actid \u003d Prv.Actid; Dacă @@ rowcount \u003d 0 pauză; Selectați Selectați Accid, Tranid, Val, Sumty de la # traducere; Drop tabelul # traduceri;

Această soluție oferă performanțe bune atunci când există un număr mare de secțiuni cu un număr mic de rânduri în secțiuni. Apoi, numărul de iterații este mic, iar lucrarea de bază este efectuată de seturile bazate pe partea de soluție, care conectează șirurile asociate cu un număr de linie, cu rânduri asociate cu numărul de linie anterior.

Actualizare multi-linie cu variabile

Afișat până la acest punct, recepțiile pentru calcularea rezultatelor incidentelor sunt garantate pentru a da rezultatul corect. Tehnica descrisă în această secțiune este ambiguă, deoarece se bazează pe comportamentul observat și nu documentat al sistemului, în plus, contrazice principiile rudelor. Activitatea ei ridicată se datorează unei viteze mari de muncă.

În această metodă, un manual de actualizare este utilizat cu variabile. Instrucțiunea de actualizare poate atribui o variabilă de expresie bazată pe o valoare a coloanei, precum și la atribuirea valorilor în coloanele cu o variabilă. Soluția începe cu crearea unui tabel temporar prin numele tranzacțiilor cu atributele ACTIDID, TRANID, VAL și Balanța și indicele cluster cu o listă de chei (ACTID, TRANID). Tabelul de timp este apoi umplut cu toate șirurile din baza de date sursă de tranzacții, iar valoarea de 0,00 este introdusă în coloana de echilibru a tuturor rândurilor. Apoi, instrucțiunea de actualizare cu variabile asociate cu tabelul de timp este apelată pentru a calcula rezultatele crescânde și introducerea valorii calculate în coloana Balance.

Variabilele @prevaCount și @prevalance sunt utilizate, iar valoarea din coloana sold este calculată utilizând următoarea expresie:

SET @PrevBalance \u003d Balance \u003d Caz în cazul în care actid \u003d @prevaCcount apoi @Prevalance + val altceva val capătul

Expresia cazului verifică dacă identificatorii conturilor curente și anterioare nu se potrivesc și, dacă acestea sunt egale, returnează valoarea valorilor anterioare și curente în coloana Balance. Dacă identificatorii de cont sunt diferite, suma tranzacției curente este returnată. Apoi, rezultatul expresiei casei este introdus în coloana Bilanț și este atribuită variabilei @prevalance. Într-o expresie separată a variabilei, Precacacount i se atribuie identificatorul de cont curent.

După expresia de actualizare, soluția reprezintă rânduri de la tabelul de timp și șterge ultima. Iată codul deciziei finalizate:

Creați tabelul # transactions (ACTID INT, TRANID INT, VAL bani, soldul banilor); Creați indexul cluster idx_actid_tranid pe # traducători (ACTID, TRANID); Introduceți în # instrucțiuni (TabLock) (TabLock) (Acdicate, Tranid, Val, Balance) Selectați Accid, Tranid, Val, 0,00 de la DBO.Transactions Ordine de către ACTID, TRANID; Declară @prevaCount ca int, @prevalance ca bani; Actualizați #Transactions Set @prevalance \u003d echilibru \u003d caz în cazul în care actid \u003d @prevacount apoi @Prevalance + val altceva val capăt, @prevaCcount \u003d actid de la # traducere (index (1), tablockx) (maxdop 1); Selectați * de la # traversations; Drop tabelul # traduceri;

Planul acestei soluții este prezentat în figura următoare. Prima parte este reprezentată de inserare, a doua actualizare, și al treilea - selectați:

În această decizie se presupune că atunci când se optimizează executarea actualizării, va fi întotdeauna efectuată o vizionare ordonată a indicelui cluster și soluția oferă o serie de solicitări pentru a preveni circumstanțele care pot împiedica acest lucru, de exemplu, paralelismul. Problema este că nu există o garanție oficială că optimizatorul va privi întotdeauna în ordinea unui indice grupat. Este imposibil să se bazeze pe caracteristicile computerelor fizice atunci când este necesar să se asigure corectitudinea logică a codului, cu excepția cazului în care nu există elemente logice care, prin definiție, nu pot garanta un astfel de comportament. Nu există caracteristici logice în acest cod că acest comportament ar putea garanta. Alegerile naturale, utilizarea sau nu această metodă se află în întregime pe conștiința dvs. Cred că este iresponsabil să o folosim, chiar dacă ați verificat mii de ori și "totul pare să funcționeze, așa cum ar trebui".

Din fericire, în SQL Server 2012 Această alegere devine aproape inutilă. În prezența unei soluții excepțional de eficiente utilizând funcțiile ferestrei, agregarea nu trebuie să se gândească la alte soluții.

Măsurarea performanței

Am efectuat o măsurătoare și compararea performanței diferitelor tehnici. Rezultatele sunt prezentate în desenele de mai jos:

Am rupt rezultatele în două grafice datorită faptului că metoda care utilizează cererea atașată sau compusul este atât de lentă decât restul pe care trebuia să-l folosesc altă scară pentru el. În orice caz, rețineți că majoritatea soluțiilor demonstrează dependența liniară a cantității de lucru la dimensiunea secțiunii și numai o soluție bazată pe o solicitare sau compus imbricată arată o dependență patrată. De asemenea, este clar cât de eficientă noua soluție pe baza funcției ferestrei de agregare. O soluție bazată pe actualizarea cu variabile este, de asemenea, foarte rapidă, dar în conformitate cu motivele pe care le-ați descris deja, nu vă recomand să o utilizați. Soluția CLR este, de asemenea, destul de rapidă, dar trebuie să scrie tot acest cod .NET și să depună o ansamblu în baza de date. Indiferent de modul în care se uită la seturi, soluția folosind agregatele ferestrelor rămâne cea mai preferabilă.

Toate funcțiile matematice în caz de revenire a erorilor null.

Unitar minus. Modifică semnul argumentului: MySQL\u003e Selectați - 2; -\u003e -2 Este necesar să se țină seama de faptul că dacă acest operator este utilizat cu tipul de mare, valoarea de returnare va avea, de asemenea, un tip de birint! Aceasta înseamnă că utilizarea operatorului pentru întregi poate fi evitată, ceea ce poate avea -2 ^ 63! ABS (x) returnează valoarea absolută a X: MySQL\u003e Selectați ABS (2); -\u003e 2 MySQL\u003e Selectați ABS (-32); -\u003e 32 Această caracteristică poate fi utilizată cu încredere pentru valorile de tip de tip. Semnul (X) returnează semnul argumentului în formularul -1, 0 sau 1, în funcție de faptul dacă X este negativ, zero sau pozitiv: MySQL\u003e Selectați semnul (-32); -\u003e -1 mysql\u003e Selectați semnul (0); -\u003e 0 MySQL\u003e Selectați semnul (234); -\u003e 1 mod (N, M)% valoare prin modul (cum ar fi operatorul% în c). Returnează echilibrul de la împărțirea n pe m: mysql\u003e selectați mod (234, 10); -\u003e 4 MySQL\u003e Selectați 253% 7; -\u003e 1 MySQL\u003e Selectați mod (29.9); -\u003e 2 Această caracteristică poate fi utilizată cu încredere pentru valorile tipului de amplificare. Etajul (x) returnează cel mai mare număr întreg care nu depășește X: MySQL\u003e Selectați podea (1.23); -\u003e 1 MySQL\u003e Selectați podea (-1,23); -\u003e -2 Ar trebui să se țină cont de faptul că valoarea returnată este transformată în BIGINT! Plafon (x) Returnează cel mai mic întreg, nu mai mică decât X: MySQL\u003e CEILING SELECT (1,23); -\u003e 2 MySQL\u003e Selectați plafonul (-1.23); -\u003e -1 Ar trebui să se țină cont de faptul că valoarea returnată este transformată în BIGINT! Round (x) Returnează un argument X, rotunjită la cel mai apropiat număr întreg: MySQL\u003e SELECT ROUND (-1.23); -\u003e -1 mysql\u003e Selectați rotund (-1,58); -\u003e -2 mysql\u003e Selectați rotund (1.58); -\u003e 2 Trebuie amintit că comportamentul funcției Rundei () atunci când valoarea argumentului egal la mijloc între două numere întregi depinde de implementarea specifică a bibliotecii C Rotunjirea poate fi realizată: la cel mai apropiat. număr par, întotdeauna la cel mai apropiat mai mult, întotdeauna la cel mai aproape mai mici, întotdeauna a fi direcționat la zero. Astfel încât rotunjirea sa întâmplat întotdeauna într-o singură direcție, este necesar să se utilizeze funcții bine specifice, cum ar fi trunchiatul () sau podeaua () în loc de acest lucru. Runda (x, d) returnează argumentul x, rotunjit la numărul cu semne zecimale D. Dacă d este 0, rezultatul va fi prezentat fără un semn zecimal sau o parte fracțională: MySQL\u003e Selectați rotund (1.298, 1); -\u003e 1.3 MySQL\u003e Selectați rotund (1.298, 0); -\u003e 1 exp (x) returnează valoarea e (baza logaritmilor naturali), ridicată la gradul X: MySQL\u003e Selectați Exp (2); -\u003e 7.389056 MySQL\u003e Selectați EXP (-2); -\u003e 0.135335 Log (x) Returnează logaritmul natural al X: MYSQL\u003e SELECT LOG (2); -\u003e 0. 693147 MySQL\u003e Selectați jurnalul (-2); -\u003e null pentru a obține logaritmul X pentru o bază arbitrară a logaritmilor b, trebuie să utilizați formula Jurnal (X) / Log (B). Log10 (x) returnează logaritmul zecimal al X: MySQL\u003e Selectați Log10 (2); -\u003e 0.301030 MySQL\u003e Selectați Log10 (100); -\u003e 2.000000 MySQL\u003e Selectați Log10 (-100); -\u003e puterea nulă (x, y) (x, y) returnează valoarea argumentului X, ridicată în gradul de y: MySQL\u003e Selectați Pow (2.2); -\u003e 4.000000 MySQL\u003e Selectați POW (2, -2); -\u003e 0.250000 sqrt (x) returnează o rădăcină pătrată non-negativă a X: MySQL\u003e Selectați SQRT (4); -\u003e 2.000000 mysql\u003e Selectați SQRT (20); -\u003e 4.472136 pi () returnează valoarea numărului "pi". Implicit este de 5 planuri zecimale, dar în MySQL pentru a reprezenta numărul "PI" cu calcule interne, se utilizează o dublă precizie completă. MySQL\u003e Selectați pi (); -\u003e 3.141593 MySQL\u003e Selectați pi () + 0.0000000000000000; -\u003e 3.141592653589793116 COS (X) Returnează cosinia numărului X, unde X este setat în radiani: MySQL\u003e Selectați COS (PI ()); -\u003e -1.000000 SIN (X) returnează sinusul numărului X, unde X este setat în radiani: MySQL\u003e Selectați păcatul (pi ()); -\u003e 0.000000 Tan (x) returnează tangentul numărului X, unde X este setat în radiani: MySQL\u003e Selectați Tan (PI () + 1); -\u003e 1.557408 acos (x) returnează numărul arquosinei x, adică. Cantitatea de cosinie este x. Dacă X nu este în intervalul de la -1 la 1, returnează NULL: MySQL\u003e Selectați ACOS (1); -\u003e 0.000000 MySQL\u003e Selectați ACOS (1.0001); -\u003e nul mysql\u003e selectați ACO (0); -\u003e 1.570796 Asin (x) returnează numărul de arxinus x, adică. A cărui amploare este x. Dacă X nu este în intervalul de la -1 la 1, returnează NULL: MySQL\u003e Selectați Asin (0.2); -\u003e 0.201358 MySQL\u003e Selectați Asin ("Foo"); -\u003e 0.000000 ATAN (X) returnează numărul arcthannx x, adică A căror valoare este X: MySQL\u003e Selectați Atan (2); -\u003e 1.107149 MySQL\u003e Selectați Atan (-2); -\u003e -1.107149 ATAN (Y, X) Atan2 (Y, X) returnează Arcthannxul celor două variabile X și Y. Calculul se efectuează în același mod ca și calcularea arctgentului Y / X, cu excepția faptului că semnele ambelor argumente sunt utilizate pentru a determina cvadranamentul rezultat: MySQL\u003e Selectați Atan (-2.2); -\u003e -0.785398 MySQL\u003e Selectați Atan2 (pi (), 0); -\u003e 1.570796 Pat (x) returnează cotanganța X: MySQL\u003e Selectați Pat (12); -\u003e -1.57267341 MySQL\u003e Selectați Pat (0); -\u003e RAD RAND (N) returnează o valoare la punctul de plutire aleatorie în intervalul de la 0 la 1.0. Dacă este specificat un argument între întregă, este utilizat ca valoare inițială a acestei valori: MySQL\u003e Select Rand (); -\u003e 0. 9233482386203 MySQL\u003e Select Rand (20); -\u003e 0.15888261251047 MySQL\u003e Select Rand (20); -\u003e 0.15888261251047 MySQL\u003e Select Rand (); -\u003e 0.63553050033332 MySQL\u003e Select Rand (); -\u003e 0,70100469486881 În măsura ul de comandă, nu trebuie să utilizați o coloană cu valori Rand (), deoarece aplicarea comenzii de către operator va duce la calcule multiple din această coloană. În versiunea MySQL 3.23, este posibilă, totuși, să efectuați următorul operator: Selectați * din tabel_name Comandă by (): Este utilă obținerea unei instanțe aleatorii dintr-un set de selectare * din tabelul 1, tabelul2 unde a \u003d B și C.

  • Dacă valoarea returnată este utilizată într-un context întreg (Integer) sau toate argumentele sunt întregi, atunci acestea sunt comparate ca numere întregi.
  • Dacă valoarea returnată este utilizată în contextul numerelor valide (reale) sau toate argumentele sunt numere valide, atunci acestea sunt comparate ca număr de tip real.
  • Dacă unul dintre argumente este un șir dependent de registru, atunci argumentele de date sunt comparate cu registrul.
  • În alte cazuri, argumentele sunt comparate ca liniile independente de registru. MySQL\u003e Selectați celula cel puțin (2.0); -\u003e 0 mysql\u003e selectați cel puțin (34.0.3.0,5.0.767.0); -\u003e 3.0 mysql\u003e selectați celula ("B", "A", "C"); -\u003e "A" în versiunile MySQL la 3.22.5, puteți utiliza min () în loc de cel puțin. Cel mai mare (x, y, ...) returnează cel mai înalt (cu o valoare maximă) argument. Comparația argumentelor apare în conformitate cu aceleași reguli ca cel puțin: MySQL\u003e Selectați cea mai mare (2.0); -\u003e 2 MySQL\u003e SELECT GREATEST (34.0.3.0,5.0,767.0); -\u003e 767.0 MySQL\u003e Selectați cel mai mare ("B", "A", "C"); -\u003e "C" în versiunile MySQL la 3.22.5, puteți utiliza max () în loc de cea mai mare. Grade (x) Returnează un argument X convertit din radiani în grade: MySQL\u003e aplicanților selecționați (PI ()); -\u003e 180.000000 de radiani (x) returnează argumentul x, convertit din grade la radiani: MySQL\u003e Selectează radiani (90); -\u003e 1.570796 trunchiat (x, d) returnează numărul X trunchiat la semnele zecimale D. Dacă D este 0, rezultatul va fi prezentat fără un semn zecimal sau parte fracționară: MySQL\u003e SELECT TRUNCATE (1.223.1); -\u003e 1.2 MySQL\u003e Selectați trunchia (1.999.1); -\u003e 1.9 MySQL\u003e Selectați trunchia (1.999.0); -\u003e 1 Trebuie amintit faptul că, de obicei, în computerele zecimal numere sunt stocate nu ca numere întregi, ci ca un număr de precizie dublă cu un semn zecimal plutitor (dublu). Prin urmare, uneori rezultatul poate fi înșelător, ca în exemplul următor: MySQL\u003e Selectați trunchiatul (10.28 * 100.0); -\u003e 1027 Acest lucru se întâmplă pentru că, în realitate, 10.28 este stocată ca ceva de genul 10,2799999999999999.
  • Acest articol oferă o soluție pentru optimizarea sarcinilor SQL tranzacționate pentru calcularea rămășițelor în depozite. Aplicată: Participarea tabelelor și reprezentanțelor materializate.

    Formularea problemei

    Sarcina trebuie rezolvată pe SQL Server 2014 Enterprise Edition (X64). Compania are multe depozite. În fiecare depozit în fiecare zi, câteva mii de transferuri și acceptarea produselor. Există un tabel de mișcări de bunuri în sosirea / consumul de depozit. Trebuie să implementeze:

    Calcularea soldului la data și ora selectată (până la o oră) în toate / toate depozitele pentru fiecare produs. Pentru analitice, trebuie să creați un obiect (funcție, tabel, reprezentare) cu care pentru intervalul de date selectat, cu toate depozitele și produsele, această masă sursă și o coloană calculată suplimentară - reziduul în poziția poziției.

    Aceste calcule se presupune că sunt efectuate pe un program cu diferite intervale de date și ar trebui să funcționeze într-un timp accesibil. Acestea. Dacă trebuie să se retragă din tabel cu resturi de ultima oră sau zi, timpul de execuție trebuie să fie cât mai repede posibil, precum și în cazul în care aveți nevoie să-și retragă aceleași date pentru ultimii 3 ani, pentru descărcare ulterioară analitică Bază de date.

    Detalii tehnice. Tabelul însuși:

    CREATE TABLE DBO.TURNOVER (ID INT IDENTITATE PRIMARY KEY, DT DATETIME NOT NULL, productId INT NOT NULL, STOREHOUSEID INT NOT NULL, EXPLOATARE SMALLINT NOT NULL CHECK (OPERARE IN (-1.1)), - 1 parohie la depozit, -1 Consum din cantitatea de depozit numeric (20,2) nu nul, costuri de cost nu nul)

    DT - data primirii timpului de primire / scriere a depozitului.
    Productd produs
    Magazinou - depozit
    Operațiune - 2 valori Sosire sau consum
    Cantitate - cantitatea de produs din depozit. Poate fi real dacă produsul nu este în bucăți, dar, de exemplu, în kilograme.
    Costul - costul lotului de produs.

    Sarcina de cercetare

    Creați o masă completă. Pentru ca tu să mă testați împreună cu mine și să urmăriți rezultatele rezultate, vă propun să creați și să completați un tabel DBO.Turbover cu un script:

    Dacă Object_id ("dbo.turovă", "u") nu este nulă drop tabel dbo.turver; Mergeți cu ori (Selectați 1 Uniune ID All Selectați ID + 1 din ori unde ID< 10*365*24*60 -- 10 лет * 365 дней * 24 часа * 60 минут = столько минут в 10 лет) , storehouse as (select 1 id union all select id+1 from storehouse where id < 100 -- количество складов) select identity(int,1,1) id, dateadd(minute, t.id, convert(datetime,"20060101",120)) dt, 1+abs(convert(int,convert(binary(4),newid()))%1000) ProductID, -- 1000 - количество разных продуктов s.id StorehouseID, case when abs(convert(int,convert(binary(4),newid()))%3) in (0,1) then 1 else -1 end Operation, -- какой то приход и расход, из случайных сделаем из 3х вариантов 2 приход 1 расход 1+abs(convert(int,convert(binary(4),newid()))%100) Quantity into dbo.Turnover from times t cross join storehouse s option(maxrecursion 0); go --- 15 min alter table dbo.Turnover alter column id int not null go alter table dbo.Turnover add constraint pk_turnover primary key (id) with(data_compression=page) go -- 6 min
    Am acest script pe PC cu un disc SSD, aproximativ 22 de minute, iar dimensiunea tabelului a durat aproximativ 8 GB pe hard disk. Puteți reduce numărul de ani, și numărul de depozite, în scopul de a reduce timpul pentru crearea și completarea tabelului. Dar vă recomand să lăsați un volum bun pentru a estima planurile de solicitare, cel puțin 1-2 gigaocteți.

    Grupate date până la o oră

    Mai mult, trebuie să grupăm cantități de produse în stoc pentru perioada de studiu de timp, în formularea noastră de sarcini este de o oră (puteți până la un minut, până la 15 minute, zi. Dar este evident pentru milisecunde, este puțin probabil că oricine va avea nevoie de raportare). Pentru comparații din sesiune (fereastră) în cazul în care îndeplinim cererile noastre de a executa timpul de statistică a comenzii;. Apoi, efectuăm solicitările în sine și vedeți planurile de interogare:

    Selectați Top (1000) Conversie (DateTime, Convertizor (Varchar (13), DT, 120) + ": 00", 120) ca dt, rotund până la o oră product, depozit, suma (operație * cantitate) ca cantitate de la dbo .Turnover Grup de Convertire (DateTime, Convert (Varchar (13), DT, 120) + ": 00", 120), ProductId, Magazinou

    Costul de solicitare - 12406
    (Rânduri prelucrate: 1000)
    SQL Server de lucru ore:
    Timpul CPU \u003d 2096594 MS, Timpul petrecut \u003d 321797 ms.

    Dacă facem o cerere rezultată cu un echilibru, care este considerat un rezultat tot mai mare al cantității noastre, planul de cerere și interogare va fi după cum urmează:

    SELECT TOP (1000) Conversia (DateTime, Conversie (VARCHAR (13), DT, 120) + ": 00", 120) DT, până în jurul la o oră productId StoreHouseID, USM (Operațiunea * Cantitate) Ca Cantitate, Sum (SUM (EXPLOATAREA * Cantitate) Peste (partiţie de StorehouseID, productId Comandă de Conversie (DateTime, Conversie (varchar (13), DT, 120) + ": 00", 120)) balance de la DBO.Turnover Grup de Conversie ( Datetime, Convertizor (Varchar (13), DT, 120) + ": 00", 120), ProductId, depozit


    Costul de solicitare - 19329
    (Rânduri prelucrate: 1000)
    SQL Server de lucru ore:
    Timpul CPU \u003d 2413155 ms, petrecut timp \u003d 344631 ms.

    Gruparea optimizării

    Este destul de simplu aici. Cererea însăși fără un rezultat în creștere poate fi optimizată printr-o reprezentare materializată (vizualizare index). Pentru a construi o viziune materializată, faptul că este rezumat nu ar trebui să aibă o valoare nulă, suntem rezumați cu suma (funcționare * cantitate) sau fiecare câmp nu este nul sau adăugați islull / coalesce într-o expresie. Propun să creez o reprezentare materializată.

    Creați Vizualizare DBO.TURVERVEHOUR cu SchemaBinding ca Selectare Convert (DateTime, Convertizor (Varchar (13), DT, 120) + ": 00", 120) ca dt, - rotunde până la o oră product, depozite, suma (isnull (operație * Cantitate, 0)) ca Cantitate, COUNT_BIG (*) Cantitate de la DBO.Turnover Group de Conversie (DateTime, Conversie (varchar (13), DT, 120) + ": 00", 120), productId StoreHouseID Du - te
    Și construiți un indice de cluster pe ea. În index, indică faptul că câmpurile în același mod ca și în grupare (pentru gruparea atât de multă ordine nu este importantă, este important ca toate domeniile de grup să fie în index) și un rezultat din ce în ce mai mare (o comandă este importantă aici - mai întâi ce în partiție de, atunci ce în partiție de ORDER BY):

    Creați un indice cluster unic UIX_TURNONHOUR pe DBO.Turnornorhour (Magazinul, Productid, DT) cu (Data_Compression \u003d Pagina) - 19 min

    Acum, după construirea unui indice de cluster, putem redefla cererile prin schimbarea sumei Agregare ca în vizualizare:

    Selectați Top (1000) Convertiți (DateTime, Convertizor (Varchar (13), DT, 120) + ": 00", 120) ca dt, rotund până la o oră product, depozit, sumă (isNull (operație * Cantitate, 0) ) AS Cantitatea de DBO.Turnover Grupa de Conversie (DateTime, Conversie (varchar (13), DT, 120) + ": 00", 120), productId StoreHouseID SELECT TOP (1000) Conversia (DateTime, Conversie (varchar (13 ), Dt, 120) + ": 00", 120) ca dt, - rotund până la o oră product, depozit, sumă (isNull (operație * cantitate, 0)) ca cantitate, sumă (Suma (Operațiunea * Cantitate , 0))) Peste (Partiția de StoreHouseID, productId Comandă de Conversie (DateTime, Conversie (varchar (13), DT, 120) + ": 00", 120)) AS BALANCE Din grupa DBO.TurnOr de Conversie (DateTime, Conversia (varchar (13), DT, 120) + ": 00", 120), ProductID, StoreHouseID

    Planurile de solicitare au fost:

    Costul 0.008.

    Costul 0.01.

    SQL Server de lucru ore:
    Timp cp \u003d 31 ms, petrecut timp \u003d 116 ms.
    (Rânduri prelucrate: 1000)
    SQL Server de lucru ore:
    Timp cp \u003d 0 ms, petrecut timp \u003d 151 ms.

    Total, vom vedea că, cu un aspect indexată, interogarea scanează tabelul de date de grupare, dar un indice de cluster în care totul este deja grupat. În consecință, timpul de execuție a scăzut de la 321797 milisecunde la 116 ms., Adică. 2774 ori.

    Ar fi posibil să se termine optimizarea noastră, dacă nu ar fi fost pentru faptul că avem nevoie de multe ori întregul tabel (vizualizare) și partea sa pentru intervalul selectat.

    Echilibru intermediar

    Ca rezultat, trebuie să executăm rapid următoarea interogare:

    Set Dateformat YMD; Declare datetime \u003d @ Porniți "2015-01-02", datetime @finish \u003d "2015-01-03" Select * de la (Selectați DT, StorehouseID, productId Cantitate, Suma (Cantitate) Peste (Partiție de Storehouseid, productId Prin Ordinul Dt) ca echilibru de la dbo.turnverour cu (noxpand) unde dt<= @finish) as tmp where dt >\u003d @start.


    Costul planului \u003d 3103. Și imaginați-vă ce s-ar întâmpla dacă nu ar fi fost pentru reprezentarea materializată, a mers de-a lungul tabelului în sine.

    Rezultatul prezentării și echilibrului materializat pe fiecare produs în stoc la data cu timpul rotunjit la o oră. Pentru a calcula echilibrul - este necesar de la început (de la echilibrul zero) pentru a rezuma întregul număr înainte de ultima dată (@finish), și după deja într-o preluare sumator, tăiați datele mai târziu decât parametrul de pornire .

    Acesta va ajuta în mod evident soldurile calculate intermediare. De exemplu, pe numărul 1E din fiecare lună sau pentru fiecare duminică. Având astfel de solduri, sarcina este de a coborî la faptul că va trebui să rezumați balanțele calculate anterior și să calculați soldul nu de la început, ci de la ultima dată calculată. Pentru experimentele și comparațiile, vom construi un index suplimentar nu cluster după dată:

    Crearea indexului IX_DT pe DBO.TURBOVEROUR (DT) include (Cantitate) cu (Data_Compression \u003d Pagina); - 7 min și cererea noastră va fi tipul: set formatat YMD; declarare @Start DataTime \u003d "2015-01-02", @Finish Datetime \u003d "2015-01-03" Declaraþi @Start_month DataTime \u003d Conversie (DateTime, Convert (Varchar (9), @ Start, 120) + "1", . \u003e
    În general, această interogare care are chiar și indicele după dată care acoperă complet toate câmpurile afectate, va selecta grupul Indicele și scanarea. Și nu căutați după dată cu sortarea ulterioară. Propun să efectuez următoarele 2 cereri și să comparăm ceea ce am făcut, apoi să analizăm că este mai bine:

    Set Dateformat YMD; declarare @Start DataTime \u003d "2015-01-02", @Finish Datetime \u003d "2015-01-03" Declaraþi @Start_month DataTime \u003d Conversie (DateTime, Convert (Varchar (9), @ Start, 120) + "1", . \u003e \u003d @ @Start de magazine, productId, dt Selectați * de la (selectați DT, Magazinați, ProductId, Cantitate, Sumă (cantitate) peste (partiție by depozite, comanda produsizată de DT) ca echilibru de la dbo.Turnorhour cu (noxpand, index \u003d ix_dt) unde dt între @Start_month și @finish) ca TMP unde DT\u003e \u003d @Start Comandă de magazine, productId, dt

    SQL Server de lucru ore:
    Timp cp \u003d 33860 ms, timpul petrecut \u003d 24247 ms.

    (Rânduri prelucrate: 145608)

    (Rânduri prelucrate: 1)

    SQL Server de lucru ore:
    Timpul CPU \u003d 6374 ms, timpul petrecut \u003d 1718 ms.
    TIMP CP \u003d 0 MS, Timp expirat \u003d 0 ms.


    Din timp, este clar că indicele după dată se face mult mai rapid. Dar planurile de interogare în comparație arată astfel:

    Costul cererii 1 cu indicele de cluster selectat automat \u003d 2752, dar valoarea cu indicele de la data interogării \u003d 3119.

    Așa cum a fost, nu au existat două sarcini din indexul aici: gama de sortare și eșantionare. Un indice al acestei sarcini pe care îl putem decide. În acest exemplu, datele de date în doar o zi, dar dacă există o perioadă mai mare, dar nu toți, de exemplu, timp de 2 luni, atunci căutarea definitivă a indicelui nu va fi eficientă din cauza costurilor de sortare.

    Aici, de la soluții optime vizibile, văd:

    1. Creați un câmp de lună calculat și creați un index (pe lună, câmpurile rămase ale indicelui cluster). În cazul în care DT între @Start_month și finisaj înlocuiește pentru anul de ani [E-mail protejat]lună și după aceea aplicați deja un filtru la datele dorite.
    2. Indicii filtrați - indexul în sine ca cluster, dar filtrul după dată, pentru luna dorită. Și astfel de indici fac cât avem toate lunile. Ideea este aproape de soluție, dar aici dacă gama de condiții este de la 2 indici filtrați, conexiunea va fi necesară și sortarea este încă inevitabilă.
    3. Noi, un indice de cluster, astfel încât în \u200b\u200bfiecare secțiune datele numai într-o lună.
    În proiect, ca rezultat, am făcut cea de-a treia opțiune. Împărțirea indicelui clusterului unei prezentări materializate. Și dacă eșantionul trece în intervalul de timp de o lună, în esență, optimizatorul afectează doar o singură secțiune, făcând scanarea fără sortare. O tăiere a datelor neutilizate are loc la nivelul de întrerupere a secțiunilor neutilizate. Aici, dacă o căutare de la 10 la 20, nu avem o căutare exactă a acestor date și căutarea datelor de la 1 până în ultima zi a lunii, apoi scanarea acestui interval în indicele sortat cu filtrarea în timpul Datele sunt scanate.

    SECȚIUNEA CONDUCERILOR CLUERILOR. Mai întâi de toate, eliminăm toți indexurile din role:

    Picătură index ix_dt pe dbo.turnverhour; Picătură index uix_turnverour pe dbo.turnverhour;
    Și creați o schemă de funcționare și de partiționare:

    Set Dateformat YMD; Creați funcția de partime PF_TURVERVEHOUR (DateTime) ca Drept Range pentru valori ("2006-01-01", "2006-02-01", "2006-03-01", "2006-04-01", "2006-05- 01 "," 2006-07-01 "," 2006-08-01 "," 2006-09-01 "," 2006-10-01 "," 2006-11-01 " , "2006-12-01", "2007-02-01", "2007-03-01", "2007-04-01", "2007-05-01", " 2007-06-01 "," 2007-08-01 "," 2007-09-01 "," 2007-10-01 "," 2007-11-01 "," 2007- 12-01 "," 2008-02-01 "," 2008-03-01 "," 2008-05-01 ", 2008-06 01 "," 2008-10-01 "," 2008-10-01 "," 2008-10-01 "," 2008-11-01 "," 2008-12-01 ", "2009-01-01", "2009-03-01", 2009-04-01 ", 2009-05-01", 2009-06-01 "," 2009 -07-01 "," 2009-09-01 "," 2009-10-01 "," 2009-11-01 "," 2009-12-01 ", 2010- 01 -01 "," 2010-02-01 ", 2010-03-01", 2010-04-01 ", 2010-06-01", 2010-07-01 "," 2010-08-01 "," 2010-09-01 ", 2010-11-01", "2010-12-01", "2011-01-01", "2011-02-01", 2011-03-01 ", 2011-04-01", 2011-05-01 ", 2011-06-01 "," 2011-07-01 "," 2011-08-01 ", 2011-09-01", 2011-10-01 ", 2011-11-01", 2011-12-01 ", "2012-01-01", "2012-03-01", "2012-04-01", 2012-05-01 ", 2012-06-01", 2012 -07-01 "," 2012-09-01 "," 2012-10-01 "," 2012-11-01 "," 2012-12-01 "," 2013-01 -01 "," 2013-03-01 "," 2013-03-01 "," 2013-05-01 "," 2013-06-01 "," 2013-07-01 "," 2013-10-01 "," 2013-11-01 "," 2013-12-01 "," 2013-01-01 ", "2014-02-01", "2014-03-01", 2014-04-01 ", 2014-06-01", 2014-07-01 ", 2014 -08-01 "," 2014-10-01 "," 2014-11-01 "," 2014-12-01 ", 2015-01-01", 2015-02 -01 "," 2015-03-01 "," 2015-05-01 ", 2015-06-01", 2015-07-01 ", 2015-08-01 ", 2015-12-01", "2015-11-01", "2015-12-01", "2016-01-01", "2016-02-01", "2016-03-01", 2016-04-01 ", 2016-05-01", 2016-06-01 ", 2016-07-01", 2016-08-01 ", 2016 -09-01 "," 2016-10-01 "," 2016-11-01 "," 2016-12-01 ", 2017-01-01", 2017-02-01 ", 2017-03 -01 "," 2017-04-01 "," 2017-05-01 "," 20 17-06-01 "," 2017-08-01 "," 2017-09-01 "," 2017-10-01 ", 2017-11-01", 2017- 12-01 "," 2018-02-01 "," 2018-03-01 "," 2018-04-01 ", 2018-05-01", 2018-06- 01 "," 2018-08-01 "," 2018-10-01 "," 2018-11-01 "," 2018-12-01 " , "2019-03-01", "2019-03-01", "2019-04-01", "2019-05-01", "2019-06-01", " 2019-07-01 "," 2019-09-01 "," 2019-10-01 "," 2019-11-01 "," 2019-12-01 "); Du-te crea schema de partiție ps_turnonorhour ca partiție pf_turnonorhour toate la (); Mergeți bine, indicele de cluster deja cunoscut numai în schema de partiționare creată: Creați un indice unic clustered uix_turnornorhour pe dbo. Turnishour (Magazin, Productid, DT) cu (Data_Compression \u003d Page) pe PS_Turnornorhour (DT); - - 19 min și acum să vedem ce sa întâmplat. Cereți-vă: setați Dateformat YMD; declarare @Start DataTime \u003d "2015-01-02", @Finish Datetime \u003d "2015-01-03" Declaraþi @Start_month DataTime \u003d Conversie (DateTime, Convert (Varchar (9), @ Start, 120) + "1", . \u003e \u003d Comanda @start by depozit, productid, opțiune dt (recompilare);


    SQL Server de lucru ore:
    Timpul CPU \u003d 7860 ms, petrecut timp \u003d 1725 ms.
    Timp de analiză sintactică și compilație SQL Server:
    TIMP CP \u003d 0 MS, Timp expirat \u003d 0 ms.
    Costul planului de interogare \u003d 9.4

    De fapt, datele din aceeași secțiune sunt selectate și scanate în funcție de indicele de cluster destul de repede. Aici trebuie adăugat aici că atunci când cererea este parametrizată, apare efectul de snifire a parametrilor neplăcut, opțiunea (recompilul) este tratată.

    SQL 2003 Sintaxă acceptă toate platformele.

    Podea (expresie)

    Dacă transmiteți un număr pozitiv la funcție, funcția va fi capabilă să elimine tot ceea ce stă după punctul zecimal.

    Selectați podeaua (100.1) de la Dual;

    Cu toate acestea, amintiți-vă că, în cazul numerelor negative, rotunjirea la o parte mai mică corespunde unei creșteri a valorii absolute.

    Selectați podeaua (-100.1) de la Dual;

    Pentru a obține efectul opus acțiunii funcției de podea, utilizați funcția de la plail.

    Ln

    Funcția LN returnează logaritmul natural al numărului, adică măsura în care constanta matematică E (aproximativ 2.718281) este obținerea unui număr dat ca rezultat.

    SQL 2003 Sintaxă.

    LN (expresie)

    DB2, Oracle, Postgresql

    Platformele DB2, Oracle și PostgreSQL sunt acceptate pentru sintaxa SQL 2003. Caracteristica DB2 și PostgreSQL acceptă și funcția de jurnal ca Synonym LN.

    MySQL și SQL Server

    Serverul MySQL și SQL are propria sa funcție pentru a calcula logaritmul natural - jurnalul.

    Log (expresie)

    În exemplul următor, Oracle este calculată de logaritmul natural al numărului, aproximativ egal cu constanta matematică.

    Selectați LN (2.718281) de la Dual;

    Pentru a efectua operația opusă, utilizați funcția EXER.

    Mod.

    Funcția mod returnează echilibrul de împărțirea divizării într-un divizor. Toate platformele suportă sintaxa SQL 2003 MOD standard 2003.

    SQL 2003 Sintaxă.

    Mod (divizibil, divizor)

    Funcția Mod standard este proiectată pentru a obține un echilibru de la împărțirea divizării într-un divizor. Dacă divizorul este zero, atunci Delimi este returnat.

    Mai jos este afișat modul în care puteți utiliza funcția mod în instrucțiunea SELECT.

    Selectați mod (12, 5) din numerele 2;

    Poziţie

    Funcția de poziție returnează un număr întreg indicând poziția inițială a liniei în bara de căutare.

    SQL 2003 Sintaxă.

    Poziție (linia 1 în linie22)

    Funcția de poziție standard este concepută pentru a obține poziția primei intrare a șirului specificat (șir) în bara de căutare (șir!). Funcția returnează 0 în cazul în care șirul! Nu apare în linie!, Și null - dacă vreunul dintre argumente este nul.

    DB2.

    DB2 are o funcție de posstr echivalentă.

    Mysql.

    Platforma MySQL acceptă funcția de poziție în conformitate cu standardul SQL 2003.

    Putere

    Funcția de alimentare este utilizată pentru a ridica numărul la gradul specificat.

    SQL 2003 Sintaxă.

    Putere (bază, indicator)

    Rezultatul acestei funcții este fundația ridicată într-o măsură determinată de indicator. Dacă baza este negativă, indicatorul trebuie să fie un număr întreg.

    DB2, Oracle, Postgreql și SQL Server

    Toți acești producători suportă sintaxa SQL 2003.

    Oracol

    Oracle are o funcție echivalentă a instalurilor.

    Postgresql.

    Platforma PostgreSQL acceptă funcția de poziție în conformitate cu standardul SQL 2003.

    Mysql.

    Platforma MySQL sprijină această funcție, nu acest cuvânt cheie.

    P0W (bază, indicator)

    Construcția unui număr pozitiv este destul de evidentă.

    Selectați puterea (10.3) de la Dual;

    Orice număr ridicat într-un grad de 0 este egal cu 1.

    Selectați puterea (0.0) de la Dual;

    Indicatorul negativ schimbă punctul zecimal spre stânga.

    Selectați puterea (10, -3) de la Dual;

    Fel.

    Funcția SQRT returnează rădăcina pătrată a numărului.

    SQL 2003 Sintaxă.

    Toate platformele suportă sintaxa SQL 2003.

    Sortare (expresie)

    Selectați SQRT (100) de la Dual;

    Lățimea găleată.

    Caracteristica cupă de lățime atribuie valori ale coloanelor histogramei egale.

    SQL 2003 Sintaxă.

    În următoarea sintaxă, expresia este o valoare care i se atribuie o coloană de histogramă. De regulă, expresia se bazează pe una sau mai multe coloane ale mesei returnate de cerere.

    Lățimea găleată (expresie, min, max, columns_gigograma)

    Parametrul coloanei histograme arată numărul de coloane de histogramă create în intervalul de valori min la max. Valoarea parametrului min este pornită în intervalul, iar valoarea parametrului MAX nu pornește. Valoarea expresiei este atribuită uneia dintre coloanele histogramei, după care funcția returnează numărul coloanei histograme corespunzătoare. Dacă expresia nu se încadrează în intervalul de coloane specificate, funcția returnează 0 sau max + 1, în funcție de faptul dacă expresia este mai mică de min sau mare sau egală cu max.

    În exemplul următor, valorile întregi de la 1 la 10 sunt distribuite între două coloane ale histogramei.

    Următorul exemplu este mai interesant. 11 Valorile de la 1 la 10 sunt distribuite între cele trei coloane ale histogramei pentru a ilustra diferența dintre valoarea min, care este pornită în intervalul și valoarea maximă, care nu este pornită în intervalul.

    Selectați X, Width_Bucket (x, 1.10.3) de la pivot;

    Acordați o atenție deosebită rezultatelor CX \u003d, X \u003d 9.9 și X-10. Valoarea de intrare a parametrului de către aceștia, adică în acest exemplu - 1, se încadrează în prima coloană, indicând limita inferioară a intervalului , deoarece numărul coloanei 1 este definit ca x\u003e \u003d min. Cu toate acestea, valoarea de intrare a parametrului MAX nu este inclusă în coloană cu valori maxime. În acest exemplu, numărul 10 intră în coloana de depășire, cu numărul maxim + 1. Valoarea de 9,9 intră în coloana numărul 3 și aceasta ilustrează regula conform căreia limita superioară a intervalului este definită ca x< max.