łączenie pól tekstowych z wielu rekordów

czasem jest taka potrzeba by zwrócić kilka połączonych stringów z kolejnych rekordów.

np.:

mając tabelkę z takimi danymi:

> select pracownik from pracownicy where firma_id = 1;

  • Jan Kowalski
  • Hubert Lubaczewski
  • Piotr Nowak
  • Adam Słodowy

chcielibyśmy dostać jedno pole z zawartością:

Jan Kowalski, Hubert Lubaczewski, Piotr Nowak, Adam Słodowy

lub wręcz posortowane:

Adam Słodowy, Hubert Lubaczewski, Jan Kowalski, Piotr Nowak

aby to zrobić najwygodniej jest zdefiniować własny agregat (czyli funkcję taką jak min(), max(), sum(), avg()).

najpierw zdefiniujemy funkcję łączącą:

CREATE OR replace function agg_text_sum(txt_before TEXT, txt_new TEXT) RETURNS TEXT AS
$BODY$
DECLARE
BEGIN
IF (txt_before IS NULL) THEN
RETURN txt_new;
END IF;
RETURN txt_before || ‘, ‘ || txt_new;
END;
$BODY$
LANGUAGE ‘plpgsql';

następnie tworzymy agregat:

CREATE aggregate text_sum (
basetype = TEXT,
sfunc = agg_text_sum,
stype = TEXT
);

i już możemy:

> select text_sum(pracownik) from pracownicy where firma_id = 1;

  • Jan Kowalski, Hubert Lubaczewski, Piotr Nowak, Adam Słodowy

aby to posortować trzeba uciec się do podzapytań:

> select text_sum(pracownik) from (select pracownik from pracownicy where firma_id = 1 order by pracownik) x;

  • Adam Słodowy, Hubert Lubaczewski, Jan Kowalski, Piotr Nowak

9 thoughts on “łączenie pól tekstowych z wielu rekordów”

  1. A gdybyś miał MySQL to napisałbyś:
    select group_concat(pracownik) from pracownicy
    where firma_id=1

    Bez tworzenia żadnych funkcji :> A, chciałeś posortowane. Nic prostszego, również można sobie odpuścić podzapytania :>
    select group_concat(pracownik order by pracownik) from pracownicy
    where firma_id=1

    Więcej: http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html

  2. Naturalnie, że da się zmienić. Domyślnie łączy przecinkiem.
    A teraz połączy blah’em 😉

    select group_concat(pracownik SEPARATOR ‘blah’) from pracownicy
    where firma_id=1

  3. fajne.
    mysql ma sporo naprawde sympatycznych rzeczy.
    choc z drugiej strony – widze tam wiele dziwnego entuzjazmu. np. jakos ostatnio czytalem o tym, ze na 5.x da sie zrobic replikacje multi-master z automatycznym fileoverem. ale sa “ale”:
    1. aby to zadzialalo trzeba uzywac 5.1.x ktore jest developerskie
    2. multi-master owszem jest. dopoki sie nie uzywa kluczy unikalnych (takich jak np. username).

    nie zmienia to jednak faktu, ze mysql sie zmienia w dobrym kierunku i jest coraz bardziej sympatyczna baza. ale ja juz chyba za stary jestem by sie go uczyc …

  4. Jeśli chodzi o multi-master replikację, to chyba nic się nie zmieniło między stabilną 5.0 i betą 5.1, poza tym, że oczywiście w 5.1 dodano replikację row-based oprócz od dawna istniejącej statement-based.

    Co do unikalnych kluczy na wielu masterach — jak sobie wyobrażasz, że to miałoby działać? Co ma zrobić slave gdy napotka rekord z unikalnym kluczem, który już pobrał od innego mastera?

  5. nie pozwolić na dodanie. dlatego prawdziwa replikacja multimaster działa tylko gdy jest synchroniczna.
    niestety tego typu rozwiązania są kosztowne. mocno.

  6. W MySQL mozesz osiągnąć więc to samo: w przypadku gdy zreplikowane dane powodują błąd, można nakazać go slave’owi zignorować i nasłuchiwać dalej.
    –slave-skip-errors=[err_code1,err_code2,…|all]
    np. 1062 duplicate entry

  7. ok, ale te bledne dane pozostaja.
    przynajmniej tak to rozumiem.
    tzn. mam tabele users (id, username (unique), haslo);
    na jednym nodzie dodaje username=depesz, haslo = ‘blabluga’;
    na drugim dodaje username=depesz, haslo = dasdsa
    i jak rozumiem bazy sie roznia.

    ale to akurat najmniejsza kwestia. do mojego ulubionego postgresa tez nie ma replikacji multi-master (ani nawet master multi-slave synchronicznej). moze za jakis czas bedzie. na razie trzeba sobie dawac rade pgpoolem lub (jak kogos stac) pclustrem czy bizgresmpp’em.

  8. Ano pozostają. Nie ma z tym zwykle problemu, bo się decyduje ręcznie jakie dane na których masterach wrzucąć. Problem był z kluczami podstawowymi autoinkrementowanymi, ale w MySQL jest sposób, żeby to obejść (różna inkrementacja na masterach). W Postgres zresztą analogicznie — tylko inne sekwencje trzeba zrobić.

Comments are closed.