【问题标题】:MYSQL select count for every unique row even if count is zeroMYSQL 为每个唯一行选择计数,即使计数为零
【发布时间】:2017-11-20 02:00:42
【问题描述】:

我在编写 select 时遇到了麻烦,因为我可以为每个唯一的行获取多个计数,我已经搜索了整个网络并编写了 select,但它没有按照我想要的方式工作。 所以,我有两张表横幅和横幅历史

banners
Id  UserId BannerName
167 35      Polly
168 35      Joke
169 21      Kim
170 35      Buck
................
BannerHistory
BannerId  UserId  IP
167       35      123asd123
167       35      123asd123
168       35      BBB123sss
.......................

So my query is:
SELECT
banners.Banner_Name,
banners.Counter,
banners.ClickCounter,
COUNT(DISTINCT(bannerhistory.Ip)) 
FROM
 bannerhistory 
  INNER JOIN
  banners 
  ON bannerhistory.BannerId = banners.ID 
  WHERE
   bannerhistory.BannerId IN 
   (
      SELECT
     bannerhistory.BannerId 
    FROM
     bannerhistory 
      WHERE
     bannerhistory.UserId = $ UserId 
       )
      GROUP BY
       banners.Banner_Name

我想获取 userId 广告的所有bannerNames 我想分别计算每个横幅的不同 IP。我的代码不起作用,因为计数它为每个横幅获得了不同的第一个横幅(计数(Ip)),我的意思是几乎总是计数是相同的。任何人都可以帮助纠正我的查询吗?或者告诉我不同​​的方法。 不用担心 counter 和 clickcounter 列,它们在 banners 表中没有什么可计算的,只需获取它们即可。 对不起我的英语。

【问题讨论】:

    标签: mysql select count


    【解决方案1】:

    尝试使用以下查询:

    Select b.Id,b.UserId,h.ips_count from banners b inner join 
    (select BannerId,count(distinct ip) as ips_count 
    from bannerhistory group by BannerId) h on b.Id = h.BannerId
    

    注意/建议:
    我认为在您的架构中,将 userId 保留在 bannerhistory 中是多余的,以防横幅表本身具有 id vs user 的引用, 所以在bannerhistory中,应该只有bannerId vs ip组合。

    【讨论】:

    • 感谢您的回答,我将 userId 保留在横幅历史记录中以用于不同的目的,但感谢您提供额外的提示。问题是,我已更改您的代码以获取一个用户 ID 的信息:Select b.Id,b.UserId,h.ips_count from banners b inner join (select BannerId,count(distinct ip) as ips_count from bannerhistory WHERE bannerhistory.UserId = 38 group by BannerId) h on b.Id = h.BannerId。我没有得到所有的横幅,我的错我没有提到并非所有的横幅都在横幅历史中,但它们可能会出现。或者我改错了。
    【解决方案2】:

    在这种情况下,您希望在子查询中执行聚合 (COUNT ... GROUP BY) 操作,然后执行联接。子查询如下所示,并获取历史表中用户和横幅 ID 的每个组合的不同 IP 值的数量。 (http://sqlfiddle.com/#!9/c502bb/3/0)

                SELECT BannerId, UserId, COUNT(DISTINCT IP) BannerCount
                  FROM bannerHistory
                 GROUP BY BannerId, UserId
    

    然后您可以将其加入到您的banner 表中,就像它是一个虚拟表一样,并使用WHERE 选择所需的用户ID。 (http://sqlfiddle.com/#!9/c502bb/2/0)

    SELECT b.Id, B.UserId, b.BannerName, c.BannerCount 
      FROM banner B
      JOIN (
                SELECT BannerId, UserId, COUNT(DISTINCT IP) BannerCount
                  FROM bannerHistory
                 GROUP BY BannerId, UserId
           ) C ON B.Id = c.BannerId AND B.UserId = C.UserId
     WHERE B.UserId = 35
    

    请注意,MySQL 查询计划器通常足够聪明,即使在子查询中也可以有效地处理WHERE 子句。

    编辑 最后,一个普通的内部JOIN 会抑制与ON 条件不匹配的行。要保留banner 表的每一行,您需要使用LEFT JOIN。 (http://sqlfiddle.com/#!9/c502bb/4/0)

    SELECT b.Id, B.UserId, b.BannerName, IFNULL(c.BannerCount, 0) BannerCount 
      FROM banner B
       LEFT JOIN (
                SELECT BannerId, UserId, COUNT(DISTINCT IP) BannerCount
                  FROM bannerHistory
                 GROUP BY BannerId, UserId
           ) C ON B.Id = c.BannerId AND B.UserId = C.UserId
     WHERE B.UserId = 35 
    

    注意使用SELECT ... IFNULLBannerHistory 表中缺失的行用零代替空值。

    专业提示:开发一种个人风格的查询代码缩进,以便您可以用结构化查询语言查看结构。缩进每个查询所花费的额外 30 秒将在您维护代码时得到数百倍的回报。

    【讨论】:

    • 感谢您的出色回答和其他提示,像魅力一样工作,不知道 IFNULL
    猜你喜欢
    • 2012-01-06
    • 1970-01-01
    • 2012-03-05
    • 2015-07-07
    • 2018-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多