I have a database of exhibitions and artists. Each exhibition is associated with any number of artists, and joined by a pivot table, like so:
'exhibitions'
+-----+--------------------------+
| id | title |
+-----+--------------------------+
| 358 | A Closed Circuit |
| 360 | Do You Love Me? |
| 361 | Romans |
| 364 | Men in Love |
| 367 | Bien Hoa |
| 369 | Jokes |
| 370 | Speech |
| 373 | Shimabuku's Fish & Chips |
+-----+--------------------------+
'artists'
+-----+-----------------+----------------------+
| id | first_name | last_name |
+-----+-----------------+----------------------+
| 301 | Roman | Schramm |
| 302 | Ben | Schumacher |
| 303 | Hugh | Scott-Douglas |
| 304 | Nina | Beier and Marie Lund |
| 305 | Constant | Dullaart |
| 306 | Brenna | Murphy |
| 307 | Jaako | Pallasvuo |
| 308 | Nicolas | Pelzer |
| 309 | Jon | Rafman |
+-----+-----------------+----------------------+
'associations'
+-------+-------------+------+-------------+------+
| id | a_t | a_id | b_t | b_id |
+-------+-------------+------+-------------+------+
| 21371 | exhibitions | 369 | artists | 301 |
| 21373 | exhibitions | 1842 | artists | 2644 |
| 21379 | exhibitions | 315 | artists | 2644 |
| 27752 | exhibitions | 4015 | artists | 2644 |
| 21848 | exhibitions | 71 | artists | 2650 |
| 34 | artists | 1 | exhibitions | 1 |
| 85 | artists | 5 | exhibitions | 36 |
| 90 | artists | 8 | exhibitions | 38 |
| 97 | artists | 10 | exhibitions | 39 |
| 98 | artists | 54 | exhibitions | 40 |
| 100 | artists | 55 | exhibitions | 42 |
| 102 | artists | 55 | exhibitions | 43 |
| 103 | artists | 301 | exhibitions | 370 |
| 584 | exhibitions | 998 | venues | 22 |
| 1234 | venues | 15 | exhibitions | 370 |
+-------+-------------+------+-------------+------+
'venues'
+------+--------------------+
| id | name |
+------+--------------------+
| 22 | ACME |
| 1116 | Adams and Ollman |
| 510 | Adamski |
| 1077 | Agustina Ferreyra |
| 15 | Air de Paris |
+------+--------------------+
I need to create a search that matches terms against:
- Exhibition titles
- Artists associated with the exhibition
- The venues associated with the exhibition
But, as you can see, the associations
table has some entries that are swapped. Sometimes the exhibition is the "A" table, sometimes it's "B". Also, it's also not a simple pivot table - as you can see, it's making associations for all tables. It's what I have to work with, and breaking this into more succinct pivot tables (table A to B, instead of the current table A/B/C/D/E to A/B/C/D/E) would require a lot of refactoring elsewhere and is out of the question.
As an example: the artist David Leiske has the ID of 128
, and associations that go both ways:
mysql> select * from associations where (a_t = 'artists' AND a_id = 126 AND b_t = 'exhibitions') OR (a_t = 'exhibitions' AND b_id = 126 AND b_t = 'artists');
+-------+-------------+------+-------------+------+
| id | a_t | a_id | b_t | b_id |
+-------+-------------+------+-------------+------+
| 10438 | artists | 126 | exhibitions | 458 |
| 10772 | exhibitions | 55 | artists | 126 |
| 27490 | artists | 126 | exhibitions | 3877 |
| 27500 | artists | 126 | exhibitions | 3878 |
| 28435 | artists | 126 | exhibitions | 4378 |
| 28474 | artists | 126 | exhibitions | 4379 |
| 28475 | artists | 126 | exhibitions | 4380 |
+-------+-------------+------+-------------+------+
These two queries are giving me what I need, separately:
SELECT exhibitions.id as result_id, 'exhibitions' as result_type, exhibitions.title AS result_title, GROUP_CONCAT(CONCAT_WS(' ', artists.first_name, artists.last_name)) AS col_3
FROM exhibitions
LEFT JOIN associations AS associations ON (associations.b_id = exhibitions.id AND b_t = 'exhibitions' AND a_t = 'artists')
LEFT JOIN artists ON (associations.a_id = artists.id AND a_t = 'artists')
WHERE
(artists.id IS NOT NULL)
AND ((CONCAT_WS(' ', artists.first_name, artists.last_name)) RLIKE 'Lieske')
OR (exhibitions.title RLIKE 'Lieske')
GROUP BY exhibitions.id
+-----------+-------------+------------------------------------------------------------+
| result_id | result_type | result_title | col_3 |
+-----------+-------------+------------------------------------------------------------+
| 458 | exhibitions | Deformation Professionelle | David Lieske |
| 3877 | exhibitions | Der africanishe Stuhl von marcel Breuer | David Lieske |
| 3878 | exhibitions | Imerium in Imperio | David Lieske |
| 4378 | exhibitions | A Greater Administration of Lower Interests | David Lieske |
| 4379 | exhibitions | Platitude Normale | David Lieske |
| 4380 | exhibitions | Fantôme Mains Sur | David Lieske |
+-----------+-------------+------------------------------------------------------------+
And:
SELECT exhibitions.id as result_id, 'exhibitions' as result_type, exhibitions.title AS result_title, GROUP_CONCAT(CONCAT_WS(' ', artists.first_name, artists.last_name)) AS col_3
FROM exhibitions
LEFT JOIN associations AS associations ON (associations.a_id = exhibitions.id AND a_t = 'exhibitions' AND b_t = 'artists')
LEFT JOIN artists ON (associations.b_id = artists.id AND b_t = 'artists')
WHERE
(artists.id IS NOT NULL)
AND ((CONCAT_WS(' ', artists.first_name, artists.last_name)) RLIKE 'Lieske')
OR (exhibitions.title RLIKE 'Lieske')
GROUP BY exhibitions.id
+-----------+-------------+--------------+--------------+
| result_id | result_type | result_title | col_3 |
+-----------+-------------+--------------+--------------+
| 55 | exhibitions | Adequate | David Lieske |
+-----------+-------------+--------------+--------------+
I could UNION the two queries into one - this gives me the final result that I'm going for. But, I'll be building this query dynamically - this is just one (troublesome) portion of a more comprehensive search, and it would be much easier if I could handle this A:B / B:A relationship in a more dynamic way.
I've tried adding an OR to the join:
SELECT exhibitions.id as result_id, 'exhibitions' as result_type, exhibitions.title AS result_title, GROUP_CONCAT(CONCAT_WS(' ', artists.first_name, artists.last_name)) AS col_3
FROM exhibitions
LEFT JOIN associations AS associations ON (associations.b_id = exhibitions.id AND b_t = 'exhibitions' AND a_t = 'artists')
OR (associations.a_id = exhibitions.id AND a_t='exhibitions' AND b_t='artists')
LEFT JOIN artists ON (associations.a_id = artists.id AND a_t = 'artists')
OR (associations.b_id = artists.id AND b_t = 'artists')
WHERE
(artists.id IS NOT NULL)
AND ((CONCAT_WS(' ', artists.first_name, artists.last_name)) RLIKE 'Lieske')
OR (exhibitions.title RLIKE 'Lieske')
GROUP BY exhibitions.id
but, I know I'm doing something wrong, because the query takes over 30 seconds, whereas the UNION'd query took 0.07.
What am I doing wrong here?
Lastly: how do I also JOIN venues.name
to the table and add that to the query?
Sample searches:
RLIKE 'art|acme'
, an expected result: The exhibition(s) that hadart
in the title, and was at the venueACME
RLIKE 'davis|paris'
, an expected result: The exhibition(s) including an artist with a first or last name ofdavis
, which took place atAir de Paris
(I realize these are going to bring up all of the OR matches - but, this post is getting long enough, so I can make another one about ranking the results..)
Aucun commentaire:
Enregistrer un commentaire