Bazy Danych
MySQL i rank()
Monday, May 14, 2007 at 10:17:32 am

MySQL nie posiada funkcji rankingujących. Przynajmniej tak jest jeszcze dzisiaj i pewnie długo jeszcze będzie trzeba na nie czekać.

Da radę jednak stworzyć zapytanie, które będzie realizowało funkcjonalność składni :

SELECT
ticket_id,
received,
rank() OVER (partition by ticket_id order by received ASC) as rank
FROM
ebok_ticket_messages
WHERE
rank = 1;

Dla tych, którzy nie znają funkcji rankingujących to zapytanie zwróci numer zgłoszenia razem z datą pierwszej wiadmości.

No a teraz ABRAKADABRA i wyczarowujemy zapytanie w MySQL:

SET @intRownum := 0;
SET @intLastTicketId := 0;
SELECT
ticket_id,
received
FROM
(
SELECT
ticket_id,
received,
CASE
WHEN @intLastTicketId <> ticket_id
THEN @intRownum := 1
ELSE @intRownum := @intRownum + 1
END as rank,
@intLastTicketId := ticket_id
FROM
`ebok_ticket_messages`
WHERE
message_type_id = 3
ORDER BY
ticket_id,
received
) as t
WHERE
t.rank = 1;
Dziedziczenie w PostgreSQL
Monday, May 7, 2007 at 12:49:17 pm

Jedną z moich ulubionych opcji w PostgreSQL jest dziedziczenie dla TABEL. Wyobraźcie sobie prosty sklep internetowy. Produkty w tym sklepie mają pewien zestaw wspólnych atrybutów, plus kilka związanych tylko z danym typem. Zauważyłem, że w tym przypadku dużo lepiej od generycznego modelu danych sprawdza się mechanizm dziedziczenia.

Zakładamy sobie tabelkę `products`:

CREATE TABLE products (
id int,
name text
-- ... costam jeszcze
);

Teraz chcemy dobrze opisać np. monitory.

CREATE TABLE product_monitors (
max_resolution_x int,
max_resolution_y int
-- ... costam jeszcze
) INHERITS (products) ;

No i co zyskaliśmy w ten sposób ? Moim zdaniem dużo :

  • Kwerendy typu SELECT * FROM products wybiorą nam rekordy zarówno z tabeli `products` jak i `product_monitors` (oraz kolejnych, które dziedziczą po `products`);
  • Wszystkie produkty mają wspólną sekwencję, a przez to pulę unikalnych id;
  • Łatwo można modyfikować zestaw wspólnych atrybutów;
  • W atrybutach dodatkowych nie musimy się przejmować kolizją nazw;
  • Ograniczenia na dozwolone wartości atrybutów są łatwe do opisania przy pomocy mechanizmów wbudowanych w bazie danych (CHECK, CONSTRAINT, typ kolumny). Stanowi to dużą przewagę nad modelem generycznym, dla którego musimy analizować metadane na poziomie aplikacji;
MySQL i GROUP_CONCAT
Friday, May 4, 2007 at 08:59:05 am

Ostatnimi czasy mam styczność wieloma projektami Web 2.0.

Typowym elementem w takich projektach są tagi. Nie tak dawno trafiłem na bardzo przyjemną funkcję MySQL'a, która pięknie pasuje do wyciągania z bazy tagów danego post'u w postaci stringa:

SELECT
posts.*,
GROUP_CONCAT(tags.name SEPARATOR ', ')
FROM
posts
LEFT JOIN posts_tags
ON (posts.id = posts_tags.post_id)
LEFT JOIN tags
ON (posts_tags.tag_id = tags.id)
GROUP BY
posts.*;

Naturalnie w wielu przypadkach wygodniejsze będzie operowanie na tagach zebranych do tablicy (dlatego proszę nie czepiajcię się w tej kwestii) - ten przykład podałem, żeby pokazać całkiem miły feature MySQL'a. 

MySQL i rownum()
Wednesday, May 2, 2007 at 06:54:20 pm

Pewnie wielu użytkownikom MySQL'a brakuje rownum'a. Dobra wiadomość jest taka, że można w MySQL zbudować zapytanie realizujące (chyba) w pełni funkcjonalność rownum().

Wystarczy mały trick z wykorzystaniem zmiennych :

SET @rownum :=0;
SELECT
rownum,
name
FROM
(
SELECT
@rownum := @rownum +1 AS rownum,
name FROM users
ORDER BY name ) t1
WHERE
t1.rownum % 2 = 0;

Powyższe zapytanie zwróci co drugi wiersz z tabeli użytkowników posortowanej po nazwie.

Sekwencje w MySQL
Wednesday, May 2, 2007 at 06:45:37 pm

Kiedy zacząłem pracę z MySQL (wcześniej pracowałem na Oracle i PostgreSQL) strasznie brakowało mi sekwencji. Wrzucenie danych do typowej struktury MASTER - DETAIL wymagało obsługi sekwencji na poziomie aplikacji. Po paru próbach i dzięki pomocy kilku użytkowników forum.php.pl udało mi się stworzyć coś takiego:

BEGIN;
INSERT INTO master (created) VALUES (NOW());

SET @myid = LAST_INSERT_ID();

INSERT INTO detail (master_id, title) VALUES (@myid, 'Johny');
INSERT INTO detail (master_id, title) VALUES (@myid, 'Tonny');
INSERT INTO detail (master_id, title) VALUES (@myid, 'Ronnie');

COMMIT;