mysql - 相同查询的不同 MYSQL 输出

标签 mysql

我有三个表:

事件:

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' 的行。

从内到外,它使用 fornamesurname 列中返回的所有值来计算 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/

相关文章:

php - 在 <tr> 中划分时,Mysql 第 4 行丢失(每表行 3 行,每列 1 行)

PHP 和 MySQL SELECT 子字符串

php - 如何使用一个mysql查询,更新数据而不重复?

php - 在sql中使用concat语句检索记录

php - VARCHAR 到 DATE - MySQL

c# - 当应用程序有很多调用时如何管理连接池限制?

mysql - ADO.NET 连接字符串和密码,其中包含 "="

mysql - 检索其存储的时间范围在给定工作日中出现一次或多次的行

php - WordPress 查询更新未更新所有帖子

php - 如何计算广告的有效点击次数?