MySQLで複雑なGROUP BYを実行するにはどうすればよいですか?


8

他のテーブルへのいくつかのキーを含むテーブルがあります(各キーは複数の列で構成されています)。等しいキーを持つ行をグループ化たいのですが、それらすべてをグループ化たくありません。それは単純なGROUP BYキーではなく、たとえば10のグループを作成できるようにしたいのです。したがって、特定のキーが50回表示された場合、このグループ化(5グループの10)を実行すると5つの結果が得られます。また、このグループ化をキー内でランダムに発生させます。

私はこれを行う直接的な方法を知りませんでした、そして私が思いついたラウンドアバウト方式は私が思っているように機能していません。私が思いついたラウンドアバウト交差点の解決策は、値がそのキーiith出現を(ただしランダムな順序で)表すような整数である各キーの新しい列を作成することでした。次に、整数の除算を行って、キー内のすべてのn(たとえば10)行が同じ値になるようにし、GROUP BYその値に対してa を行うことができます。

今説明したことを達成するためのより直接的な方法はありますか?これはかなり厄介で、新しいインデックス列を作成するときに問題が発生しました(この質問で説明したように)。

編集:まず、これはMySQL用であることに注意してください。目標が明確でない場合に備えて、例を追加します。MySQLのドキュメントはほとんどそこに到達する方法を示しています:

CREATE TABLE animals (
    grp ENUM('fish','mammal','bird') NOT NULL,
    id MEDIUMINT NOT NULL AUTO_INCREMENT,
    name CHAR(30) NOT NULL,
    PRIMARY KEY (grp,id)
) ENGINE=MyISAM;

INSERT INTO animals (grp,name) VALUES
    ('mammal','dog'),('mammal','cat'),
    ('bird','penguin'),('fish','lax'),('mammal','whale'),
    ('bird','ostrich');

SELECT * FROM animals ORDER BY grp,id;

これは、私が望むものではありませんが、近くなるテーブルを作成します:

+--------+----+---------+
| grp    | id | name    |
+--------+----+---------+
| fish   |  1 | lax     |
| mammal |  1 | dog     |
| mammal |  2 | cat     |
| mammal |  3 | whale   |
| bird   |  1 | penguin |
| bird   |  2 | ostrich |
+--------+----+---------+

IDが1〜10の1つの「グループ」、IDが11〜20の別の「グループ」などGROUP BYのレコードが必要な場合を除いて、基本的mammalにIDを使用します。ただし、既存のテーブルを使用してこれを行います。そして、私は必ずしも「犬」がID 1で表示されることを望んでいません。その最初の順序をランダムにしたいのですが、その後は決定論的です。


I would want that initial ordering to be random, but then deterministic from then out.<-何を言う?何をするにしても、なんらかの種類の2番目のテーブルにレコードを配置する必要があると思います。このビジネスロジックはどの程度正確に機能しますか?(例えば)犬が最初に来ることを要求するものは何もないので。そして、どういう意味I would want the records from *mammal* to have one "group" for IDs 1-10, and another for IDs 11-20ですか...上記の質問の説明で、哺乳類に焦点を当てた別の表でそれを説明できますか?
jcolebrand

@jcolebrand哺乳動物であるすべてのレコードに対して、1からまでの一意のIDを割り当てnumMammalます。私は実際にid dogが何を取得するかは気にしませんが、元の挿入順序に依存しないようにします。
Michael McGowan、2011

@jcolebrandウェイトカラムもあるとします。IDが1から10の哺乳類の平均体重とIDが11から20の哺乳類の平均体重などを取得したいと思うかもしれません。それが私の感覚ですGROUP BY。次に、平均値間の相関関係を見つけるために、10のグループをペアにすることができます。元の挿入順序がたまたまウェイトでソートされた場合、これは私に間違った結果を与えるため、このランダムな順序付けが必要です。私は理にかなっていると思います。
マイケル・マクゴーワン、2011

私はまだ質問のサンプルTABLEが役立つと思います。しかし、私はあなたが望むものが見えると思います。それは実際にはセットに関するものではないので、SQLのドメインがどこにあるかはわかりません。SQLはセットのドメインです。私はあなたが示唆しているロジックを、1つ(または2つ)のループでphpファイルで実行します。SQLは、とにかく番号を割り当てるために効果的な単一ループを実行します。
jcolebrand

@jcolebrand私はSQLでこれを行うべきではないかもしれませんが、データベースの機能を利用することをお勧めします。データベース内で処理すべきものと処理すべきでないものの境界をまだ学習していますが、過去に結果を引き出して処理し、結果を元に戻そうとすると、パフォーマンスの結果が悪くなりました(何時間も私はおそらく結果を元に戻す際に何か間違ったことをしていたので)。
マイケルマッゴーワン

回答:


5

グループを動的に生成するために、ID列に対して少し計算を行うとどうでしょうか?

SELECT grp, FLOOR(id/10) AS id_grp
FROM animals
GROUP BY grp, id_grp

これにより、レコードのIDに基づいて10のグループが得られます。上記の動物の表を使用して、以下のデータを生成しました。

サンプルデータ

 INSERT INTO animals VALUES
 ('mammal',10,'dog'),('mammal',11,'dog'),('mammal',12,'dog'),
 ('mammal',21,'cat'),('mammal',22,'cat'),('mammal',23,'cat'),
 ('mammal',24,'cat'),('mammal',25,'cat'),('mammal',26,'cat'),
 ('bird',30,'penguin'),('bird',31,'penguin'),('bird',32,'penguin'),
 ('bird',33,'penguin'),('fish',44,'lax'),('fish',45,'lax'),
 ('fish',46,'lax'),('fish',47,'lax'),('fish',48,'lax'),
 ('mammal',31,'whale'),*'fish',51,'lax'),('fish',52,'lax'),
 ('fish',53,'lax'),('fish',54,'lax'),('bird',10,'ostrich');

クエリ出力

 +--------+--------+
 | grp    | id_grp |
 +--------+--------+
 | fish   |      4 |
 | fish   |      5 |
 | mammal |      1 |
 | mammal |      2 |
 | mammal |      3 |
 | bird   |      1 |
 | bird   |      3 |
 +--------+--------+
 7 rows in set (0.00 sec)

問題のテーブルを最初に生成できれば、同様の計算を行うことを計画していました。IDを正しく割り当てることができません。
マイケルマッゴーワン、2011

これは@MichaelMcGowanに役立ちますか?explainextended.com/2009/03/05/row-samplingまたはjimlife.wordpress.com/2008/09/09/...
jcolebrand

@jcolebrandありがとう、私はまだ最初のリンクを見ています。私は、第二リンクと同様のアプローチを試みたが、それに問題を抱えていた:dba.stackexchange.com/questions/1932/...
マイケル・マッゴーワン

2

SQLでは通常、次のようになります。

  • DISTINCT副選択
  • DISTINCTキーのメインテーブルにJOINする
  • DISTINCTキーにPARTITION BYを指定したNTILEとバケットを作成するORDER BY

集合体ではないため、GROUP BYは必要ありません

編集:

実際、NTILEはそれ自体で「個別の値のセットごとにnバケット」を作成するのに十分です


MySQLがNTILEをサポートしているとは思いません。
マイケルマッゴーワン、2011年

申し訳ありませんが、そのリンクはそうであることを意味します。NTILEにはおそらく解決策/回避策があります。
gbn 2011年

優れたOracleソリューション。
リーリフェル

@Leigh Riffel:およびSQL Server。そしてSybase。そしてPostGres ...
gbn

2
@gbn MySQLではなく、明確にすべきだったポイントです。記事はOracleを参照しています。
リーリフェル

1

私はまだ完全なソリューション(実際にはMySQLで動作する)をまだ見ていません。そのため、これはおそらく私が使用するソリューションです。

  1. SQLの外で完全にランダムなIDを生成する(ある種のスクリプトで)
  2. それらのIDに整数除算を適用して、それらを適宜グループ化します。

私はまだ誰かがこの答えを打つことができることを望んでいます。自分の答えを受け入れたくありません。私は以前にこれを言ったことがありますが、最初から2番目の方法を知っていました。#1は私を悩ませているものです。#1に回答できる場合は、実際には別の質問にも回答できますが、この質問に別の方法で回答して、#1をバイパスすることができる場合があります。


0
-- Change 'ValueField' to whatever provides your 'group' values

set @rownum := 0;
set @groupnum := 0;
set @lastGroup := 0;

select
    ValueField, 
    Grouping, 
    count(1) as Count
from
    (
        -- We have a row number for each record
        select
            -- Set the record number
            case when @lastGroup != ValueField 
                then @rownum := 0 else (@rownum := @rownum + 1) 
            end as Record, 

            -- Determine which group we are in
            case
                -- If the 'Group' changed, reset our grouping
                when @lastGroup != ValueField 
                    then @groupnum := 0

                -- Determines the grouping value; group size is set to 10
                when floor(@rownum / 10) != @groupnum 
                    then @groupnum := @groupnum + 1 
                else @groupnum
            end as Grouping,

            -- Track the last Group
            case 
                when @lastGroup != ValueField 
                    then @lastGroup := ValueField 
                else @lastGroup 
            end as LastGroup,

            -- Value field that will be aggregated
            ValueField 
        from 
            YourTable
        order by 
            ValueField
    ) as x
group by
    ValueField, 
    Grouping;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.