【问题标题】:MySQL using GROUP BY after multiple UNIONSMySQL 在多个 UNION 之后使用 GROUP BY
【发布时间】:2018-05-10 19:24:29
【问题描述】:

我有一个相当复杂的 MySQL 查询,其中包含多个 UNION 语句。我试图从最终输出中消除重复项,但并非所有列都相同(包括 ID)。所以我想在“名称”列上使用GROUP BY 来消除具有相同名称的条目。这是用于许多其他地图图层的地图混搭。一些地点标记出现在多个图层上(即餐厅可能出现在“餐厅”图层、“酒吧”图层和“家庭烹饪”图层上,每个图层都有不同的 ID)。

每个UNION SELECT 语句只生成一行包含该地点的“名称”,但到最后,在所有UNIONs 运行之后,我对同一个地点有多个条目。所以我觉得我需要在每个UNION 发生后以某种方式处理所有数据上的GROUP BY

我将尝试用简化版本的语句来说明......

(
    SELECT l.id AS lid, m.markername AS markername, m.id AS mid, m.layer AS mlayer
    FROM layertable 
    INNER JOIN markertable 
    ON m.layer LIKE Concat('%"',l.id,'"%') 
    WHERE l.id='66' 
    ## RESULT INCLUDES Joes Place AND Eatery
)
UNION
(
    SELECT l.id AS lid, m.markername AS markername, m.id AS mid, m.layer AS mlayer
    FROM layertable 
    INNER JOIN markertable 
    ON m.layer LIKE Concat('%"',l.id,'"%') 
    WHERE l.id='82' 
    ## RESULT INCLUDES Joes Place AND Eatery
)
UNION
(
    SELECT l.id AS lid, m.markername AS markername, m.id AS mid, m.layer AS mlayer
    FROM layertable 
    INNER JOIN markertable 
    ON m.layer LIKE Concat('%"',l.id,'"%') 
    WHERE l.id='91' 
    ## RESULT INCLUDES Joes Place
)

ORDER BY markername ASC
LIMIT 10

结果看起来像...

| lid | markername | mid | mlayer               |
=================================================
| 66  | Joes Place | 10  | ["66","82","91"]     |
| 82  | Joes Place | 10  | ["66","82","91"]     |
| 91  | Joes Place | 10  | ["66","82","91"]     |
| 66  | Eatery     | 11  | ["66","82"]          |
| 82  | Eatery     | 11  | ["66","82"]          |

我想要的是……

| lid | markername | mid | mlayer               |
=================================================
| 91  | Joes Place | 10  | ["66","82","91"]     |
| 82  | Eatery     | 11  | ["66","82"]          |

DISTINCT 似乎不起作用,因为记录并不完全相同。

我尝试在ORDER BY 之前和之后放置GROUP BY markername,但无论哪种方式我都会收到语法错误消息。在单个 SELECTS 中应用它并没有帮助,因为每个表都只会有一个地方实例。

所以,重申我的问题: 如何在UNION 之后将GROUP BY 应用于总列表并仅输出唯一命名的地点? 或者还有其他方法可以完成这项任务吗?

提前致谢。

【问题讨论】:

  • 如果您需要 id 值,为什么要尝试丢弃除一个之外的所有值,每个都有一个 group by?如果没有,为什么要选择它们?
  • 另外,你为什么使用 UNION 而WHERE l.id IN ('66', '82', '91') 会更清晰,可能更快?
  • 这是在地图本身下方显示地图标记的列表。我不需要该列表来显示 Joes Place 3 次。只有一个。地图本身已经消除了重复的标记,但开箱即用的列表却没有。
  • 为什么不对联合结果进行查询呢? SELECT * FROM (<union query>) GROUP BY markername
  • 其实不是,这是因为lid 在每一行上都是不同的。只是不要选择该字段。从每个查询中删除 l.id AS lid,。如果您对它们的独特价值不感兴趣,我认为这意味着您实际上对它们根本不感兴趣。所以不要选择它们,你不会有重复的。

标签: mysql group-by distinct union


【解决方案1】:

您可以对所有结果进行GROUP BY,例如:

SELECT * FROM 
(
    SELECT l.id AS lid, m.markername AS markername, m.id AS mid, m.layer AS mlayer
    FROM layertable AS l
    INNER JOIN markertable 
    ON m.layer LIKE Concat('%"',l.id,'"%') 
    WHERE l.id='66' 
    ## RESULT INCLUDES Joes Place AND Eatery
)
UNION
(
    SELECT l.id AS lid, m.markername AS markername, m.id AS mid, m.layer AS mlayer
    FROM layertable AS l
    INNER JOIN markertable 
    ON m.layer LIKE Concat('%"',l.id,'"%') 
    WHERE l.id='82' 
    ## RESULT INCLUDES Joes Place AND Eatery
)
UNION
(
    SELECT l.id AS lid, m.markername AS markername, m.id AS mid, m.layer AS mlayer
    FROM layertable AS l
    INNER JOIN markertable 
    ON m.layer LIKE Concat('%"',l.id,'"%') 
    WHERE l.id='91' 
    ## RESULT INCLUDES Joes Place
)

ORDER BY markername ASC
LIMIT 10
) AS makernames
GROUP BY makername

或者您可以忽略导致结果不唯一的部分。喜欢:

(
    SELECT m.markername AS markername, m.id AS mid, m.layer AS mlayer
    FROM layertable AS l
    INNER JOIN markertable 
    ON m.layer LIKE Concat('%"',l.id,'"%') 
    WHERE l.id='66' 
    ## RESULT INCLUDES Joes Place AND Eatery
)
UNION
(
    SELECT m.markername AS markername, m.id AS mid, m.layer AS mlayer
    FROM layertable AS l
    INNER JOIN markertable 
    ON m.layer LIKE Concat('%"',l.id,'"%') 
    WHERE l.id='82' 
    ## RESULT INCLUDES Joes Place AND Eatery
)
UNION
(
    SELECT m.markername AS markername, m.id AS mid, m.layer AS mlayer
    FROM layertable AS l
    INNER JOIN markertable 
    ON m.layer LIKE Concat('%"',l.id,'"%') 
    WHERE l.id='91' 
    ## RESULT INCLUDES Joes Place
)

ORDER BY markername ASC
LIMIT 10 

您不需要在SELECT 子句中包含l.id,它也可以在WHERE 子句中工作。而且我假设如果您愿意在某些行上丢失 lid 只是为了每个 makername 只有一行,那意味着您实际上根本不需要 lid 在结果中。

【讨论】:

    【解决方案2】:

    您可以使用 group by 和 max(id)

      select max(lid), markername, mid, mlayer 
      from (
    
    
      (
          SELECT l.id AS lid, m.markername AS markername, m.id AS mid, m.layer AS mlayer
          FROM layertable 
          INNER JOIN markertable 
          ON m.layer LIKE Concat('%"',l.id,'"%') 
          WHERE l.id='66' 
          ## RESULT INCLUDES Joes Place AND Eatery
      )
      UNION
      (
          SELECT l.id AS lid, m.markername AS markername, m.id AS mid, m.layer AS mlayer
          FROM layertable 
          INNER JOIN markertable 
          ON m.layer LIKE Concat('%"',l.id,'"%') 
          WHERE l.id='82' 
          ## RESULT INCLUDES Joes Place AND Eatery
      )
      UNION
      (
          SELECT l.id AS lid, m.markername AS markername, m.id AS mid, m.layer AS mlayer
          FROM layertable 
          INNER JOIN markertable 
          ON m.layer LIKE Concat('%"',l.id,'"%') 
          WHERE l.id='91' 
          ## RESULT INCLUDES Joes Place
      )
    
      ORDER BY markername ASC
      LIMIT 10 
      ) t
      group by markername, mid, mlayer 
    

    【讨论】:

    • 这个也适合我。不过,我认为安东尼的评论更适合我的情况。
    • 这个更便携,也更面向未来;其他数据库和最近 MySQL 版本的默认配置,不允许 group by 包含既不分组也不聚合的字段。
    • @cvc Anthony 提供的解决方案在最新版本的 mysql (5.7) 中不起作用 .. 因为在 group by 中没有提到没有聚合功能的 select 列之前使用 group by 是在 SQL 中已弃用,未聚合列的结果是不可预测的,并且如前所述.. 在更新的 mysql 版本中不起作用..
    【解决方案3】:

    您可以通过一个查询完成所有操作:

    SELECT max(l.id) AS lid, m.markername AS markername, m.id AS mid, m.layer AS mlayer
    FROM layertable 
    INNER JOIN markertable 
    ON m.layer LIKE Concat('%"',l.id,'"%') 
      WHERE l.id in ('66','82','91')
    GROUP BY m.markername, m.id, m.layer
    

    【讨论】:

      【解决方案4】:

      (这应该是注释,但有点长)

      因为记录不完全相同

      那么你需要更具体地了解你所说的重复是什么意思。在您的示例中,您提取了 MAX(l.id) - 这是您的意图吗?

      你为什么在这里使用 UNION?你可以...

      SELECT MAX(lid), markername, mid, mlayer
      FROM ( 
        SELECT l.id AS lid, m.markername, m.id AS mid, m.layer AS mlayer
        FROM layertable 
        INNER JOIN markertable 
        ON m.layer LIKE Concat('%"',l.id,'"%') 
        WHERE l.id IN ('91', '82', '66')
        LIMIT 10
      ) AS ilv
      GROUP BY markername, mid, mlayer
      

      使用 LIKE is a JOIN 很糟糕,这意味着您的数据未标准化。为什么在您的查询中引用整数值?

      (请注意,子选择可能是多余的/加快或减慢速度,具体取决于数据的分布和可用的索引)。

      您不认为应该围绕地理空间索引来组织地图数据吗?

      【讨论】:

        猜你喜欢
        • 2012-09-11
        • 2012-08-08
        • 2017-06-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-22
        • 1970-01-01
        相关资源
        最近更新 更多