Marcin Lewandowski

Marcin Lewandowski Programista
PHP/MySQL,
poszukuję...

Temat: Przeszukiwanie grup pól

Witam,

Chciałem się dowiedzieć w jaki sposób byście ugryźli przeszukiwanie struktury bazy danych, która wygląda w następujący sposób, mamy tabele:

- subskrybent,
- subskrybent_grupa
- subskrybent_grupa_pole
- pole

Połączenie tych tabel wygląda w ten sposób że do każdego subskrybenta jest przypisana grupa pól np.

Jak Kowalski
+ Grupa1: (pole1:poznań, pole2:00-000)
+ Grupa2: (pole1:kraków)
+ Grupa3: (pole1:poznań, pole2:99-999)

Franciszek Nowak
+ Grupa1: (pole1:łódź)
+ Grupa2: (pole1:kraków, pole3: 29lat)
+ Grupa3: (pole1:poznań, pole2:99-999)

I teraz jak efektywnie przeszukać taką strukturę kiedy użytkownik wprowadził kryteria wyszukiwania:

Opcja1: pole1:poznań (oczekiwany rezultat: Jan Kowalski, Franciszek Nowak)
Opcja2: pole1:poznań, pole2:00-000 (oczekiwany rezultat: Jan Kowalski)
Nikodem Dobrzański

Nikodem Dobrzański Architekt systemu,
BizTech Konsulting
S.A.

Temat: Przeszukiwanie grup pól

Przez LEFT OUTER JOIN. Serwer bazy (MSSQL) nie musi odrzucać duplikatów, bo ich nie ma, a poza tym, skupia się na dołączeniu danych.
Grzegorz G.

Grzegorz G. ASE / Systems
Architect, Syniverse

Temat: Przeszukiwanie grup pól

Tabelki wziąłem takie:
create table s (s_id number primary key, nm varchar2 (100));
create table sg (sg_id number primary key, nm varchar2 (100), s_id number);
alter table sg add constraint fk_s_id foreign key (s_id) references s (s_id);
create table sgf (sgf_id number primary key, nm varchar2(100), sg_id number);
alter table sgf add constraint fk_sg_id foreign key (sg_id) references sg (sg_id);
create table f (f_id number, nm varchar2(100), sgf_id number);
alter table f add constraint fk_sgf_id foreign key (sgf_id) references sgf (sgf_id);


Dane takie:
insert into s values (1, 'Jan Kowalski');
insert into s values (2, 'Franciszek Nowak');

insert into sg values (10, 'Grupa1', 1);
insert into sg values (11, 'Grupa2', 1);
insert into sg values (12, 'Grupa3', 1);
insert into sg values (13, 'Grupa1', 2);
insert into sg values (14, 'Grupa2', 2);
insert into sg values (15, 'Grupa3', 2);

insert into sgf values (100, 'pole1', 10);
insert into sgf values (101, 'pole2', 10);
insert into sgf values (102, 'pole1', 11);
insert into sgf values (103, 'pole1', 12);
insert into sgf values (104, 'pole2', 12);
insert into sgf values (105, 'pole1', 13);
insert into sgf values (106, 'pole1', 14);
insert into sgf values (107, 'pole3', 14);
insert into sgf values (108, 'pole1', 15);
insert into sgf values (109, 'pole2', 15);

insert into f values (1000, 'poznan', 100);
insert into f values (1001, '00-000', 101);
insert into f values (1002, 'krakow', 102);
insert into f values (1003, 'poznan', 103);
insert into f values (1004, '99-999', 104);
insert into f values (1005, 'lodz', 105);
insert into f values (1006, 'krakow', 106);
insert into f values (1007, '29lat', 107);
insert into f values (1008, 'poznan', 108);
insert into f values (1009, '99-999', 109);


Chyba takie jak chciałeś:
SQL> select s.nm, sg.nm, sgf.nm, f.nm
2 from s
3 inner join sg on s.s_id = sg.s_id
4 inner join sgf on sg.sg_id = sgf.sg_id
5 inner join f on sgf.sgf_id = f.sgf_id;

NM NM NM NM
-------------------- -------------------- -------------------- --------------------
Jan Kowalski Grupa1 pole1 poznan
Jan Kowalski Grupa1 pole2 00-000
Jan Kowalski Grupa2 pole1 krakow
Jan Kowalski Grupa3 pole1 poznan
Jan Kowalski Grupa3 pole2 99-999
Franciszek Nowak Grupa1 pole1 lodz
Franciszek Nowak Grupa2 pole1 krakow
Franciszek Nowak Grupa2 pole3 29lat
Franciszek Nowak Grupa3 pole1 poznan
Franciszek Nowak Grupa3 pole2 99-999

10 rows selected.


Nie wiem, czy to "wydajne" (cokolwiek oznacza "wydajne" w tym kontekście) przeszukiwanie, ale tak na pierwszy rzut oka to można chainem existów to wyciągnąć:

SQL> select s.nm
2 from s
3 where exists (
4 select 'x'
5 from sg
6 where sg.s_id = s.s_id
7 and exists (
8 select 'x'
9 from sgf
10 where sgf.sg_id = sg.sg_id
11 and exists (
12 select 'x'
13 from f
14 where f.sgf_id = sgf.sgf_id
15 and (sgf.nm, f.nm) in (('pole1', 'poznan'))
16 )
17 )
18 );

NM
--------------------
Jan Kowalski
Franciszek Nowak

SQL> select s.nm
2 from s
3 where exists (
4 select 'x'
5 from sg
6 where sg.s_id = s.s_id
7 and exists (
8 select 'x'
9 from sgf
10 where sgf.sg_id = sg.sg_id
11 and exists (
12 select 'x'
13 from f
14 where f.sgf_id = sgf.sgf_id
15 and (sgf.nm, f.nm) in (('pole1', 'poznan'))
16 )
17 )
18 and exists (
19 select 'x'
20 from sgf
21 where sgf.sg_id = sg.sg_id
22 and exists (
23 select 'x'
24 from f
25 where f.sgf_id = sgf.sgf_id
26 and (sgf.nm, f.nm) in (('pole2', '00-000'))
27 )
28 )
29 );

NM
--------------------
Jan Kowalski


Aaa jeszcze disclaimer: nie wnikam póki co w strukturę - czy ma ona sens czy nie; zakładam, że to jakieś uproszczenie czegoś większego i nie planujesz tego przemodelować.Ten post został edytowany przez Autora dnia 14.05.13 o godzinie 12:42
Marcin Lewandowski

Marcin Lewandowski Programista
PHP/MySQL,
poszukuję...

Temat: Przeszukiwanie grup pól

Witam,

Przepraszam że wcześniej nie wrzuciłam tak podstawowych danych które umożliwiły by wam łatwe przetestowanie własnych zapytań oraz porównanie ich z moim pomysłem na rozwiązanie problemu.

Struktura bazy
CREATE TABLE IF NOT EXISTS `pole` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`nazwa` varchar(200) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;

CREATE TABLE IF NOT EXISTS `subskrybent` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(200) NOT NULL,
`imie` varchar(200) NOT NULL,
`nazwisko` varchar(200) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;

CREATE TABLE IF NOT EXISTS `subskrybent_grupa` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`subskrybent_id` int(11) NOT NULL,
`nazwa` varchar(200) NOT NULL,
PRIMARY KEY (`id`),
KEY `subskrybent_id` (`subskrybent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;

CREATE TABLE IF NOT EXISTS `subskrybent_grupa_pole` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`subskrybent_grupa_id` int(11) NOT NULL,
`pole_id` int(11) NOT NULL,
`wartosc` varchar(200) NOT NULL,
PRIMARY KEY (`id`),
KEY `subskrybent_grupa_id` (`subskrybent_grupa_id`,`pole_id`),
KEY `pole_id` (`pole_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=11 ;

INSERT INTO `subskrybent` (`id`, `email`, `imie`, `nazwisko`) VALUES
(1, 'jan.kowalski@gmail.com', 'Jan', 'Kowalski'),
(2, 'franciszek.nowak@gmail.com', 'Franciszek', 'Nowak');

INSERT INTO `pole` (`id`, `nazwa`) VALUES
(1, 'Miejscowość'),
(2, 'Kod pocztowy'),
(3, 'Wiek');

INSERT INTO `subskrybent_grupa` (`id`, `subskrybent_id`, `nazwa`) VALUES
(1, 1, 'Grupa 1'),
(2, 1, 'Grupa 2'),
(3, 1, 'Grupa 3'),
(4, 2, 'Grupa 1'),
(5, 2, 'Grupa 2'),
(6, 2, 'Grupa 3');

INSERT INTO `subskrybent_grupa_pole` (`id`, `subskrybent_grupa_id`, `pole_id`, `wartosc`) VALUES
(1, 1, 1, 'poznań'),
(2, 1, 2, '00-000'),
(3, 2, 1, 'kraków'),
(4, 3, 1, 'poznań'),
(5, 3, 2, '99-999'),
(6, 4, 1, 'łódź'),
(7, 5, 1, 'kraków'),
(8, 5, 3, '29'),
(9, 6, 1, 'poznań'),
(10, 6, 2, '99-999');

ALTER TABLE `subskrybent_grupa`
ADD CONSTRAINT `subskrybent_grupa_ibfk_1` FOREIGN KEY (`subskrybent_id`) REFERENCES `subskrybent` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

ALTER TABLE `subskrybent_grupa_pole`
ADD CONSTRAINT `subskrybent_grupa_pole_ibfk_1` FOREIGN KEY (`subskrybent_grupa_id`) REFERENCES `subskrybent_grupa` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `subskrybent_grupa_pole_ibfk_2` FOREIGN KEY (`pole_id`) REFERENCES `pole` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

Podgląd danych:
SELECT s.imie, s.nazwisko, sg.nazwa grupa, p.nazwa, sgp.wartosc
FROM `subskrybent` s
INNER JOIN subskrybent_grupa sg ON s.id = sg.subskrybent_id
INNER JOIN subskrybent_grupa_pole sgp ON sgp.subskrybent_grupa_id = sg.id
INNER JOIN pole p ON sgp.pole_id = p.id
ORDER BY s.id, sg.nazwa

Wyszukiwanie:
SELECT * FROM (SELECT s.imie, s.nazwisko, sg.nazwa grupa, p.nazwa, sgp.wartosc, count(sgp.subskrybent_grupa_id) AS ilosc_pol
FROM `subskrybent` s
INNER JOIN subskrybent_grupa sg ON s.id = sg.subskrybent_id
INNER JOIN subskrybent_grupa_pole sgp ON sgp.subskrybent_grupa_id = sg.id
INNER JOIN pole p ON sgp.pole_id = p.id
WHERE
(sgp.pole_id = '1' AND sgp.wartosc = 'poznań') OR
(sgp.pole_id = '2' AND sgp.wartosc = '00-000')
group by sgp.subskrybent_grupa_id) AS pola WHERE ilosc_pol = 2

Wyszukiwanie nie wydaje mi się na zbyt optymalne i samej struktury bazy też w stu procentach nie jestem pewien. Wydaje mi się to rozwiązanie dość elastyczne pytanie tylko jak się zachowa ta struktura, gdy załaduje się tam kilka milionów rekordów. Jakieś pomysły na przeprowadzenie testów oraz modyfikacji zapytań i struktury ?
Nikodem Dobrzański

Nikodem Dobrzański Architekt systemu,
BizTech Konsulting
S.A.

Temat: Przeszukiwanie grup pól

Zapomniałeś o trzech aliasach i formatowaniu, przez co oświadczenie jest mniej czytelne.

SELECT
pola.*
FROM (SELECT
s.imie
, s.nazwisko
, sg.nazwa grupa
, p.nazwa
, sgp.wartosc
, count(sgp.subskrybent_grupa_id) AS ilosc_pol
FROM `subskrybent` AS s
INNER JOIN subskrybent_grupa AS sg ON s.id = sg.subskrybent_id
INNER JOIN subskrybent_grupa_pole AS sgp ON sgp.subskrybent_grupa_id = sg.id
INNER JOIN pole AS p ON sgp.pole_id = p.id
WHERE
(sgp.pole_id = '1' AND sgp.wartosc = 'poznań') OR
(sgp.pole_id = '2' AND sgp.wartosc = '00-000')
GROUP BY sgp.subskrybent_grupa_id) AS pola
WHERE pola.ilosc_pol = 2

Wszystko jest w miarę poprawnie, dobrze by było, żebyś zobaczył, jaki masz plan zapytania, bo ten niejednego zaskoczył. A dwie tablice są tutaj najważniejsze: subskrybent oraz subskrybent_grupa_pole, bo na nich jest położone grupowanie. Nie wiadomo, czy wyłączenie tego grupowania do podzapytania nie da lepszych wyników. Jak na ironię, złączenia typu INNER JOIN ograniczają Ci wstępnie dane.
Zapewne zmieniłbym drugie wystąpienie INNER JOIN z

INNER JOIN subskrybent_grupa_pole sgp ON sgp.subskrybent_grupa_id = sg.id

na

INNER JOIN (
SELECT
n.wartosc,
n.subskrybent_grupa_id
n.pole_id
FROM subskrybent_grupa_pole as n
WHERE (n.pole_id = '1' AND n.wartosc = 'poznań') OR (n.pole_id = '2' AND n.wartosc = '00-000')
) AS sgp ON sgp.subskrybent_grupa_id = sg.id

To powinno zmniejszyć ilość złączeń. Niemniej powtórzę - sprawdź plany wykonania.



Wyślij zaproszenie do