【问题标题】:Trying to group on a column, while selecting all other information with ordering尝试对列进行分组,同时选择所有其他信息并排序
【发布时间】:2013-11-07 05:24:27
【问题描述】:

我在构建使用以下条件的查询时遇到了一些麻烦:

  1. 与组织匹配
  2. 按分数 (desc) 排序,然后按句柄 (asc) 排序
  3. 按类型分组

所以这个查询是我的出发点:

select * from social_media_handles where org = '00000001' order by score desc, handle asc;

这将为我提供以下数据......然后我需要按类型分组,所以我只提取最匹配的 social_media_handles。

   org    |                            handle                             |                   url                   |   type   |      score      | dataset_date
----------+---------------------------------------------------------------+-----------------------------------------+----------+-----------------+--------------
 00000001 | boathousesw15                                                 | http://www.boathouseputney.co.uk        | twitter  | 500111972000056 | 2013-10-15
 00000001 | aspall                                                        | http://www.boathouseputney.co.uk        | twitter  | 500111972000018 | 2013-10-15
 00000001 | nathansloane                                                  | http://www.boathouseputney.co.uk        | twitter  | 500111972000018 | 2013-10-15
 00000001 | youngspubs                                                    | http://www.boathouseputney.co.uk        | twitter  | 500111972000018 | 2013-10-15
 00000001 | pages/the-boathouse-putney/153429008029137                    | http://www.boathouseputney.co.uk        | facebook | 500111972000011 | 2013-10-15
 00000001 | putneysocial                                                  | http://www.boathouseputney.co.uk        | twitter  | 500111972000009 | 2013-10-15
 00000001 | theexchangesw15                                               | http://www.boathouseputney.co.uk        | twitter  | 500111972000009 | 2013-10-15
 00000001 | youngspubs                                                    | http://www.youngshotels.co.uk           | twitter  | 500111970000016 | 2013-10-15

预期输出

   org    |                            handle                             |                   url                   |   type   |      score      | dataset_date
----------+---------------------------------------------------------------+-----------------------------------------+----------+-----------------+--------------
 00000001 | boathousesw15                                                 | http://www.boathouseputney.co.uk        | twitter  | 500111972000056 | 2013-10-15
 00000001 | pages/the-boathouse-putney/153429008029137                    | http://www.boathouseputney.co.uk        | facebook | 500111972000011 | 2013-10-15

我尝试过group bydistinct 和子查询,但运气不佳。这个问题有规律吗?

我正在使用 Postgres,并通过 distinct on 解决了这个问题,但我正在寻找与不同供应商兼容的版本。

【问题讨论】:

  • 我不明白我们如何定义top matched social_media_handles !?!?!
  • @Strawberry,在score 列中每个type 中具有最高值的那些。
  • 啊哈,假设 Roman Pekar 的第二个解决方案适用于 postgres(稍作修正),那么我猜这将是最快的。
  • @Strawberry 我认为这取决于,如果您有 10000 条 type = 'facebook' 的记录并且您有适当的索引,那么第一个会更快
  • 好的 - 我不知道 postgres 所以我不能真正评论那种答案。

标签: sql postgresql group-by greatest-n-per-group


【解决方案1】:

有几种方法可以做到这一点,全部基于 2 个想法。第一个想法是获取每种类型的最大分数记录集,然后将原始表连接到该记录集。如果你有排名函数,第二个想法是可行的——你只需在每个type 中使用row_number(),然后用row_number > 1 过滤掉所有记录

所以第一个想法可以这样写:

select *
from Table1 as T
where
    exists (
        select 1
        from Table1 as TT
        where TT.type = T.type
        having max(TT.score) = T.score
    )

select T.*
from Table1 as T
    inner join (
        select max(TT.score), TT.type
        from Table1 as TT
        group by type
    ) as TT on TT.type = T.type and TT.score = T.score

如果你有排名功能,那么你也可以使用第二个想法:

with cte as (
   select *, row_number() over(partition by type order by score desc) as rn
   from Table1
)
select *
from cte
where rn = 1

您可以轻松地将公用表表达式替换为子查询:

select *
from (
   select *, row_number() over(partition by type order by score desc) as rn
   from Table1
) as a
where rn = 1

更新

有一点需要提一下 - 如果您有多个记录,例如 score = 500111972000056 and type = twitter,那么第一个解决方案将返回多个 type = 'twitter' 的记录,而第二个解决方案将返回一个任意行 type = '推特'

另外,我忘了提到第三个想法(请参阅@Bill Karwin 的好回答)。我将在此处添加它:

select *
from Table1 as T
where
    not exists (
        select *
        from Table1 as TT
        where TT.type = T.type and TT.score > T.score
    );

sql fiddle demo

【讨论】:

    【解决方案2】:

    这个问题在 SO 上经常出现,通常带有标签 (在您的情况下为 n=1)。

    以下是一些适用于 MySQL 的常见解决方案:

    SELECT h.*
    FROM social_media_handles AS h
    JOIN (
        SELECT type, MAX(score) AS score 
        FROM social_media_handles WHERE org = '00000001' 
        GROUP BY type) AS maxh USING (type, score)
    WHERE org = '00000001' 
    ORDER BY score DESC, handle ASC;
    

    第二种解决方案不使用子查询或分组依据。它尝试将行 h1 与具有相同 typeorg 但具有更高 score 的假设行 h1 匹配。如果不存在这样的得分较高的行 h2,则 h1 必须得分最高的行。

    SELECT h1.*
    FROM social_media_handles AS h1
    LEFT OUTER JOIN social_media_handles AS h2
     ON h1.type = h2.type AND h1.org = h2.org AND h1.score < h2.score
    WHERE h1.org = '00000001'
     AND h2.score IS NULL
    ORDER BY h1.score DESC, h1.handle DESC;
    

    哪种解决方案最快?这取决于。根据数据集的大小、不同类型的数量等,我的两种解决方案都做得更好。所以你应该测试这两种解决方案,看看哪种方案更适合你的情况。

    @Roman Pekar 展示的 CTE 解决方案也适用于支持 CTE 语法的 RDBMS。其中包括 PostgreSQL、Oracle、Microsoft SQL Server、IBM DB2 等。

    MySQL 和 SQLite 是仅有的仍不支持 CTE 语法的广泛使用的数据库。

    【讨论】:

    • +1 用于第二种解决方案并提到没有灵丹妙药:),我忘了不存在一个。也许我应该在我的答案中补充一点,如果原始表中有重复项,解决方案的工作方式可能会有所不同
    • @RomanPekar,如果您有多行且每组都具有最高分,那么您会在结果中获得不止一行。这就是 CTE 解决方案最好的方式。
    • 为让我了解每个组中最伟大的人而欢呼。这是我必须深入研究的事情。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-15
    • 1970-01-01
    • 2021-01-29
    • 2022-10-08
    • 1970-01-01
    • 2017-05-12
    • 1970-01-01
    相关资源
    最近更新 更多