我有三个表:
事件:
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=?
然后我就找到了合适的主人(罗布·沃森和彼得·沃森)。我还可以将其更改为:
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=?
这也能正常工作。因此,LEFT JOIN
的第二个 OR
条件似乎正在摧毁我的所有者:(
反向测试(条件相同且没有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
输出。
我的错误在哪里?
最佳答案
使用不带 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
它使用上面显示的查询选择的所有行来计算上面表达式的单个值。如果运行查询,您可以看到它在 persons.type
列中生成同时包含 'event_owner'
和 'event_organizer'
的行。
从内到外,它使用 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
以类似的方式,您可以计算组织者名称。
关于mysql - 相同查询的不同 MYSQL 输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34810178/