otrdiena, 2011. gada 19. jūlijs

Nejaušu datu izgūšana no tabulas

Rakstā par to, kā iegūt nejaušus (vai, precīzāk, apmēram nejaušus) datus no tabulas.

Top
Ja grib redzēt, kas apmēram ir tabulā, tad visdrīzāk pirmā doma ir izmanot TOP, piemēram:
Select Top 10 * From dbo.SomeTable
Lielākajā vairumā gadījumu ar to arī ir gana. Tomēr dati, ko atgriež šis vaicājums, ne tuvu nav "nejauši". Tiesa gan- īsti paredzēt rezultātu vaicājumam nevar, bet nojaust ar lielu pārliecības pakāpi gan var- SQL Server atgriezīs tos datus, kas viņam būs vistuvāk pa rokai- visdrīzāk tos, kas ir klasterētā indeksa sākumā. Ja tāda nav, tad tos, kas pirmie ievietoti datu tabulā. Lai nu kā- rezultāts nav garantēti paredzams, tomēr nejaušs arī tas nav.

Top un Order By NewID()
[pievientos 2011-12-15]
Šis ir vēl viens risinājums, kas gan nav pārāk ātrs, bet strādā diezgan glīti. Ideja risinājumam ir tāda, ka dati tiek kāroti pēc NewID() funkcijas rezultāta- ģenerēta unikālā identifikatora. izskatās apmēram tā:
Select Top 10 * From dbo.SomeTable Order By NewID()
Trūkums metodei ir tāds, ka jānolasa viss datu kopa, kas atbilst kritērijiem un jāveic kārtošana.
[pievientos 2012-02-28]
Iepriekšējo metodi var uzlabot, ja tabula ir ļoti liela un atlasāmā datu kopa- salīdzinoši ļoti maza (atvainojos par neizmērāmiem lielumiem, bet ideja- ja klasterētā indeksa nolasīšana neatmaksājas. To var matemātiski izrēķināt) var izmantot modifikāciju no iepriekšējā, izvairoties no tabulas skanēšanas:
Select *
From dbo.SomeTable Order By NewID()
Where SomeTableID In
(
    Select Top 10 SomeTableID
    From dbo.SomeTable
    Order By NewID()
Izskatās nevajadzīgi? Patiesībā nē- šajā gadījumā vaicājums var izmantot mazāko indeksu, līdz ar to veicot daudz mazāk nolasīšanas operāciju kā iepriekšējā gadījumā. Manā testā atšķirība:
1. variants: Table 'SomeTable'. Scan count 1, logical reads 26255
2. variants: Table 'SomeTable'. Scan count 1, logical reads 4945
Un vēl - idejas līmenī (neizveidoju reālu piemēru) var izmantot šo un šo rakstu apvienojumu. Doma- ģenerēt daudz brīvi izvēlētas vērtības noteiktā intervālā un atlasīt pēc identifikatoriem rindas. Tādā veidā var izvairīties no tabulas pilnīgas nolasīšanas (ja tabula milzīga, bet vajag dažas vērtības, tas varētu būt efektīvi), bet ierobežojumi- ja rindu identifikatori ir ar lieliem caurumiem, tad šis nebūs efektīvi. Pie tam, primārai atslēgai būtu jābūt skaitlim.

TableSample
Daudz nejaušākus datus (kas vienalga ir "nejauši" tikai līdz zināmai robežai) var iegūt izmantojot TableSample. Ar šīs klauzas palīdzību tiek atgriezti parauga dati no tabulas. Piemēram:
Select * From dbo.SomeTable TableSample (100 Rows)
Vai (ja datus vajag varēt ielasīt atkārtoti, ar garantiju strādā tikai tik ilgi, kamēr tabula netiek mainīta. 103 ir manis nejauši izvēlēts skaitlis, tur var norādīt arī jebkuru citu):
Select * From dbo.SomeTable TableSample (15 Percent) Repeatable(103)
Norādītais atgriežamo rindu skaits / procenti ir aptuveni. Rezultāti var atšķirties (atkal "var atšķirties", kas nav gluži tas pats, kas "atšķirsies") izpildot to pašu vaicājumu bez Repeatable klauzas vairākas reizes- gan datu (kas liekas pavisam loģiski), gan atgriezto rindu skaita (kas sākotnēji šķiet neloģiski) ziņā.

Tas ir tāpēc, ka atgriezti dati ir "nejauši" datu lapu, nevis datu rindu līmenī (Datu glabāšana SQL Server). Datu lapas visdrīzāk nesaturēs precīzu rindu skaitu (varbūt kāda rinda ir izdzēsta, varbūt mainīga garuma datu rindas, varbūt datu lapa vienkārši nav aizpildīta).
Aprēķins ir relatīvi vienkāršs- zināms cik % datu vajag, zināms cik datu lapu ir tabulai, atlasam datus no X nejauši izvēlētām datu lapām.
No tā var arī izdarīt secinājumu- jo vairāk datu ir, jo labāk strādās TableSample.

Citi varianti
MSDN rakstā (TableSample) ir variants, kā iegūt nejaušus datus datu rindu līmenī. Tiek izmantota relatīvi sarežģīta matemātika, ko, vismaz virspusēji apskatot, nav viegli saprast.
Tāpēc idejiski- varbūt var to visu padarīt vienkāršāku un atlasīt, piemēram, katru 10, 100 (skaitļi no gaisa parauti) rindu? Izmantojot to pašu analītisko funkciju Row_Number() (Piemērs: Vaicājums izmantojot analītisko funkciju Row_Number).
Tas varētu izskatīties aptuveni šādi:

With X as
(
    Select Row_Number() Over (Order By RindasID) nr, * From dbo.SomeTable
)
Select * From X Where nr % 50 = 0
Protams, šajā gadījumā dati pavisam noteikti nav nejauši, ja ar to saprot izvēlēto datu paredzamību. Tomēr priekštatu par apskatāmo datu tabulu, manuprāt, šādam vaicājumam vajadzētu sniegt.
Galvenais neielikt vaicājumā Top, pareizi izvēlēties cik rindas atgriezt (variējot ar piemērā norādīto skaitli 50, kas vaicājumā nozīmē "atgriezt katru 50-to rindu").

Nav komentāru:

Komentāra publicēšana