2

I have a celebrity photo website. It has 1 table that holds all searchable data for each photo called photoSearch and the full set of data is spread over many tables (normalization), named photos, photoPeople and people.

Table photoPeople contains photoID's and peopleID's and the people table contains peopleID's and the people's names. The photos table contains captions, headlines, heights, widths, file sizes, sales codes etc etc.

This is my current script which produces good results.

SELECT 
     p.photoID, 
     p.setID, 
     p.headline, 
     p.caption,
     GROUP_CONCAT(pe.people ORDER BY pe.people ASC SEPARATOR ';') AS people
FROM 
     photos p
INNER JOIN
     (
        SELECT photoID 
        FROM photoSearch 
        WHERE MATCH (allKeywords, shortCaption)
        AGAINST ('+red +dress +claridges +hotel' IN BOOLEAN MODE) 
        LIMIT 50
     ) pids ON p.photoID = pids.photoID
***********
LEFT JOIN
     photoPeople pp ON p.photoID = pp.photoID
LEFT JOIN
     people pe ON pp.peopleID = pe.peopleID
***********
GROUP BY p.photoID

The problem is, instead of just selecting celebrity names to display in the results, I now want to search for one or more celebrity names to alter the results.

I'm pretty sure it's not possible to add two or more where clauses on the same column (where pe.people = 'angelina jolie' and pe.people = 'brad pitt'), so I'm pretty sure it needs to be done using JOINS.

This confused me a bit. So I asked a question here on SO to find out how I could achieve this and somebody advised this:

"Join to the 'photoPeople' and 'people' tables n times, where n is the number of people you are searching for."

I tried many different ways of achieving this but so far have failed. I know what he said involves removing the joins in between the two lines of asterisks (code above) and replacing them with n amount of joins to the people table, but I just can't figure it out.

I tried this but phpMyAdmin has now been thinking for the last 10 minutes!:

********
JOIN ( photoPeople AS pp1 JOIN people AS p1 ON pp1.peopleID = p1.peopleID) 
ON p1.people = 'Brad Pitt'
JOIN ( photoPeople AS pp2 JOIN people AS p2 ON pp2.peopleID = p2.peopleID) 
ON p2.people = 'Angelina Jolie'
********

On top of the above attempt taking FOREVER, I also struggle to see how I can alter the GROUP_CONCAT function to get the results from more than one join/alias (p1, p2, p3 etc)!

Please, I would love some advice to get this working correctly.


TEST SCENARIO

If I run my query as-is, this is what I would see:

Photo:    120030
Set:      8803
Headline: Brad and Angelina arrive at film premiere.
People:   Brad Pitt; Angelina Jolie

Photo:    120031
Set:      8803
Headline: Brad and Angelina arrive at film premiere.
People:   Angelina Jolie

Photo:    120032
Set:      8803
Headline: Brad and Angelina arrive at film premiere.
People:   Brad Pitt

I want my new query to search for names, so if I search for Angeline Jolie and Brad Pitt, I will see only this:

Photo:    120030
Set:      8803
Headline: Brad and Angelina arrive at film premiere.
People:   Brad Pitt; Angelina Jolie

NEW ATTEMPT:

No errors but returned 0 results, where there should be 1000's of results for 'Brad Pitt' and 'Angelina Jolie' in 'France'.

SELECT 
     p.photoID, 
     p.setID, 
     p.headline, 
     p.caption,
     GROUP_CONCAT(pe.people ORDER BY pe.people ASC SEPARATOR ';') AS people
FROM 
     photos p
INNER JOIN
     (
        SELECT photoID 
        FROM photoSearch 
        WHERE MATCH (allKeywords, shortCaption)
        AGAINST ('+France' IN BOOLEAN MODE) 
        LIMIT 50
     ) pids ON p.photoID = pids.photoID
LEFT JOIN
     photoPeople pp ON p.photoID = pp.photoID
JOIN
     people pe ON pp.peopleID = pe.peopleID
     AND (pe.people = 'Angelina Jolie' OR pe.people = 'Brad Pitt')
GROUP BY p.photoID

If I remove the INNER JOIN section and run this, it returns all photos of Brad and Angelina whether they are together or not, which is incorrect - I just need them together. If I also remove the OR pe.people = 'Brad Pitt', it returns photos of Angelina correctly, I just can't get the whole thing linked up!

4

2 回答 2

2

要查找显示列表中所有人的照片,请使用

SELECT 
     p.photoID, 
     p.setID, 
     p.headline, 
     p.caption,
     GROUP_CONCAT(pe.people ORDER BY pe.people ASC SEPARATOR ';') AS people
FROM 
     photos p
INNER JOIN
     (
        SELECT photoID 
        FROM photoSearch 
        WHERE MATCH (allKeywords, shortCaption)
        AGAINST ('+red +dress +claridges +hotel' IN BOOLEAN MODE) 
        LIMIT 50
     ) pids 
   ON p.photoID = pids.photoID

INNER JOIN
     photoPeople pp ON p.photoID = pp.photoID
INNER JOIN
     people pe ON pp.peopleID = pe.peopleID
-- List of people
WHERE pe.People in ('Angelina Jolie', 'Brad Pit')
GROUP BY p.photoID
-- "2" must match number o people in the list
-- If you add third name, change this to 3
HAVING count(distinct pe.People) = 2

我已将左连接更改为内部连接,因为where会这样对待它们(因为 null 不能与任何东西进行比较),而且我觉得如果接受不匹配的照片,这个问题就没有意义。

PS您的尝试没有成功,因为它确实将所有安吉丽娜加入所有照片,并将所有布拉德加入所有照片,从而产生大量记录。您可能在 PhotoID 上连接了两个派生表以产生与上述查询相同的结果,但它需要另一个派生表来添加第三个名称。

SELECT photos1.PhotoID
FROM
(
  SELECT pp1.PhotoID
    FROM photoPeople AS pp1 
    JOIN people AS p1 ON pp1.peopleID = p1.peopleID
   WHERE p1.people = 'Brad Pitt'
) photos1
JOIN 
(
  SELECT pp2.PhotoID
    FROM photoPeople AS pp2
    JOIN people AS p2 ON pp2.peopleID = p2.peopleID
   WHERE p2.people = 'Angelina Jolie'
) photos2
  ON photos1.PhotoID = photos2.PhotoID

更新:组合条件以避免可能的limit 50不匹配:

SELECT 
     p.photoID, 
     p.setID, 
     p.headline, 
     p.caption,
     GROUP_CONCAT(pe.people ORDER BY pe.people ASC SEPARATOR ';') AS people
FROM 
     photos p
INNER JOIN
     (
        SELECT distinct ps.photoID 
        FROM photoSearch ps
        INNER JOIN
             photoPeople pp ON ps.photoID = pp.photoID
        INNER JOIN
             people pe ON pp.peopleID = pe.peopleID
     -- List of people
        WHERE pe.People in ('Angelina Jolie', 'Brad Pit')
          AND MATCH (allKeywords, shortCaption)
              AGAINST ('+red +dress +claridges +hotel' IN BOOLEAN MODE) 
        GROUP BY ps.photoID
     -- "2" must match number o people in the list
     -- If you add third name, change this to 3
        HAVING count(distinct pe.People) = 2
        LIMIT 50
     ) pids 
   ON p.photoID = pids.photoID
INNER JOIN
     photoPeople pp ON p.photoID = pp.photoID
INNER JOIN
     people pe ON pp.peopleID = pe.peopleID
GROUP BY p.photoID

重新编辑:注意到我忘记在主查询中留下与人相关的加入。

于 2012-09-10T23:37:53.757 回答
1

有没有照片人的照片?还是照片没有人的人?如果不是,请使用常规联接而不是左联接。然后,您可以在该查询上放置额外的 where 子句

where (people = "Brad Pitt" or people = "Angelina Jolie")

如果需要保持左连接,可以在ON中添加更多条件

... ON pp.peopleID = pe.peopleID and (people = "Brad Pitt" or people = "Angelina Jolie")

更新:

我的大脑看到的是自加入版本,而不是 Nikola 的并集:

select pp1.photoId
from photoPeople pp1, people p1, photoPeople pp2, people p2
where pp1.peopleID = p1.peopleID
and p1.people = "angelina"
and pp2.peopleID = p2.peopleID
and pp1.photoId = pp2.photoId
and p2.people = "brad"

但这让我想起了我建立的系统,您希望根据匹配的数量进行排名,而使用 UNIONS 更容易。

 SELECT photoID, count(*) as rank
 from (
  SELECT pp.PhotoID
    FROM photoPeople AS pp 
    JOIN people AS p ON pp.peopleID = p.peopleID
    WHERE p.people = 'Brad Pitt'
  UNION
  SELECT pp.PhotoID
    FROM photoPeople AS pp 
    JOIN people AS p ON pp.peopleID = p.peopleID
    WHERE p.people = 'Angelina Jolie'
 ) foo
 group by photoID
 order by rank desc

我会投票给尼古拉 :)

于 2012-09-10T23:31:32.787 回答