Tas ko vēlos panākt, ir pēc iespējas efektīvāks veids, kā panākt teksta virknes izveidi. Nepieciešamais rezultāts ir šāds:
Iespējamie risinājumi:
- Funkcija, kuras iekšienē ir kursors
- Funkcija, kuras iekšienē ir izmantots "viltotais kursors" (plašāk SQL Server kursors bez kursora)
- CLR agregātfunkcija (izmantota: CLR agregātfunkcijas piemērs)
- FOR XML (ideja no raksta)
rezultāti izskatās tā (pirmie bez indeksa, otrie ar indeksu uz detaļu tabulas kolonnas TabGalvaID):
Rezultāti "noķerti" ar Profiler. Godīgi sakot mazliet pārsteidz CLR agregātfunkcijas pārrākums Read operāciju ziņā, tomēr nevaru atrast nevienu iemeslu piesieties skaitļiem- visi trīs pārējie risinājumi katru reizi meklē detaļu tabulā atbilstošās rindas. Savukārt CLR gadījumā gan galvas gan detaļu tabula tiek nolasīta tikai vienu reizi!
[
Neizturēju un uztaisīju vēl aprēķinu- cik lapu teorētiski jānolasa gadījumā, ja detaļu tabulai ir indeks uz TabGalvaID. Aprēķinā izmantots tas, ko var izlasīt rakstos Indeksi (non-clustered indexes) un Informācijas par indeksiem apskatīšana.
Sanāk, katrai galvas rindai, detaļu tabulā tiks pārmeklēts neklasterētais indekss, tātad:
100 000 * 3 (3 ir neklasterētā indeksa dziļums- tātad, lai atrastu indeksa datu lapas vajag nolasīt 3 lapas). Vidēji vienai atrastajai vērtībai indeksā būs 4.5 vērtības (1 galvas tabulas ierakstam vidēji 4.5 detaļu rindas), lai līdz klasterētā indeksa datu lapai nonāktu, jāpārmeklē klasterētais detaļu tabulas indekss, kura dziļums arī ir 3, tātad- 4.5 * 3 * 100 000. aprēķinā galvas tabulu maz maina, jo tā ir relatīvi maza- 445 lapas.
Kopā sanāk:
3 * 100 000 + 4.5 * 3 * 100 000 + 445 = 1 650 445
Tas savukārt sanāk ļoti, ļoti tuvu tam, kā strādā "viltotais kursors".
Jautājums uz kuru vēl būtu jāatbild- kāpēc īstajam kursoram vajadzēja vēl 2x vairāk kā šis teorētiskais aprēķins.
]
Un kā redzams, CLR konkrētajā gadījumā sanāk labākais risinājums.
Starp citu- minētajā rakstā arī ātrdarbības salīdzinājumi, tomēr tur
* nav aprakstīta CLR agregātfunkcija
* nav galvas-detaļu tabulas, kas maina ideju
(tāpēc pamēģināju pats).
Testa tabulu sagatavošana
Tiesa gan, 100 000 rindu galvas tabulā ir mazliet par daudz:
create table dbo.TabGalvaKursors
(
TabGalvaID int primary key identity,
Teksts nvarchar(30)
)
Go
create table dbo.TabDetalas
(
TabDetalasID int primary key identity,
TabGalvaID int,
Saisinajums nvarchar(5)
)
Go
Set NoCount On
Declare @arejais int
Declare @id int
Declare @rSkaits int
Set @Arejais = 0;
While @arejais < 100000 -- 100 000 galvas rindiņu un X detaļu rindiņu
Begin
Insert Into dbo.TabGalva(Teksts)
Values (Replicate('x', Cast(Rand()*20 as int)))
Set @id = @@Identity
Set @rSkaits = Cast(Rand()*10 as int)
While @rSkaits > 0
Begin
Insert Into dbo.TabDetalas(TabGalvaID, Saisinajums)
Values (@id, Replicate('Y', Cast(Rand()*5 as int)))
Set @rSkaits = @rSkaits - 1
End
Set @Arejais = @Arejais + 1;
End
Go
Kursors tiek realizēts ar funkcijas palīdzību:
Create Function fn_VirkneTekstu_KursorsVaicājums datu izgūšanai:
(
@TabGalvaID int
)
Returns NVarChar(1000)
As
Begin
DECLARE @n NVARCHAR(5)
DECLARE @s NVARCHAR(1000)
DECLARE c CURSOR
LOCAL STATIC FORWARD_ONLY READ_ONLY
FOR
SELECT Saisinajums FROM dbo.TabDetalas Where TabGalvaID = @TabGalvaID
OPEN c;
FETCH NEXT FROM c INTO @n;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @s = COALESCE(@s + ';' + @n, @n);
FETCH NEXT FROM c INTO @n;
END
CLOSE c;
DEALLOCATE c;
Return @s
End
Select g.TabGalvaID, dbo.fn_VirkneTekstu_Kursors(TabGalvaID)"Viltotais kursors"
From dbo.TabGalva g
Arī tiek izmantota funkcija (sanāk īsāk nekā ar kursoru!):
Create Function fn_VirkneTekstu_ViltotsKursorsVaicājums datu izgūšanai:
(
@TabGalvaID int
)
Returns NVarChar(1000)
As
Begin
Declare @txt NVarChar(1000)
SELECT @txt = COALESCE(@txt + ';' + Saisinajums, Saisinajums)
FROM dbo.TabDetalas Where TabGalvaID = @TabGalvaID
Return @txt
End
Select g.TabGalvaID, dbo.fn_VirkneTekstu_ViltotsKursors(TabGalvaID)CLR
From dbo.TabGalva g
Lai varētu izmantot funkciju, jāseko norādījumiem rakstā CLR agregātfunkcijas piemērs.
Vaicājums datu izgūšanai:
Select g.TabGalvaID, dbo.Concatenate(Saisinajums)FOR XML
From dbo.TabGalva g
Left Join dbo.TabDetalas d on g.TabGalvaID = d.TabGalvaID
Group By g.TabGalvaID, Teksts
Pie "FOR XML" vēl noteikti atgriezīšos, bet nu šeit bez garākiem paskaidrojumiem vaicājums (ideja no raksta), kurā tiek izmantota arī With klauza (CTE):
With X
as
(
Select TabGalvaID, Saisinajums
From dbo.TabDetalas
)
Select TabGalvaID,
(
SELECT Saisinajums + ';'
FROM X i
Where i.TabGalvaID = o.TabGalvaID
FOR XML PATH(''), TYPE
).value('.[1]', 'NVARCHAR(1000)')
From X o
Group By TabGalvaID
Nav komentāru:
Ierakstīt komentāru