带连接的相关子查询

时间:2016-06-15 23:48:40

标签: mysql

我有3个表:alert,user_to_alert和user_device。设备表包含一个令牌列表,并包含一个user_id字段。

user_to_alert表保存用户何时读取警报的记录。如果相应的alert_id / user_id组合不存在记录,则表示用户尚未读取警报。还有一个alert_read列可以为零(如果用户在阅读后将警报标记为未读)。

我有一个加载设备令牌列表的查询,我需要展开它以包含未读警报的数量。这是我提出的查询

SELECT d.*, (
SELECT COUNT(*) FROM `alert` AS a 
LEFT JOIN `user_to_alert` AS uta ON 
( uta.`alert_id` = a.`alert_id` AND uta.`user_id` = d.`user_id` ) 
WHERE uta.`alert_read` = 0 OR uta.`alert_read` IS NULL) AS user_badge_number 
FROM `user_device` AS d  
WHERE d.`device_type` = 'iOS' AND d.`device_active` = 1 
GROUP BY d.`device_token`

此查询错误#1054 - Unknown column 'd.user_id' in 'on clause'

我已经使用架构和查询设置了一个SQLFiddle:http://sqlfiddle.com/#!9/ee653/2

如果我单独运行我的子查询并用d.user_id替换有效的user_id,我会得到预期的结果(即user_id 10为3)。我认为这意味着问题出在我引用列d.user_id的地方。我想MySQL首先尝试执行子查询,所以d.user_id不是一个有效的列呢?这只是一个猜测。

我已经尝试将我的ON子句的uta.user_id = d.user_id部分移动到WHERE子句,但是只为user_badge_number列提供了所有行。

我无法通过Google查找与此问题相关的任何内容。我发现关于相关子查询的所有内容似乎都在ON或WHERE子句中使用它们,而不是使用它们来检索额外的数据。

这是通过PHP脚本运行的,所以我知道我可以在PHP脚本的结果循环中自己运行子查询。显然,我宁愿弄清楚如何使用一个查询来实现它,因为这会更有效。

2 个答案:

答案 0 :(得分:0)

它是相关子查询中的范围问题,请尝试使用表名

SELECT d.*, (
    SELECT COUNT(*) FROM `alert` AS a 
    LEFT JOIN `user_to_alert` AS uta ON 
    ( uta.`alert_id` = a.`alert_id`) 
    WHERE  uta.`user_id` = d.`user_id`  AND 
            (uta.`alert_read` = 0 OR uta.`alert_read` IS NULL)) 
            AS user_badge_number 
FROM `user_device` AS d
WHERE d.`device_type` = 'iOS' AND d.`device_active` = 1 
GROUP BY d.`device_token`

答案 1 :(得分:0)

正如您所指出的,不难计算读取的警报数量。您可以从总警报中减去该数字以获取未读数字:

SELECT d.*, 
    ( SELECT COUNT(*) FROM `alert` ) - 
    ( SELECT COUNT(*) FROM `user_to_alert` 
        WHERE `user_id` = d.`user_id` AND `alert_read` = 1 )
FROM `user_device` AS d 
WHERE d.`device_active` AND d.`device_type` = 'iOS';

但请注意,设计存在问题,即如果在6个月后添加设备或用户,则过去的所有警报都将在该新设备上显示为未读。如果将设备重新分配给新用户,或者现有用户切换设备,则存在类似问题(取决于您是否计算未读警报每个用户每个设备)。

为了解决这个问题,您可以将查询限制在特定日期范围9,因为添加了用户或设备,但是如果设备被重新分配,则不清楚使用的日期。)我认为更简单的答案是在创建每个警报时将行插入user_to_alert。这有几个好处:

  • 易于计算用户或设备的未读行数
  • 新设备/用户不会继承旧警报
  • 允许您添加新功能:仅针对某些用户/设备的警报