MySQL 中的 GROUP_CONCAT - 如何包含不同的值?

Posted

技术标签:

【中文标题】MySQL 中的 GROUP_CONCAT - 如何包含不同的值?【英文标题】:GROUP_CONCAT in MySQL - how do I include distinct values? 【发布时间】:2012-12-03 21:07:21 【问题描述】:

我有两张表,我用 UNION ALL 查询 - 一张用于 android,一张用于 iPhone。每个表都有自己的设备字段(“android”或“iphone”)。

我使用GROUP_CONCAT(DISTINCT `device` ORDER BY `device` SEPARATOR ', ') AS `device` 几次,每次我按另一列分组时(例如日期、user_id 等)。我也使用相同的 GROUP_CONCAT 来计算总数。

问题是,当我按日期分组时,我选择了设备的 GROUP_CONCAT(不是直接设备),因为有些日期同时购买了 Android 和 iPhone。选择还包括 WHERE 或 HAVING 让用户按特定日期过滤,加入日期的用户数量等。当我计算总数时,我在设备上有 GROUP_CONCAT,这本身就是一个 GROUP_CONCAT 函数。结果可能类似于“android, android, iphone, iphone”,因为有只有 Android 的日期、只有 iPhone 的日期和两者都有的日期(其他查询只能有这些选项中的一个或一些)。我正在寻找一种方法将此结果转换为“android, iphone”。

目前我正在使用 php 函数:

    private function get_device_human_string($fp_device_computer_string)
    
        $devices= array(
            'android' => 'Android',
            'iphone' => 'iPhone'
        );
        $device_computer_string= strtolower($fp_device_computer_string);
        $ret= array();
        foreach ($devices as $device_key => $device_human_string)
        
            if (strpos($device_computer_string, $device_key) !== false)
            
                $ret[]= $device_human_string;
            
        
        return implode(', ', $ret);
    

但我正在寻找一种在 mysql 中执行此操作的方法(返回的结果在“Android”中也应该有一个大写 A,在“iPhone”中应该有一个大写 P,但我不介意使用 PHP 函数为此)。

顺便说一下,总的 SELECT 查询是 SELECT .... FROM (SELECT .... FROM (.. UNION ALL ....) GROUP BY ....) 并且 GROUP BY 是里面。如果我在内部查询中不包含设备,那么外部查询中将没有设备到 GROUP_CONCAT。所以我不能直接在设备上进行 GROUP_CONCAT。

编辑:这是我正在使用的查询示例(WHERE 和 HAVING 可以根据用户的过滤器进行更改):

SELECT
  COUNT(1) AS `count`,
  SUM(`joined`) AS `joined`,
  SUM(`users`) AS `users`,
  SUM(`purchases`) AS `purchases`,
  SUM(`credits_purchased`) AS `credits_purchased`,
  GROUP_CONCAT(DISTINCT `device` ORDER BY `device` SEPARATOR ', ') AS `device`,
  GROUP_CONCAT(DISTINCT `application` ORDER BY `application` SEPARATOR ', ') AS `application`
FROM (
  SELECT
    `all_purchases`.*,
    IF(`users_joined`.`joined` IS NOT NULL, `users_joined`.`joined`, 0) AS `joined`,
    'esalne' AS `application`
  FROM (
    SELECT
      DATE(`date`) AS `date`,
      COUNT(DISTINCT `user_id`) AS `users`,
      COUNT(1) AS `purchases`,
      SUM(
        IF(
          STRCMP(SUBSTRING(`item`, 1, CHAR_LENGTH('esalne.sip.')), 'esalne.sip.')=0,
          CAST(SUBSTRING(`item`, CHAR_LENGTH('esalne.sip.')+1) AS UNSIGNED INTEGER),
          0
        )
      ) AS `credits_purchased`,
      GROUP_CONCAT(DISTINCT `device` ORDER BY `device` SEPARATOR ', ') AS `device`
    FROM (
      (
        SELECT 
         `id`,
          `item`,
          `date`,
          `status`,
          `user` AS `user_id`,
          NULL AS `transaction_id`,
          'android' AS `device`
        FROM `enswitch_android_purchases`
        WHERE (`status`=1)
          AND (`user` IS NOT NULL)
      )
      UNION ALL
      (
        SELECT
          `id`,
          `item`,
          `date`,
          `status`,
          `user` AS `user_id`,
          `transaction_id`,
          'iphone' AS `device`
        FROM `enswitch_iphone_purchases`
        WHERE (`status`=1)
          AND (`user` IS NOT NULL)
      )
    ) AS `all_purchases`
    GROUP BY DATE(`date`)
  ) AS `all_purchases`
  LEFT JOIN (
    SELECT
      `join_date` AS `date`,
      COUNT(1) AS `joined`
    FROM (
      SELECT
        `user_id`,
        MIN(DATE(`date`)) AS `join_date`
      FROM (
        (
          SELECT
            `id`,
            `item`,
            `date`,
            `status`,
            `user` AS `user_id`,
            NULL AS `transaction_id`,
            'android' AS `device`
          FROM `enswitch_android_purchases`
          WHERE (`status`=1)
            AND (`user` IS NOT NULL)
        )
        UNION ALL
        (
          SELECT
            `id`,
            `item`,
            `date`,
            `status`,
            `user` AS `user_id`,
            `transaction_id`,
            'iphone' AS `device`
          FROM `enswitch_iphone_purchases`
          WHERE (`status`=1)
            AND (`user` IS NOT NULL)
        )
      ) AS `all_purchases`
      GROUP BY `user_id`
    ) AS `users`
    GROUP BY `date`
  ) AS `users_joined` ON (`all_purchases`.`date`=`users_joined`.`date`)
  HAVING (`date` >= DATE_ADD('2012-11-01', INTERVAL 0 DAY))
     AND (`date` < DATE_ADD('2012-11-30', INTERVAL 1 DAY))
     AND (`joined` >= 2)
     AND (`purchases` <= 30)
     AND (`credits_purchased` <= 3000)
) AS `all_purchases_by_dates`

谢谢, 乌里。

【问题讨论】:

如果您不发布实际查询,没有人可以帮助您。 【参考方案1】:

只是一个草稿来说明一个想法:

select d1.device, d2.device, ...
from (
    select 'android' as device
    union
    select 'iphone' as device 
    ) as devices
left outer join mydata d1 on d1.device = devices.device and devices.device = 'android'
left outer join mydata d2 on d2.device = devices.device and devices.device = 'iphone'
inner join ...
where ...

使用“虚拟”表和外连接,您可以获得androidiphone 的所有记录,这样两个设备都不是空的,或者其中至少一个是非空的。 如果您愿意,可以将 d1.deviced2.device 连接起来,或者以任何其他合适的方式评估它们到 group by 这些。

【讨论】:

以上是关于MySQL 中的 GROUP_CONCAT - 如何包含不同的值?的主要内容,如果未能解决你的问题,请参考以下文章

如何仅使用“group_concat”过滤表 mysql 中的行

Mysql 中的group_concat函数的使用及陷阱

Mysql 中的group_concat函数的使用及陷阱

MySql 存储过程.... group_concat 中的错误

mysql中的 group_concat

mysql 对 group_concat 中的项目进行计数