【问题标题】:Keep all records in "WHERE IN()" clause, even if they are not found保留“WHERE IN()”子句中的所有记录,即使它们没有找到
【发布时间】:2012-11-20 22:41:06
【问题描述】:

我有以下 mysql 查询:

SELECT id, sum(views) as total_views
FROM table
WHERE id IN (1,2,3)
GROUP BY id
ORDER BY total_views ASC

如果在数据库中只找到id 1,3,我仍然希望出现id 2,并将total_views 设置为0。

有没有办法做到这一点?这不能使用任何其他表。

【问题讨论】:

  • 您从哪里获取要放入“IN”子句的数据?就像 1,2,3 是固定的还是动态的你得到它
  • Michaels 的回答可能是这个问题的最佳解决方案,除非你可以使用 PHP 或其他东西。如果可以的话,解决方案会变得容易得多。
  • 我刚刚注意到“不能使用任何其他表”的评论。我敢问为什么?这个限制是否适用于临时表? (我的假设只是某些主管禁止您为此目的创建一个永久表。)
  • 这里有类似问题的答案:stackoverflow.com/questions/10229575/…>

标签: mysql


【解决方案1】:

此查询使用由联合组成的子查询对可能的 ID 列表进行硬编码...然后将这组 ID 左连接到包含要计数的信息的表中。

即使没有出现,这也会在您的结果中保留一个 ID:

SELECT ids.id, sum(views) as total_views
FROM (
    SELECT 1 AS ID 
    UNION ALL SELECT 2 AS ID 
    UNION ALL SELECT 3 AS ID 
) ids 
LEFT JOIN table
    ON table.ID = ids.ID
GROUP BY ids.id
ORDER BY total_views ASC

或者,如果您有 numbers table,您可以执行以下查询:

SELECT numbers.number, sum(views) as total_views
FROM
    numbers
    LEFT JOIN table
        ON table.ID = ids.ID
WHERE numbers.number IN (1, 2, 3)
GROUP BY numbers.number
ORDER BY total_views ASC

【讨论】:

  • IN() 子句中可以有数千个 id。我怎么会那样做?有什么方法可以使用 if/then?
  • 有一个数字表,但它在不同服务器上的不同数据库中,否则我可以做一个简单的左连接。
  • @k1lljoy 我假设您不允许在此服务器上创建新的数字表...但是您有权创建临时表吗?如果是这样,您可以使用正在传入的数千个 ID 填充一个临时表,从该表执行 LEFT JOIN 到您的视图表,然后完全取消大的 IN 语句。
  • 由于运行这些查询的服务器数量众多,每台服务器都有自己的部分数据供他们查看,因此创建临时表也是不可能的。其次,我每次都必须在主数据库中查询这些数据,这有点违背了分片数据库的目的。
  • @k1lljoy,无论是在 IN 子句、隐式 UNION 表还是显式临时表中,ID 集都将驻留在服务器的内存中。这只是数据以什么“格式”存在以及查询如何对其进行操作的问题(可能作为临时表最有效)。
【解决方案2】:

这是 Micheal 解决方案的替代方案(请注意,这不是一个糟糕的解决方案 - 即使有“很多”ID),只要您不针对集群进行查询即可。

create temporary table __ids (
  id int unsigned primary key
) engine=MEMORY;

insert into __ids (id) values
  (1),
  (2),
  (3)
;

SELECT table.id, sum(views) as total_views
FROM __ids left join table using (id)
GROUP BY table.id
ORDER BY total_views ASC

如果您的查询变得复杂,我什至可以设想它以这种方式运行更有效。但是,如果我是你,我会将此选项与 Michael 使用真实数据的临时 UNION 表选项进行基准测试。

【讨论】:

  • 我会选择这样做。但是不确定 SELECT 中是否需要 WHERE 子句(所选 ID 已由 JOIN 处理)。
  • 啊,碰一下。我会编辑它。我还将添加临时关键字,我似乎忽略了它。
  • +1...这是我懒得写的临时表的实现。
  • 这需要用相对大量的数据填充这个临时表,这些数据必须从主数据库传输,这违背了整个目的。
  • 在这里,我会说你错了!无论哪种方式,您都必须通过连接“传输” ID 列表。这只是您选择在以下三种基本“格式”中的哪一种:显式临时表、隐式临时表(UNION 解决方案)或作为 IN 子句的问题,让您在应用程序端解决问题。
【解决方案3】:

在@Michael 的回答中,如果您确实有一个包含您关心的 id 的表,您可以将其用作“id”来代替 Michael 的内联数据。

【讨论】:

    【解决方案4】:

    检查这个小提琴...http://www.sqlfiddle.com/#!2/a9392/3

    Select B.ID, sum(A.views) sum from tableB B
     left outer join tableA A
     on B.ID = A.ID
    group by A.ID
    

    还检查 http://www.sqlfiddle.com/#!2/a1bb7/1

    【讨论】:

      【解决方案5】:

      试试这个

          SELECT id 
                      (CASE 1
                          IF EXISTS THEN  views = mytable.views END
                          IF NOT EXIST THEN views = 0 END
                      CASE 2
                          IF EXISTS THEN  views = mytable.views END
                          IF NOT EXIST THEN views = 0 END
                      CASE 3
                          IF EXISTS THEN  views = mytable.views END
                          IF NOT EXIST THEN views = 0 END), sum(views) as total_views
          FROM mytable
          WHERE id IN (1,2,3)
          GROUP BY id
          ORDER BY total_views ASC
      

      【讨论】:

        【解决方案6】:

        它必须是行还是您可以旋转数据以为每个 id 提供一行和一列?

        SELECT
          SUM(IF (id=1, views, 0)) views_1,
          SUM(IF (id=2, views, 0)) views_2,
          SUM(IF (id=3, views, 0)) views_3
        FROM table
        

        【讨论】:

          猜你喜欢
          • 2018-04-14
          • 2019-08-05
          • 2014-06-17
          • 1970-01-01
          • 2010-10-25
          • 2016-01-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多