web-dev-qa-db-ja.com

DESC順序でグループ化する方法

質問と呼ばれる次の表があります。

ID | asker 
1  | Bob
2  | Bob
3  | Marley

各質問者を1回だけ選択し、同じ名前の質問者が複数いる場合は、最も高いIDの質問者を選択します。したがって、期待される結果:

ID | asker 
3  | Marley
2  | Bob

次のクエリを使用します。

SELECT * FROM questions GROUP by questions.asker ORDER by questions.id DESC

次の結果が得られます。

ID | asker 
3  | Marley
1  | Bob

したがって、最後のものではなく、最初に遭遇した「ボブ」を選択します。

ありがとう

26
Michael Samuel

idごとに最後のaskerが必要な場合は、集約関数を使用する必要があります。

SELECT max(id) as id, 
   asker
FROM questions 
GROUP by asker 
ORDER by id DESC

異常な結果が得られた理由は、MySQLがGROUP BYの拡張機能を使用して、選択リスト内のアイテムを非集計にし、GROUP BY句に含めないようにするためです。ただし、MySQLは返される値を選択できるため、これにより予期しない結果が生じる可能性があります。 ( GROUP BYのMySQL拡張機能 を参照)

MySQL Docsから:

MySQLは、GROUP BY句で指定されていない非集計列を選択リストが参照できるように、GROUP BYの使用を拡張します。 ...この機能を使用して、不必要な列のソートとグループ化を回避することにより、パフォーマンスを向上させることができます。ただし、これは主に、GROUP BYで指定されていない各非集計列のすべての値が各グループで同じ場合に役立ちます。サーバーは各グループから任意の値を自由に選択できるため、それらが同じでない限り、選択される値は不定です。さらに、各グループからの値の選択は、ORDER BY句を追加しても影響を受けません。結果セットのソートは値が選択された後に行われ、ORDER BYはサーバーが選択する値に影響しません。

テーブルから返す必要がある他の列があり、取得できる一貫性のない結果のためにGROUP BYに追加したくない場合は、サブクエリを使用して返すことができます。 ( デモ

select 
  q.Id,
  q.asker,
  q.other -- add other columns here
from questions q
inner join
(
  -- get your values from the group by
  SELECT max(id) as id, 
    asker
  FROM questions 
  GROUP by asker 
) m
  on q.id = m.id
order by q.id desc
43
Taryn

通常、MySQLは昇順のレコードのみでグループを許可します。したがって、グループ化する前にレコードを注文できます。

 SELECT * 
 FROM(
 SELECT * 
 FROM質問
 ORDER BY id DESC 
)AS質問
 GROUP BY question.asker 
18
Kapil dev

すべてのaskerの最大IDを取得するには、_GROUP BY_およびMAX()を使用してレコードをグループ化する必要があります。

_SELECT  asker, MAX(ID) ID
FROM    TableName
GROUP   BY asker
_

出力

_╔════════╦════╗
║ ASKER  ║ ID ║
╠════════╬════╣
║ Bob    ║  2 ║
║ Marley ║  3 ║
╚════════╩════╝
_
3
John Woo

他のユーザーは、MAX(ID)を使用して必要な結果を取得することについて正しいです。クエリが機能しない理由がわからない場合は、ORDER BYが発生しますGROUP BY

2
Michael L.

この回答を書いているのは、受け入れられた回答の@Tarynの最初/より短い代替案が、GROUP BYおよびMAXで使用される列だけを正確に選択している場合にのみ機能するためです。ユーザーが質問するのは、テーブル内のすべての列を選択することです(彼はSELECT *を使用しました)。そのため、テーブルに別の3番目の列を追加すると、クエリ結果のその列の値が正しくなくなります。異なるテーブル行から混合値を取得します。 @Tarynの2番目/より長い選択肢(内部結合とサブクエリを使用)は機能しますが、クエリは無駄に複雑で、は私の単純な選択肢よりも5倍遅いです以下。


テーブルquestionsを考慮してください:

_id | asker 
-----------
1  | Bob
2  | Bob
3  | Marley
_

クエリSELECT max(id) as id, asker FROM questions GROUP BY asker ORDER BY id DESCは、expectedを返します。

_id | asker 
-----------
3  | Marley
2  | Bob
_

次に、別のテーブルquestionsを考えます。

_id | asker  | other
-------------------
1  | Bob    | 1st
2  | Bob    | 2nd
3  | Marley | 3rd
_

クエリSELECT max(id) as id, asker, other FROM questions GROUP BY asker ORDER BY id DESCは以下を返しますunexpected

_id | asker  | other
-------------------
3  | Marley | 3rd
2  | Bob    | 1st
_

...結果の2行目のotherの値は、_id=2_は表の2行目から来るが、_other=1st_は表の1行目から来るため、正しくないことに注意してください。これが、タリンの回答レポートの多くのユーザーが、このソリューションが機能しないと報告している方法です。


別の列も選択する場合の簡単な解決策は、_GROUP BY_ + DESCを使用することです。

_SELECT id, asker, other FROM questions GROUP BY asker DESC_

_id | asker  | other
-------------------
3  | Marley | 3rd
2  | Bob    | 2nd
_

(デモを参照: https://www.db-fiddle.com/f/esww483qFQXbXzJmkHZ8VT/1

...しかし、この単純なソリューションにはいくつかの制限があります。

  • テーブルはInnoDBである必要があります(パフォーマンスが向上し、MySQL> = 5.5.5デフォルト/優先ストレージエンジンがMyISAMからInnoDBに変更されたため、問題はないと思います)
  • GROUP BYで使用される列のインデックスを作成する必要があります-この場合asker(この場合インデックスが適切であるため、パフォーマンスが向上するため問題ありません。GROUPBYは通常作成が必要ですtmpテーブルのが、インデックスが利用可能な場合、tmpテーブルは作成されないため、高速です)
  • MySQL 5.7および8.0の場合、エラーER_WRONG_FIELD_WITH_GROUPを回避するために、SQLモードONLY_FULL_GROUP_BY(たとえば_SET SESSION sql_mode = '';_)を無効にするか、集約されていない選択列でANY_VALUE()を使用する必要があります。
  • 残念ながら、MySQL開発者は、MySQL 8.0以降、GROUP BYを使用したASC/DESCのサポートを削除しました https://dev.mysql.com/worklog/task/?id=869 しかし、幸いなことに、代替_GROUP BY col1 ORDER BY col1 ASC/DESC_があります:

_SELECT id, asker, other FROM questions GROUP BY asker ORDER BY asker DESC_

_id | asker  | other
-------------------
3  | Marley | 3rd
2  | Bob    | 2nd
_

(デモを参照: https://www.db-fiddle.com/f/esww483qFQXbXzJmkHZ8VT/11

...結果は_GROUP BY ... DESC_の場合と同じです(InnoDBを使用してインデックスを作成することを忘れないでください)。

0
mikep

その理由は ORDER BYが実行されます[〜#〜] after [〜#〜]GROUP BY

これを試して:

SELECT * FROM questions
WHERE id IN 
(
    SELECT max(id) as id
    FROM questions 
    GROUP by asker 
    ORDER by id DESC
)
0
evilReiko

すべての列を取得するには:

SELECT * FROM questions
WHERE id IN 
(SELECT max(id) as id, asker
FROM questions 
GROUP by asker 
ORDER by id DESC)

@bluefeetの回答の改善されたバージョン。

0
Olimjon