我有三张桌子:
事件:
id | name
-------------------
1 | Test
2 | Another test
人:
type | type_id | name | user_id
------------------------------------------------
event_organizer | 318 | NULL | 22
event_owner | 318 | Rob | NULL
event_owner | 318 | NULL | 6
用户:
id | forname | surname
--------------------------
6 | Peter | Watson
7 | Chris | Brown
22 | Charlie | Teck
(当然,表格比那些大得多,我只是复制了相关部分。)
此查询:
SELECT event.*,
IF(persons.type='event_organizer', CONCAT_WS(', ', GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '), GROUP_CONCAT(persons.name SEPARATOR ', ')), NULL) AS organizer_names,
IF(persons.type='event_owner', GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '), NULL) AS owner_names
FROM event
LEFT JOIN persons ON persons.type_id = event.id AND (persons.type = 'event_owner' OR persons.type = 'event_organizer')
LEFT JOIN user ON user.id = persons.user_id
WHERE event.id=?
应输出所有事件数据以及所有者和组织者的姓名。我得到的输出是:
Array ( [id] => 318 [name] => Test [organizer_names] => Peter Watson, Rob, Charlie Teck [owner_names] => )
我不明白为什么owner_names
总是空的。如果我删除organizer_names
部分:
SELECT event.*,
IF(persons.type='event_owner', GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '), NULL) AS owner_names
FROM event
LEFT JOIN persons ON persons.type_id = event.id AND persons.type = 'event_owner'
LEFT JOIN user ON user.id = persons.user_id
WHERE event.id=?
我得到了正确的主人(Rob和Peter Watson)。我也可以将其更改为:
SELECT event.*,
IF(persons.type='event_organizer', CONCAT_WS(', ', GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '), GROUP_CONCAT(persons.name SEPARATOR ', ')), NULL) AS organizer_names,
IF(persons.type='event_owner', GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '), NULL) AS owner_names
FROM event
LEFT JOIN persons ON persons.type_id = event.id AND persons.type = 'event_owner'
LEFT JOIN user ON user.id = persons.user_id
WHERE event.id=?
这也正常。所以似乎OR
的第二个LEFT JOIN
条件正在摧毁我的主人:(
反向测试(条件相同但没有organizer_names
):
SELECT event.*,
IF(persons.type='event_owner', GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '), NULL) AS owner_names
FROM event
LEFT JOIN persons ON persons.type_id = event.id AND (persons.type = 'event_owner' OR persons.type = 'event_organizer')
LEFT JOIN user ON user.id = persons.user_id
WHERE event.id=?
也没有owner_names
输出。
我的错误在哪里?
答案 0 :(得分:1)
我无法对此进行测试 - SQLFiddle现在似乎已被破坏 - 但我认为您需要明确地将组织者和所有者作为单独的联接加入。类似的东西:
SELECT event.*,
organizers.*,
IF(organizers.type='event_organizer', CONCAT_WS(', ', GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '), GROUP_CONCAT(persons.name SEPARATOR ', ')), NULL) AS organizer_names,
owners.* ,
IF(owners.type='event_owner', GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '), NULL) AS owner_names
FROM event
LEFT JOIN persons organizers ON organizers.type_id = event.id AND organizers.type = 'event_organizer'
left join persons owners on owners.type_id = event.id and owners.type = 'event_owner'
LEFT JOIN user ON user.id = owners.user_id
left join user u2 on u2.id = organizers.user_id
WHERE event.id=?;
答案 1 :(得分:1)
在没有GROUP BY
子句的情况下使用GROUP BY
汇总函数有效SQL
。该标准表示从所有选定的行创建单个组。
对于此特定问题,相应的GROUP BY
子句将为GROUP BY event.id
。但是,它没有帮助,因为WHERE
子句只过滤了event.id
具有单个值的行。
要调试查询,请删除SELECT
子句中的所有聚合函数,并检查是否选择了正确的行:
SELECT event.*, persons.*, users.*
FROM event
LEFT JOIN persons ON persons.type_id = event.id AND (persons.type = 'event_owner' OR persons.type = 'event_organizer')
LEFT JOIN user ON user.id = persons.user_id
WHERE event.id=?
从概念上讲,这是MySQL
在运行原始查询时执行的第一步。在实践中,会采取一些快捷方式并进行优化,并且不会完全生成您得到的结果集;但这就是它在理论上的运作方式。
最有可能的是,行已正确连接和过滤,问题在于聚合值的方式。
让我们尝试分析MySQL
如何计算这个表达式:
IF(
persons.type='event_owner',
GROUP_CONCAT(forname, ' ', surname SEPARATOR ', '),
NULL
) AS owner_names
它使用上面显示的查询选择的所有行来计算上述表达式的单个值。如果您运行查询,则可以看到它会在'event_owner'
列中生成包含'event_organizer'
和persons.type
的行。
从内到外,它使用forname
和surname
列中返回的所有值来计算GROUP_CONCAT(forname, ' ', surname SEPARATOR ', ')
,然后使用persons.type
列中的一个值进行检查IF()
条件。但是哪一个?
它有权从上一步中选择的persons.type
列的所有可能值中选择它想要的任何值。如果它偶然使用'event_owner'
,它会返回您期望的值,并且您认为查询是正确的。当它使用'event_organizer'
时,你很困惑并在StackOverflow上询问;)
owner_names
的正确表达是:
GROUP_CONCAT(
IF( # for each row
persons.type='event_owner',
CONCAT(forname, ' ', surname), # full name for owner
NULL # nothing for organizer
) # owner name or NULL
SEPARATOR ', ' # concatenate all using separator
) as owner_names
以类似的方式,您可以计算组织者名称。