【问题标题】:Using DISTINCT inside JOIN is creating trouble [duplicate]在 JOIN 中使用 DISTINCT 会造成麻烦 [重复]
【发布时间】:2013-02-02 04:41:38
【问题描述】:

可能重复:
How can I modify this query with two Inner Joins so that it stops giving duplicate results?

我无法让我的查询正常工作。

SELECT itpitems.identifier, itpitems.name, itpitems.subtitle, itpitems.description, itpitems.itemimg, itpitems.mainprice, itpitems.upc, itpitems.isbn, itpitems.weight, itpitems.pages, itpitems.publisher, itpitems.medium_abbr, itpitems.medium_desc, itpitems.series_abbr, itpitems.series_desc, itpitems.voicing_desc, itpitems.pianolevel_desc, itpitems.bandgrade_desc, itpitems.category_code, itprank.overall_ranking, itpitnam.name AS artist, itpitnam.type_code FROM itpitems 
        INNER JOIN  itprank ON (itprank.item_number = itpitems.identifier) 
        INNER JOIN  (SELECT DISTINCT type_code FROM itpitnam) itpitnam ON (itprank.item_number = itpitnam.item_number)   
        WHERE mainprice > 1    
        LIMIT 3

我在“字段列表”中不断收到未知列“itpitnam.name”。

但是,如果我将 DISTINCT type_code 更改为 *,我不会收到该错误,但也不会得到我想要的结果。

这是一个很大的结果表,所以我正在制作一个虚拟示例......

使用 *,我得到类似:

+-----------+---------+----------+
| identifier| name    | type_code|
+-----------+---------+----------+
| 2         | Joe     | A        |
| 2         | Amy     | R        |
| 7         | Mike    | B        |
+-----------+------------+-------+

这里的问题是我有两个 identifier = 2 实例,因为 type_code 不同。 我已经在查询的外部尝试了 GROUP BY,但是它正在筛选这么多的记录,这会给服务器造成太大的压力,所以我正在尝试寻找另一种方法来获取我需要的结果。

我想要实现的(使用相同的虚拟输出)看起来像这样:

+-----------+---------+----------+
| identifier| name    | type_code|
+-----------+---------+----------+
| 2         | Joe     | A        |
| 7         | Mike    | B        |
| 8         | Sam     | R        |
+-----------+------------+-------+

无论 type_code 是否不同,它都应该跳过重复的标识符。

有人可以帮我修改这个查询以获得上图中模拟的结果吗?

【问题讨论】:

    标签: mysql sql greatest-n-per-group


    【解决方案1】:

    一种方法是使用内联视图,就像您已有的查询一样。但是,您可以使用 GROUP BY 来消除重复项,而不是使用 DISTINCT。满足您要求的最简单的内联视图是:

    ( SELECT n.item_number, n.name, n.type_code
        FROM itpitnam n
       GROUP BY n.item_number
    ) itpitnam
    

    虽然它不确定从itpitnam 的哪一行检索name 和type_code 的值。更精细的内联视图可以使其更具体。


    解决此类问题的另一种常见方法是在 SELECT 列表中使用相关子查询。对于返回一小组行,这可以执行得相当好。但是对于返回大集合,有更有效的方法。

    SELECT i.identifier
         , i.name
         , i.subtitle
         , i.description
         , i.itemimg 
         , i.mainprice
         , i.upc
         , i.isbn
         , i.weight
         , i.pages
         , i.publisher
         , i.medium_abbr
         , i.medium_desc
         , i.series_abbr
         , i.series_desc
         , i.voicing_desc
         , i.pianolevel_desc
         , i.bandgrade_desc
         , i.category_code
         , r.overall_ranking
         , ( SELECT n1.name
               FROM itpitnam n1
              WHERE n1.item_number = r.item_number
              ORDER BY n1.type_code, n1.name
              LIMIT 1
           ) AS artist
         , ( SELECT n2.type_code
               FROM itpitnam n2
              WHERE n2.item_number = r.item_number
              ORDER BY n2.type_code, n2.name
              LIMIT 1
           ) AS type_code
      FROM itpitems i
      JOIN itprank r
        ON r.item_number = i.identifier
     WHERE mainprice > 1
     LIMIT 3
    

    该查询将返回指定的结果集,但有一个显着差异。原始查询显示itpitnam 表的内部联接。这意味着只有在itpitnam 表中有匹配行时才会返回一行。但是,上面的查询模拟了 OUTER JOIN,当在 itpitnam 中找不到匹配的行时,查询将返回一行。


    更新

    为了获得这些相关子查询的最佳性能,您需要一个合适的可用索引,

    ... ON itpitnam (item_number, type_code, name)
    

    该索引是最合适的,因为它是一个“覆盖索引”,可以完全从索引中满足查询,而无需引用基础表中的数据页,并且在前导列上有相等谓词,在下一个列上有 ORDER BY两列,这样可以避免“排序”操作。

    --

    如果您保证 itpitnam 表中的 type_codename 列不为 NULL,则可以添加谓词来消除“缺少”匹配行的行,例如

    HAVING artist IS NOT NULL
    

    (添加它可能会对性能产生影响。)如果没有这种保证,您需要添加一个 INNER JOIN 或一个测试匹配行是否存在的谓词,以获得 INNER JOIN 行为。


    【讨论】:

    • 哇!我什至不知道如何告诉你这有多棒。这正是我所需要的,我也不需要保证匹配,所以它可以完美地工作。我注意到您可能想要编辑的一个小错误。我相信您的意思是键入 SELECT n2.type_code。您确实为 SELECT n1 命名。无论如何,谢谢!!!!
    • @osakagreg:是的,应该是n2.type_code。我已经更新了我的答案以纠正这个问题。相关子查询可能很方便,但我还要(再次)警告说,对于大型集合,它们可能比其他方法效率低。为了获得最佳性能,您需要适当的索引...ON itpitnam (item_number, type_code, name),我在回答中添加了一条注释。
    【解决方案2】:
    SELECT  a.*
            b.overall_ranking, 
            c.name AS artist, 
            c.type_code 
    FROM    itpitems a
            INNER JOIN  itprank b 
                ON b.item_number = a.identifier
            INNER JOIN  itpitnam c
                ON b.item_number = c.item_number
            INNER JOIN
            (
                SELECT  item_number, MAX(type_code) code
                FROM    itpitnam
                GROUP   BY item_number
            ) d ON  c.item_number = d.item_number AND
                    c.type_code = d.code
    
    WHERE   mainprice > 1    
    LIMIT   3
    

    后续问题:能否请您发布表模式以及表之间的关系?所以我会知道要链接哪些列。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-11
      • 2015-09-17
      • 2023-03-09
      • 1970-01-01
      • 2016-07-16
      • 2022-07-22
      • 1970-01-01
      • 2017-01-11
      相关资源
      最近更新 更多