具有相同查询的不同MYSQL输出

时间:2016-01-15 11:35:07

标签: 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=?

我得到了正确的主人(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输出。

我的错误在哪里?

2 个答案:

答案 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的行。

从内到外,它使用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

以类似的方式,您可以计算组织者名称。