Konkatenacija string vrednosti iz razlicitih redova (1. deo)

Objavljeno: 11-02-2014 | Autor: Tatjana Mađer | Kategorija: T-SQL

Ознаке:

3

Vrlo često se programeri susreću sa zahtevom da prikažu u aplikaciji izveštaj koji sadrži sumarne rezultate gde je potrebno prikazati u posebnoj koloni svakog sumarnog reda sve članove koji učestvuju u agregatnom redu.
Konkretan primer u bazi SQL_KEFALO ukoliko imamo tabelu sa crtanim filmovima i tabelu sa epizodama za svaki crtani film,

	select E.*, F.Naziv as NazivCrtaca 
	from dbo.CrtaneEpizode E
	inner join dbo.CrtaniFilmovi F
		on E.CrtacID=F.CrtacID

zahtev bi bio sledeći:
Potrebno je za svaki crtani film prikazati naziv crtanog filma, godinu kada je prvi put prikazan crtać i nazive svih epizoda koje su ikada prikazane odvojene zarezima, tj. result set treba da izgleda ovako:

Konkatenacija

U rešavanju ovog problema, može se krenuti od dve pretpostavke:

– da je broj redova mali i unapred poznat i
– da je broj redova veliki i nepoznat developeru.

Ova dva slučaja ćemo posmatrati odvojeno i u prvom delu ove teme posmatramo prvi slučaj.

Unapred poznat broj redova

Ova situacija je veoma retka, rešenje problema u ovom slučaju je jednostavnije i može se uraditi na više načina. Sledeći upiti prikazuju u agregatnom redu prve tri epizode za svaki crtani film po abecednom redosledu preko:

a) Ugnježdenog upita gde je ideja da se kreira izraz unutar podupita koji proizvodi rank (seq) baziran na nazivima epizoda i iskoristiti ga u spoljašnjem upitu.

      SELECT  NazivCrtaca,PremGod,
		   Konkatenacija=MAX( CASE seq WHEN 1 THEN Naziv ELSE '' END ) + ', ' +
						 MAX( CASE seq WHEN 2 THEN Naziv ELSE '' END ) + ', ' +
						 MAX( CASE seq WHEN 3 THEN Naziv ELSE '' END )
      FROM ( SELECT F.Naziv as NazivCrtaca, e1.Naziv, 
                    seq=(SELECT COUNT(*)
                         FROM dbo.CrtaneEpizode e2
                         WHERE e2.CrtacID = e1.CrtacId
                               AND e2.Naziv <= e1.Naziv ),
                    PremGod=(SELECT MIN(GodinaPremijernogIzvodjenja) 
						     FROM dbo.CrtaneEpizode e3
                             WHERE e3.CrtacID = e1.CrtacId)
             FROM dbo.CrtaneEpizode e1 
             INNER JOIN dbo.CrtaniFilmovi F
				on e1.CrtacID=F.CrtacID) D 
       GROUP BY NazivCrtaca,PremGod;

b) Koristeći ROW_NUMBER funkciju i common table expression (CTE) bi mogao upit da se napiše na sledeći način:

; WITH CTE ( NazivCrtaca, PremGod, Naziv, seq ) AS
      (SELECT F.Naziv as NazivCrtaca, E.GodinaPremijernogIzvodjenja, E.Naziv,  
			  ROW_NUMBER() OVER ( PARTITION BY F.CrtacID ORDER BY E.Naziv )
       FROM dbo.CrtaneEpizode E
	   INNER JOIN dbo.CrtaniFilmovi F
			on E.CrtacID=F.CrtacID   )

	 SELECT NazivCrtaca,MIN(PremGod) AS PremGod,
            Konkatenacija=MAX( CASE seq WHEN 1 THEN Naziv ELSE '' END ) + ', ' +
						  MAX( CASE seq WHEN 2 THEN Naziv ELSE '' END ) + ', ' +
						  MAX( CASE seq WHEN 3 THEN Naziv ELSE '' END )
     FROM CTE
     GROUP BY NazivCrtaca

Ovaj izraz sa ROW_NUMBER() funkcijom će raditi za verzije SQL servera od 2005 pa nadalje.

c) Uz pomoć PIVOT funkcije, gde se prvo rankiranjem pa pivotiranjem ovih rankiranih vrednosti dobijaju tri naslova koja se spajaju prostom operacijom konkateniranja.

	SELECT NazivCrtaca,
		   PremGod,
		   ISNULL([1],'') + ', ' + ISNULL([2],'') + ', ' + ISNULL([3],'')  AS Konkatenacija
	FROM 

					(SELECT F.Naziv as NazivCrtaca, 
							PremGod=(SELECT MIN(GodinaPremijernogIzvodjenja) 
									 FROM dbo.CrtaneEpizode e3
									 WHERE e3.CrtacID = e.CrtacId),
							E.Naziv,  
							seq=ROW_NUMBER() OVER ( PARTITION BY F.CrtacID ORDER BY E.Naziv )
					FROM dbo.CrtaneEpizode E
					INNER JOIN dbo.CrtaniFilmovi F
						on E.CrtacID=F.CrtacID   
					) P 
	 PIVOT ( MAX( Naziv ) FOR seq IN ( [1], [2], [3], [4] ) ) AS P

 

Zbog jednostavnijeg zahteva da se uradi spajanje samo prva tri naslova, i upiti za njegovu realizaciju su prilično jednostavni i resursno manje zahtevni. Na malom broju slogova (do 1000), performanse sva tri načina su odlične, tako da se može preporučiti bilo koji upit za primenu. Na velikom broju slogova (oko 1 milion), jedini upit koji ima zadovoljavajuće performanse je upit b), što se osim trajanja na primeru, može videti i na osnovu query cost-a svakog od ova tri upita.

Komentari (3)

[…] prikazati u posebnoj koloni svakog sumarnog reda sve članove koji učestvuju u agregatnom redu. U prethodnom delu ovog članka, objašnjeni su načini konkatenacije string vrednosti ukoliko je unapred poznat i ograničen broj […]

[…] Konkatenacija string vrednosti iz razlicitih redova (1. deo) Konkatenacija string vrednosti iz razlicitih redova (2. deo) […]

[…] nekog vremena smo u dva članka (1, 2) opisali proces konkatenacije, odnosno spajanja vrednosti iz kolone u jedan string. Ovog puta […]