MySQL - 喜欢+ JOIN

时间:2012-09-14 17:38:21

标签: mysql join concat group-concat

我想在我的数据库中搜索用户,并为每个用户在另一个表上创建一个JOIN。这是我的表格:

USERSidfirstnamelastname,[...] - 包含用户数据

FRIENDSfan_ididol_id,[...] - 哪个用户关注哪个用户

这是我的搜索查询,经典:

SELECT `id`, `username`, `firstname`, `lastname`, `lang`, `twitter`, `facebook`, `picture`, `regdate`
FROM `users`
WHERE `username` LIKE ?
OR `email` LIKE ?
OR `firstname` LIKE ?
OR `lastname` LIKE ?
OR `twitter` LIKE ?
OR `facebook` LIKE ?
ORDER BY `username`;

所以现在,我想要一个获取朋友表,包含搜索到的用户和询问此用户的用户之间的关系。

以下是我的想法:

SELECT `id`, `username`, `firstname`, `lastname`, `lang`, `twitter`, `facebook`, `picture`, `regdate`,
GROUP_CONCAT(
    CONCAT_WS(':', `fan_id`, `idol_id`) SEPARATOR ';'
) AS relations
FROM `friends`, `users`
WHERE (`username` LIKE ?
OR `email` LIKE ?
OR `firstname` LIKE ?
OR `lastname` LIKE ?
OR `twitter` LIKE ?
OR `facebook` LIKE ?)
AND (
(`fan_id` = 100 AND `idol_id` = `id`)
OR
(`fan_id` = `id` AND `idol_id` = 100)
)
ORDER BY `username`;

实际上,如果搜索到的用户显示为relations而搜索用户显示为fan_id或相反,我希望进入idol_id。 但是,当我搜索具有第一个查询的用户时,我有2行,2个用户(ids = 80& 125)。对于第二个查询,我只有一行(id = 80),但relations显示100:80;100:125,表示我有一半的查询工作...

有关信息,如果没有(relations),我也希望得到一个结果,所以我尝试了IFNULL(..., 0),但仅此而已。

感谢您的帮助。

2 个答案:

答案 0 :(得分:3)

这是基于我认为你的目标的猜测:

select users.id,users.firstname,
case 
when CONCAT_WS(';',t1.fans,t2.idols)='' then null
else CONCAT_WS(';',t1.fans,t2.idols)
end as relations
from users left join 
(
select CONCAT_WS(':',fan_id,idol_id) as fans,idol_id
from friends
where fan_id=80
) as t1
on users.id=t1.idol_id
left join
(
select CONCAT_WS(':',fan_id,idol_id) as idols,fan_id
from friends
where idol_id=80
) as t2
on users.id=t2.fan_id
where id=100

Sqlfiddle是ID为100且正在搜索ID为80的用户的结果。

where fan_id=80where idol_id=80需要设置为正在搜索的用户的正确ID,where id=100可以替换为查找用户进行搜索的人员。或者你可以删除这三个并获得每个人彼此关系的列表。

--- --- EDIT

Sqlfiddle

此查询提供的细节较少,但仅通过朋友搜索一次,用户只搜索一次;特别是它不再保存idol和fan列中的id,而是这些列为1或null。 1表示用户是另一个用户的用户,因此用户125搜索用户100并且具有fan - null | idol - 1,这意味着他们不是用户100的粉丝,但他们是用户100的偶像。关系列为在我之前的评论中建议,对于粉丝和偶像都是2,对于其中一个是1,或者两者都不是。

select users.id,users.firstname,sum(t1.fan) as fan,sum(t1.idol) as idol,
case 
when t1.fan is null and t1.idol is null then null
else count(*) 
end as relations
from users
left join
(
select fan_id,idol_id,
case when fan_id=100 then 1 end as idol,
case when idol_id=100 then 1 end as fan
from friends
where fan_id=100 or idol_id=100
) as t1
on users.id=t1.fan_id or users.id=t1.idol_id
where firstname like '%user1%'
group by id

您必须更改所搜索用户的ID的所有100个地方。底部的where子句可以填充你想要的任何内容。

答案 1 :(得分:2)

使用标准JOIN语法要比使用您正在使用的过时omega-join模型容易得多。

在多表查询中,您可能应该限定列名。

使用所谓的潜在客户表开始加入。在你的情况下,你想要每个用户一行,所以用它开始连接。

您的WHERE中的OR条件的长级联将导致您的查询执行得非常糟糕。您可能需要考虑某种FULLTEXT(二进制模式)搜索。

反引号禁止可读性,除非表或列与保留字同名,否则不需要。

您在查询中有一个GROUP_CONCAT汇总函数,但没有相应的GROUP BY子句。你需要两者。 (有一些允许的语言快捷方式,其中GROUP BY不是必需的,但是在调试时应避免使用这些快捷方式。)

我想弄清楚你的加入标准,但我不明白。我不明白id = 100有什么特别之处。但是,在你加入的第二部分,你是否有可能想要idol_id = 100而不是fan_id = 100

             (f.fan_id = u.id AND  f.idol_id = 100)

对于你的项目,你所拥有的东西似乎不合乎逻辑。

我建议你试试这个:

SELECT u.id, u.username, 
       u.firstname, u.lastname, 
       u.lang, u.twitter, u.facebook, 
       u.picture, u.regdate,
       GROUP_CONCAT(CONCAT_WS(':', f.fan_id, f.idol_id) SEPARATOR ';') AS relations
  FROM users u
  JOIN friends f ON  (                  
             (f.fan_id = 100 AND f.idol_id = u.id)
                OR
             (f.fan_id = u.id AND  f.idol_id = 100)
            )
 WHERE u.username LIKE ?
    OR u.email LIKE ?
    OR u.firstname LIKE ?
    OR u.lastname LIKE ?
    OR u.twitter LIKE ?
    OR u.facebook LIKE ?
 GROUP BY u.id, u.username, 
       u.firstname, u.lastname, 
       u.lang, u.twitter, u.facebook, 
       u.picture, u.regdate
 ORDER BY u.username