Kawałek Kodu

Zazwyczaj tworząc zapytania w SQL unikamy jak ognia (albo jak mrówek) sytuacji, aby rekordy się duplikowały, triplikowały czy też...no właśnie, może ktoś z Was wie jak się nazywa czterokrotne lub pięciokrotne zwielokrotnianie rekordów?

Jednak dziś zajmiemy się sytuacją odwrotną. Chcemy z jakiegoś powodu, aby dany rekord pojawił się dwukrotnie, inny dziesięciokrotnie, a jeszcze inny pozostał pojedynczy.

Zacznijmy od końca:

SELECT t1.imie FROM tabela AS t1
JOIN
(SELECT a.a + (10 * b.a) AS ilosc FROM
 (SELECT 0 AS a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) AS a
 CROSS JOIN
 (SELECT 0 AS a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) AS b
) AS ilosci ON t1.ilosc>ilosci.ilosc

Przy naszej wejściowej tabeli:

imie ilosc
Jan 1
Marek 10
Stefan 5
Romek 44

w wyniku tego zapytania otrzymamy 60 rekordów (1 x Jan, 10 x Marek, 5 x Stefan, 44 x Romek).

Jeśli łapiesz się za głowę widząc to zapytanie, to spróbujmy je rozbić na mniejsze, aby zrozumieć krok po kroku co tu się dzieje.

Krok 1:

SELECT 0 AS a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9

To zapytanie zwróci 10 rekordów (0, 1, 2, ..., 9).

Krok 2:

SELECT a.a,b.a FROM 
(SELECT 0 AS a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) AS a
  CROSS JOIN
(SELECT 0 AS a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) AS b

Mamy tu zapytanie wybierające dwie kolumny z podzapytania a oraz podzapytania b. Łączenie następuje poprzez CROSS JOIN (połączenie krzyżowe). Zapytanie zwróci 100 rekordów:

a.a b.a
0 0
1 0
2 0
3 0
4 0
5 0
... ...
6 1
7 1
8 1
9 1
0 9
1 9
2 9
3 9
4 9
5 9
6 9
7 9
8 9
9 9

Otrzymaliśmy wynik potocznie zwany "każdy z każdym" (nieźle!), a fachowo iloczynem kartezjańskim.

Krok 3:

SELECT a.a+b.a*10 FROM 
(SELECT 0 AS a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) AS a
  CROSS JOIN
(SELECT 0 AS a UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) AS b

Teraz gdy zsumujemy wartość kolumny z jednego podzapytania (jedności) z pomnożoną przez 10 wartością z kolumny drugiego podzapytania, otrzymamy podobnie jak wyżej 100 rekordów, ale o wartościach:

a.a+b.a*10
0
1
2
3
4
5
6
7
...
90
91
92
93
94
95
96
97
98
99

To zapytanie można trochę uprościć umieszczając w drugim podzapytaniu od razu wartości pomnożone przez 10 (0, 10, 20, itd.), wtedy obliczenie w głównym zapytaniu ma postać a.a+b.a.

Krok 4:

Teraz możemy wrócić do naszego głównego zapytania. Łączymy tabelę wspomnianą na początku z "tabelą liczb" od 0 do 99. Ponieważ łączenie oparte jest na warunku t1.ilosc>ilosci.ilosc, w przypadku każdego imienia rekord będzie multiplikowany tyle razy dopóki ten warunek będzie spełniony. I tak dla imienia Jan i odpowiadającej mu ilości=1, zostanie on zwielokrotniony jeden raz (1>0,1>1), a Stefan pojawi się w wynikach 5 razy (5>0, 5>1, 5>2, 5>3, 5>4, 5>5).

Dalej łapiesz się za głowę?:)

Krok 5:

Być może już zauważyłeś, że w przypadku ilości większej niż 100 przypisanej do danego imienia, nie otrzymamy w wyniku więcej niż 100 rekordów. Dlaczego tak się dzieje? To już zagadka dla Ciebie, Czytelniku.

 

Przydatne linki:
Do pełna poproszę! Czyli array_fill w MySQL(?)