Mi-am amintit ceva, cand incepeam acest prim articol pe blog. Mai demult nu pierdeam nici un episod din South Park. Ma amuzau episoadele, chiar daca uneori producatorii treceau de o anumita limita a bunului simt. Era un episod in care spuneau “Simpsons Already Did It”. Astfel pornesc si eu cu acest articol despre SQL mentionand ca undeva, candva, cineva a mai tratat aceasta problema, dar eu macar ofer 2 alternative sau una mai serioasa.
Recent a trebuit sa sortez continutul unei baze de date pentru un client. El detinea in acea baza de date un numar de item-uri care aveau un ID unic, dar nu neaparat in ordine crescatoare. Bineinteles ca se poate sorta in ordine crescatoare orice continut din DB daca este sa interogam DB cu:
SELECT [coloane] FROM [tabela] WHERE [clauza] ORDER BY [coloana] ASC|DESC;
Eventual adaugam si un TOP, daca ne intereseaza sa selectam un anumit numar de linii/itemuri.
Ce facem cand sunt linii neordonate?
Clientul nostru beneficiaza de puterea pusa la dispozitie de oferta S+S a Dev-Vision. Astfel, el utilizeaza un client software instalat local, care ii permite sa updateze continutul bazei de date. Nu este nici primul nici ultimul client care prefera aceasta metoda, asa ca face parte din oferta noastra. Clientul nostru utilizeaza aplicatia ca sa adauge (INSERT) item-uri in DB, sa updateze (UPDATE) si sa stearga itemi din DB (DELETE). Ce se intampla cand un item este sters?
Dupa cum stiti, cand un item este sters din DB, baza de date pastreaza pozitia itemului si alt item adaugat in DB se va interpune exact in pozitia vechiului item, care a fost sters.
Daca avem o tabela de genul:
[id] |
[item] |
3654 | Itemul 1 |
100 | Itemul 2 |
1587 | Itemul 3 |
Daca stergem itemul cu [id] = 100 (albastru) si inseram un alt item, indiferent cu ce ID, ajungem sa avem acel item in pozita a 2-a.
Ar ajuta un TRUNCATE al tabelei si refacerea/rescrierea tuturor itemurilor. Astfel serverul Microsoft SQL stie sa reseteze valorile index pe care le tine si totul ar parea OK… pana la urmatoarea stergere, cand valoarea indexului din pozitia item-ului sters este pastrata pentru un INSERT.
Un truncate pe DB se face cu interogarea de mai jos, dar atentie, similar instructiunii DELETE, TRUNCATE sterge continutul tabelei, fiind insa mai rapid si fara sa necesite o clauza WHERE, resetand totodata indexul coloanelor la cel prestabilit sau la cel implicit (1).
TRUNCATE TABLE [tabela];
Varianta optima imposibila
Initial m-am gandit sa fac o interogare si sa generez un numar de linie pentru fiecare linie in parte, utilizand urmatoarea sintaxa:
1: WITH rezultate AS (SELECT row_number() OVER (ORDER BY [coloana] DESC) AS ROW,
2: [coloana2], [coloana3] FROM [tabela] WHERE [clauza1] AND [clauza2]
3: UNION SELECT row_number() OVER(ORDER BY [coloana] DESC) AS ROW,
4: [coloana], [coloana2] FROM [tabela] WHERE [clauza1] AND [clauza2])
5: SELECT * FROM rezultate WHERE ROW BETWEEN x AND y ORDER BY [coloana] DESC;
De fapt interogarea era mult mai lunga si mult mai interesanta, cu coloane si alte verificari si clauze, dar am preferat sa pastrez cat mai putin necesar si sa nu dau interogarea utlizata din motive de NDA.
Aceasta interogare imi returna foarte frumos un tabel in care imi era inserata inca o coloana (sa o numim temporara), pe care o utilizam ca sa sortez item-urile si sa pastrez doar pe cele care ma interesau. Era ceva similar LIMIT-ului din MySQL.
[id] |
[item] |
[ordonare] |
3654 | Itemul 1 | 1 |
100 | Itemul 2 | 2 |
1587 | Itemul 3 | 3 |
Acum totul pare sa fie bine, nu? Pai… nu! Deoarece, dupa cum spuneam si mai sus, valorile index sunt pastrate la DELETE si cum clientul nostru utiliza aplicatia de update a DB si pentru a face DELETE… aceasta varianta (desi interogarea arata chiar bine), nu o prea putem utiliza.
Varianta aleasa
In ultima instanta si datorita necesitatilor proiectului, dupa ce m-am consultat cu colegi si alti developeri, am ajuns la concluzia ca mai bine pastram un index propriu intr-o alta coloana a tabelei. Astfel ne-am ales cu inca o coloana [index] pe care o utilizam pentru gestionarea indexului propriu, paralel cu cel la SQL si fara a afecta cel al SQL, fiind independent de acesta.
In aceasta coloana utilizam mereu un index calculat dupa urmatoarea formula:
val_index = (SELECT TOP 1 [index] FROM [tabela] ORDER BY [index] DESC) + 1
De aici reiese ca mereu indexul va avea lipsuri, atunci cand clientul decide ca anumitor linii din tabela le este mai bine lasate in afara bazei de date, dar macar acum putem selecta bazandu-ne pe acest index propriu.
Concluzie
Stiu ca sunteti unii dintre cititorii acestui blog care vor comenta ca nu este varianta optima si ca de ce procedam asa, dar din pacate aceasta este singura varianta viabila pentru baza de date a clientului, cunoscand necesitatile clientului si ale bazei de date.
Daca totusi aveti o alternativa care sa corespunda celor scrise mai sus (atentie insa ca sus nu am mentionat si alte cerinte necesare clientului meu pentru aplicatia sa web), sunteti invitati sa comentati.
Numai bine,