2

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

  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 by,distinct和子查询,但运气不佳。有没有围绕这个问题的模式?

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

4

2 回答 2

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 与具有相同typeand的假设行 h1 匹配org,但具有更高的score。如果不存在得分较高的这样的行 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 语法的广泛使用的数据库。

于 2013-10-28T12:44:58.890 回答
1

有几种方法可以做到这一点,都基于两个想法。第一个想法是获取每种类型的最大分数记录集,然后将原始表连接到该记录集。如果您具有排名功能,则第二个想法有效-您只需row_number()在每个内部使用type,然后过滤掉所有记录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 = 'twitter'

另外,我忘了提到第三个想法(见很好的@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

于 2013-10-28T12:33:53.643 回答