281

ここには同様の質問がたくさんありますが、質問に適切に答えるものはないと思います。

現在最も人気のある質問から続けて、よろしければその例を使用します。

このインスタンスのタスクは、データベース内の各作成者の最新の投稿を取得することです。

クエリの例では、常に最新の投稿が返されるとは限らないため、使用できない結果が生成されます。

SELECT wp_posts.* FROM wp_posts
    WHERE wp_posts.post_status='publish'
    AND wp_posts.post_type='post'
    GROUP BY wp_posts.post_author           
    ORDER BY wp_posts.post_date DESC

現在受け入れられている答えは

SELECT
    wp_posts.*
FROM wp_posts
WHERE
    wp_posts.post_status='publish'
    AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author
HAVING wp_posts.post_date = MAX(wp_posts.post_date) <- ONLY THE LAST POST FOR EACH AUTHOR
ORDER BY wp_posts.post_date DESC

残念ながら、この答えは明白で単純な間違いであり、多くの場合、元のクエリよりも安定した結果が得られません。

私の最善の解決策は、フォームのサブクエリを使用することです

SELECT wp_posts.* FROM 
(
    SELECT * 
    FROM wp_posts
    ORDER BY wp_posts.post_date DESC
) AS wp_posts
WHERE wp_posts.post_status='publish'
AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author 

私の質問は単純なもの です:サブクエリに頼らずにグループ化する前に行を並べ替える方法はありますか?

編集:この質問は別の質問からの続きであり、私の状況の詳細はわずかに異なります。その特定の投稿の一意の識別子である wp_posts.id もあると想定できます (想定する必要があります)。

4

12 に答える 12

421

サブクエリでを使用することORDER BYは、この問題に対する最良の解決策ではありません。

作成者によるを取得する最善の解決策max(post_date)は、サブクエリを使用して最大日付を返し、それを とpost_author最大日付の両方でテーブルに結合することです。

解決策は次のとおりです。

SELECT p1.* 
FROM wp_posts p1
INNER JOIN
(
    SELECT max(post_date) MaxPostDate, post_author
    FROM wp_posts
    WHERE post_status='publish'
       AND post_type='post'
    GROUP BY post_author
) p2
  ON p1.post_author = p2.post_author
  AND p1.post_date = p2.MaxPostDate
WHERE p1.post_status='publish'
  AND p1.post_type='post'
order by p1.post_date desc

次のサンプル データがある場合:

CREATE TABLE wp_posts
    (`id` int, `title` varchar(6), `post_date` datetime, `post_author` varchar(3))
;

INSERT INTO wp_posts
    (`id`, `title`, `post_date`, `post_author`)
VALUES
    (1, 'Title1', '2013-01-01 00:00:00', 'Jim'),
    (2, 'Title2', '2013-02-01 00:00:00', 'Jim')
;

サブクエリは、次の最大日付と作成者を返します。

MaxPostDate | Author
2/1/2013    | Jim

次に、それをテーブルに結合しているため、両方の値で、その投稿の完全な詳細が返されます。

SQL Fiddle with Demoを参照してください。

サブクエリを使用してこのデータを正確に返すことについての私のコメントを拡張するには。

MySQL は、リストGROUP BYに含めるすべての列を強制するわけではありません。SELECTその結果、GROUP BY1 つの列だけで合計 10 列を返す場合、その列に属する他の列の値post_authorが返される保証はありません。列がGROUP BYMySQL にない場合、返される値を選択します。

集計関数でサブクエリを使用すると、正しい作成者と投稿が毎回返されることが保証されます。

補足として、MySQL ではORDER BYサブクエリでan を使用できGROUP BY、リスト内のすべての列ではなくa を適用できますが、SELECTこの動作は SQL Server を含む他のデータベースでは許可されていません。

于 2013-02-08T10:53:42.320 に答える
27

あなたのソリューションは、いくつかのフィールド(この場合はちょうど)でグループ化することを許可するGROUP BY 句の拡張をpost_author利用します:

GROUP BY wp_posts.post_author

集計されていない列を選択します。

SELECT wp_posts.*

group by 句にリストされていない、または集計関数 (MIN、MAX、COUNT など) で使用されていないもの。

GROUP BY 句の拡張の正しい使用

これは、集計されていない列のすべての値がすべての行で等しい場合に便利です。

たとえば、GardensFlowers(nameflowerで育つ、庭の)テーブルがあるとします。

INSERT INTO GardensFlowers VALUES
('Central Park',       'Magnolia'),
('Hyde Park',          'Tulip'),
('Gardens By The Bay', 'Peony'),
('Gardens By The Bay', 'Cherry Blossom');

複数の花が生育する庭で育つすべての花を抽出したいと考えています。次に、サブクエリを使用する必要があります。たとえば、次のように使用できます。

SELECT GardensFlowers.*
FROM   GardensFlowers
WHERE  name IN (SELECT   name
                FROM     GardensFlowers
                GROUP BY name
                HAVING   COUNT(DISTINCT flower)>1);

代わりに庭にある唯一の花をすべて抽出する必要がある場合は、 HAVING 条件を に変更するだけで済みますがHAVING COUNT(DISTINCT flower)=1、MySql では次のように使用することもできます。

SELECT   GardensFlowers.*
FROM     GardensFlowers
GROUP BY name
HAVING   COUNT(DISTINCT flower)=1;

サブクエリはなく、標準 SQL ではありませんが、より単純です。

GROUP BY 句の拡張の不適切な使用

しかし、すべての行が等しくない集計されていない列を SELECT するとどうなるでしょうか? MySql がその列に選択する値はどれですか?

MySql は常に、遭遇したFIRST値を選択するようです。

最初に検出した値が正確に目的の値であることを確認するにはGROUP BY、順序付きクエリに a を適用する必要があるため、サブクエリを使用する必要があります。それ以外の場合はできません。

MySql が常に最初に遭遇した行を選択するという仮定を考えると、GROUP BY の前に行を正確にソートしていることになります。しかし、残念ながら、ドキュメントを注意深く読むと、この仮定が正しくないことに気付くでしょう。

常に同じではない集計されていない列を選択すると、MySql は任意の値を自由に選択できるため、実際に表示される結果の値は indeterminate になります。

集計されていない列の最初の値を取得するこのトリックが頻繁に使用されていることがわかります。通常/ほとんど常に機能します。私も時々使用します(自己責任で)。ただし、文書化されていないため、この動作に依存することはできません。

このリンク (ypercube に感謝します!) GROUP BY トリックが最適化されましたは、おそらく異なる最適化エンジンが原因で、同じクエリが MySql と MariaDB の間で異なる結果を返す状況を示しています。

したがって、このトリックが機能するかどうかは運次第です。

他の質問で受け入れられた答えは、 私には間違っているように見えます:

HAVING wp_posts.post_date = MAX(wp_posts.post_date)

wp_posts.post_datepost_dateは集計されていない列であり、その値は公式には未定ですが、最初に遭遇する可能性があります。しかし、GROUP BY トリックは順序付けされていないテーブルに適用されるため、どれが最初に検出されるかはわかりませんpost_date

おそらく、単一の作成者の唯一の投稿である投稿が返されますが、これも常に確実であるとは限りません。

可能な解決策

これが可能な解決策になると思います:

SELECT wp_posts.*
FROM   wp_posts
WHERE  id IN (
  SELECT max(id)
  FROM wp_posts
  WHERE (post_author, post_date) = (
    SELECT   post_author, max(post_date)
    FROM     wp_posts
    WHERE    wp_posts.post_status='publish'
             AND wp_posts.post_type='post'
    GROUP BY post_author
  ) AND wp_posts.post_status='publish'
    AND wp_posts.post_type='post'
  GROUP BY post_author
)

内部クエリでは、すべての著者の最大投稿日を返しています。次に、同じ著者が理論的には同時に 2 つの投稿を行うことができるという事実を考慮しているため、最大 ID のみを取得しています。そして、それらの最大 ID を持つすべての行を返しています。IN 句の代わりに結合を使用して高速化できます。

(それIDが増加しているだけで、それID1 > ID2も意味する場合post_date1 > post_date2は、クエリをもっと簡単にすることができますが、そうであるかどうかはわかりません)。

于 2013-02-08T11:14:48.587 に答える
10

あなたが読むつもりのものはかなりハッキーなので、家でこれを試さないでください!

SQLでは、一般に、質問に対する答えはNOですが、( @ bluefeetGROUP BYで言及されている)のリラックスモードのため、MySQLでは答えはYESです。

(post_status、post_type、post_author、post_date)にBTREEインデックスがあるとします。インデックスは内部でどのように見えますか?

(post_status ='publish'、post_type ='post'、post_author ='user A'、post_date = '2012-12-01')(post_status ='publish'、post_type ='post'、post_author ='user A'、 post_date = '2012-12-31')(post_status ='publish'、post_type ='post'、post_author ='user B'、post_date = '2012-10-01')(post_status ='publish'、post_type = ' post'、post_author ='ユーザーB'、post_date ='2012-12-01')

つまり、データはこれらすべてのフィールドで昇順で並べ替えられます。

GROUP BYデフォルトでデータを並べ替える場合(post_authorこの場合、post_status、post_typeは句で必須です)WHERE、一致するインデックスがある場合は、最初のレコードごとに昇順でデータを取得します。つまり、クエリは以下をフェッチします(各ユーザーの最初の投稿):

(post_status ='publish'、post_type ='post'、post_author ='user A'、post_date = '2012-12-01')(post_status ='publish'、post_type ='post'、post_author ='user B'、 post_date = '2012-10-01')

ただしGROUP BY、MySQLでは、順序を明示的に指定できます。post_userまた、降順でリクエストすると、逆の順序でインデックスをウォークスルーし、実際に最後である各グループの最初のレコードを取得します。

あれは

...
WHERE wp_posts.post_status='publish' AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author DESC

私たちに

(post_status ='publish'、post_type ='post'、post_author ='user B'、post_date = '2012-12-01')(post_status ='publish'、post_type ='post'、post_author ='user A'、 post_date = '2012-12-31')

これで、post_dateでグループ化の結果を並べ替えると、必要なデータが得られます。

SELECT wp_posts.*
FROM wp_posts
WHERE wp_posts.post_status='publish' AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author DESC
ORDER BY wp_posts.post_date DESC;

注意

これは、この特定のクエリに対して私が推奨するものではありません。この場合、@bluefeetが提案するもののわずかに変更されたバージョンを使用します。しかし、このテクニックは非常に役立つかもしれません。ここで私の答えを見てください:各グループの最後のレコードを取得する

落とし穴:このアプローチの欠点は、

  • クエリの結果はインデックスに依存しますが、これはSQLの精神に反しています(インデックスはクエリを高速化するだけです)。
  • インデックスはクエリへの影響について何も知りません(将来、あなたや他の誰かがインデックスをリソースを消費しすぎて変更し、パフォーマンスだけでなくクエリ結果を壊してしまう可能性があります)
  • クエリがどのように機能するかを理解していない場合、おそらく1か月で説明を忘れてしまい、クエリによってあなたと同僚が混乱する可能性があります。

利点は、ハードケースでのパフォーマンスです。この場合、並べ替えに関連するデータの量が多いため、クエリのパフォーマンスは@bluefeetのクエリと同じである必要があります(すべてのデータが一時テーブルに読み込まれてから並べ替えられます。ところで、彼のクエリには(post_status, post_type, post_author, post_date)インデックスも必要です) 。

私が提案すること

私が言ったように、これらのクエリは、MySQLが一時テーブル内の潜在的に大量のデータをソートする時間を無駄にします。ページングが必要な場合(つまり、LIMITが含まれる場合)、ほとんどのデータは破棄されます。私が行うことは、並べ替えられたデータの量を最小限に抑えることです。つまり、サブクエリ内の最小限のデータを並べ替えて制限し、テーブル全体に結合します。

SELECT * 
FROM wp_posts
INNER JOIN
(
  SELECT max(post_date) post_date, post_author
  FROM wp_posts
  WHERE post_status='publish' AND post_type='post'
  GROUP BY post_author
  ORDER BY post_date DESC
  -- LIMIT GOES HERE
) p2 USING (post_author, post_date)
WHERE post_status='publish' AND post_type='post';

上記のアプローチを使用した同じクエリ:

SELECT *
FROM (
  SELECT post_id
  FROM wp_posts
  WHERE post_status='publish' AND post_type='post'
  GROUP BY post_author DESC
  ORDER BY post_date DESC
  -- LIMIT GOES HERE
) as ids
JOIN wp_posts USING (post_id);

SQLFiddleでの実行プランを含むすべてのクエリ。

于 2013-02-12T15:54:52.283 に答える
8

これを試してみてください。各著者から最新の投稿日のリストを取得するだけです。それでおしまい

SELECT wp_posts.* FROM wp_posts WHERE wp_posts.post_status='publish'
AND wp_posts.post_type='post' AND wp_posts.post_date IN(SELECT MAX(wp_posts.post_date) FROM wp_posts GROUP BY wp_posts.post_author) 
于 2013-02-08T11:04:59.460 に答える
4

max関数とgroup関数を使うだけ

    select max(taskhistory.id) as id from taskhistory
            group by taskhistory.taskid
            order by taskhistory.datum desc
于 2016-11-17T03:13:38.560 に答える
2

いいえ。グループ化は結果セットを変更するため、グループ化する前にレコードを並べ替えても意味がありません。サブクエリの方法が推奨される方法です。これが遅すぎる場合は、テーブルのデザインを変更する必要があります。たとえば、各著者の最後の投稿の ID を別のテーブルに保存するか、各著者の最後の投稿がどれであるかを示すブール列を導入する必要があります。 1。

于 2013-02-08T10:50:53.367 に答える
0

要約すると、標準的なソリューションは無相関サブクエリを使用し、次のようになります。

SELECT x.*
  FROM my_table x
  JOIN (SELECT grouping_criteria,MAX(ranking_criterion) max_n FROM my_table GROUP BY grouping_criteria) y
    ON y.grouping_criteria = x.grouping_criteria
   AND y.max_n = x.ranking_criterion;

古いバージョンの MySQL を使用している場合、またはかなり小さいデータ セットを使用している場合は、次の方法を使用できます。

SELECT x.*
  FROM my_table x
  LEFT
  JOIN my_table y
    ON y.joining_criteria = x.joining_criteria
   AND y.ranking_criteria < x.ranking_criteria
 WHERE y.some_non_null_column IS NULL;  
于 2013-02-08T11:28:00.347 に答える
-1

** サブクエリは、大規模なデータセットで使用するとパフォーマンスに悪影響を与える可能性があります **

元のクエリ

SELECT wp_posts.*
FROM   wp_posts
WHERE  wp_posts.post_status = 'publish'
       AND wp_posts.post_type = 'post'
GROUP  BY wp_posts.post_author
ORDER  BY wp_posts.post_date DESC; 

変更されたクエリ

SELECT p.post_status,
       p.post_type,
       Max(p.post_date),
       p.post_author
FROM   wp_posts P
WHERE  p.post_status = "publish"
       AND p.post_type = "post"
GROUP  BY p.post_author
ORDER  BY p.post_date; 

==>で使用maxしているため、サブ選択クエリを回避し、グループ化の後に最大列で並べ替えることができます。select clausemax(p.post_date)

于 2014-11-05T01:42:08.960 に答える
-4

まず、select で * を使用しないでください。パフォーマンスに影響を与え、group by と order by の使用を妨げます。このクエリを試してください:

SELECT wp_posts.post_author, wp_posts.post_date as pdate FROM wp_posts
WHERE wp_posts.post_status='publish'
AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author           
ORDER BY pdate DESC

ORDER BY でテーブルを指定せず、エイリアスだけを指定すると、select の結果が並べ替えられます。

于 2013-02-08T10:49:33.027 に答える