web-dev-qa-db-ja.com

MySQL:N行を選択しますが、1つの列に一意の値のみがあります

このデータセットを考えます:

ID  Name            City            Birthyear
1   Egon Spengler   New York        1957
2   Mac Taylor      New York        1955
3   Sarah Connor    Los Angeles     1959
4   Jean-Luc Picard La Barre        2305
5   Ellen Ripley    Nostromo        2092
6   James T. Kirk   Riverside       2233
7   Henry Jones     Chicago         1899

最古の3人を見つける必要がありますが、すべての都市の1つだけです。

それが3つの最も古い場合、それは...

  • ヘンリー・ジョーンズ/シカゴ
  • マック・テイラー/ニューヨーク
  • エゴン・スペングラー/ニューヨーク

しかし、エゴン・スペングラーとマック・テイラーの両方がニューヨークに位置しているため、エゴン・スペングラーは脱落し、代わりにエゴン・スペングラー(サラ・コナー/ロサンゼルス)が入ってきます。

エレガントなソリューションはありますか?

更新:

現在、PConroyのバリエーションが最適/最速のソリューションです。

SELECT P.*, COUNT(*) AS ct
   FROM people P
   JOIN (SELECT MIN(Birthyear) AS Birthyear
              FROM people 
              GROUP by City) P2 ON P2.Birthyear = P.Birthyear
   GROUP BY P.City
   ORDER BY P.Birthyear ASC 
   LIMIT 10;

「IN」を使用した彼の元のクエリは、大きなデータセット(5分後に中止)を使用すると非常に遅くなりますが、サブクエリをJOINに移動すると速度が大幅に向上します。約0.15秒かかりました。テスト環境で1 mio行。 「City、Birthyear」に関するインデックスと、「Birthyear」に関する2つ目のインデックスがあります。

注:これは関連しています...

34
BlaM

おそらく最もエレガントなソリューションではなく、INのパフォーマンスは大きなテーブルで低下する可能性があります。

ネストされたクエリは、各都市の最小Birthyearを取得します。このBirthyearを持つレコードのみが外部クエリで一致します。年齢で並べ替えてから3つの結果に制限すると、都市で最も高齢の3人の高齢者が得られます(Egon Spenglerは脱落します。)

SELECT Name, City, Birthyear, COUNT(*) AS ct
FROM table
WHERE Birthyear IN (SELECT MIN(Birthyear)
               FROM table
               GROUP by City)
GROUP BY City
ORDER BY Birthyear DESC LIMIT 3;

+-----------------+-------------+------+----+
| name            | city        | year | ct |
+-----------------+-------------+------+----+
| Henry Jones     | Chicago     | 1899 | 1  |
| Mac Taylor      | New York    | 1955 | 1  |
| Sarah Connor    | Los Angeles | 1959 | 1  |
+-----------------+-------------+------+----+

編集-出生年が同じ人が複数の値を返すため、GROUP BY Cityを外部クエリに追加しました。外側のクエリでグループ化すると、複数の人がその最小Birthyearを持っている場合、都市ごとに1つの結果のみが返されることが保証されます。 ct列には、そのBirthyearを持つ都市に複数の人が存在するかどうかが表示されます

18
ConroyP

これはおそらく最もエレガントで迅速なソリューションではありませんが、機能するはずです。実際のデータベースの達人の解決策を楽しみにしています。

select p.* from people p,
(select city, max(age) as mage from people group by city) t
where p.city = t.city and p.age = t.mage
order by p.age desc
3
Tamas Czinege

そんな感じ?

SELECT
  Id, Name, City, Birthyear
FROM
  TheTable
WHERE
  Id IN (SELECT TOP 1 Id FROM TheTable i WHERE i.City = TheTable.City ORDER BY Birthyear)
2
Tomalak

しゅう

[〜#〜] updated [〜#〜] ONの代わりにUSINGを使用するとよいことがわかりました。結果の重複する列を削除します。

SELECT P.*, COUNT(*) AS ct
   FROM people P
   JOIN (SELECT City, MIN(Birthyear) AS Birthyear
              FROM people 
              GROUP by City) P2 USING(Birthyear, City)
   GROUP BY P.City
   ORDER BY P.Birthyear ASC 
   LIMIT 10;

オリジナルポスト

こんにちは、更新されたクエリを使用しようとしましたが、追加の条件を追加するまで間違った結果が得られました(追加の列を結合選択に追加します)。あなたのクエリに転送され、私はこれを使用しています:

SELECT P.*, COUNT(*) AS ct
   FROM people P
   JOIN (SELECT City, MIN(Birthyear) AS Birthyear
              FROM people 
              GROUP by City) P2 ON P2.Birthyear = P.Birthyear AND P2.City = P.City
   GROUP BY P.City
   ORDER BY P.Birthyear ASC 
   LIMIT 10;

理論的には、最後のGROUP BY P.Cityは必要ないはずですが、念のためにここに置いておきました。おそらく後で削除します。

1
gondo

きれいではありませんが、同じdobを持つ複数の人でも動作するはずです:

テストデータ:

select id, name, city, dob 
into people
from
(select 1 id,'Egon Spengler' name, 'New York' city , 1957 dob
union all select 2, 'Mac Taylor','New York', 1955
union all select 3, 'Sarah Connor','Los Angeles', 1959
union all select 4, 'Jean-Luc Picard','La Barre', 2305
union all select 5, 'Ellen Ripley','Nostromo', 2092
union all select 6, 'James T. Kirk','Riverside', 2233
union all select 7, 'Henry Jones','Chicago', 1899
union all select 8, 'Blah','New York', 1955) a

クエリ:

select 
    * 
from 
    people p
    left join people p1
    ON 
        p.city = p1.city
        and (p.dob > p1.dob and p.id <> p1.id)
        or (p.dob = p1.dob and p.id > p1.id)
where
    p1.id is null
order by 
    p.dob
1
kristof