2

次の条件を使用するクエリを作成するのに少し問題があります。

  1. 組織との照合
  2. スコア (降順) で並べ替え、次にハンドル (昇順) で並べ替え
  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いますが、さまざまなベンダーと互換性のあるバージョンを探しています。

4

2 に答える 2

2

この問題は SO で頻繁に発生し、通常は (この場合はn=1 ) というタグが付けられます。

以下は、MySQL で機能する一般的なソリューションの 2 つです。

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;

2 番目のソリューションでは、サブクエリもグループ化も使用しません。行 h1 を仮想の行 h1 に一致させようとしますが、 は同じtypeorg、 は より大きくなり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

これを行うにはいくつかの方法がありますが、すべて 2 つのアイデアに基づいています。最初のアイデアは、各タイプの最大スコアを持つレコードセットを取得し、元のテーブルをこのレコードセットに結合することです。ランキング関数がある場合、2 番目のアイデアが機能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

ランキング関数がある場合は、2 番目のアイデアも使用できます。

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最初のソリューションはタイプ = 'twitter' の複数のレコードを返し、2 番目のソリューションはタイプ = 'twitter' の任意の行を 1 つ返します。

また、3 番目のアイデアについて言及するのを忘れていました (@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 に答える