Bagaimana cara saya melakukan GROUP BY yang kompleks di MySQL?

8

Saya memiliki tabel yang berisi beberapa kunci ke tabel lain (di mana setiap tombol terdiri dari beberapa kolom). Saya ingin dapat mengelompokkan baris bersama yang memiliki kunci yang sama, tetapi saya tidak ingin mengelompokkan semuanya bersama. Ini tidak sederhana GROUP BYpada kunci tetapi saya ingin dapat membuat kelompok katakanlah 10. Jadi jika kunci tertentu muncul 50 kali saya akan mendapatkan 5 hasil ketika saya melakukan pengelompokan ini (5 kelompok 10). Saya juga ingin pengelompokan ini terjadi secara acak di dalam kunci.

Saya tidak tahu cara langsung untuk melakukan ini, dan metode bundaran yang saya buat tidak berfungsi seperti yang saya kira seharusnya. Solusi bundaran yang saya buat adalah untuk membuat kolom baru untuk setiap kunci yang akan menjadi bilangan bulat sehingga nilai imewakili ithterjadinya kunci itu (tetapi dalam urutan acak). Saya kemudian bisa melakukan pembagian integer sehingga setiap n (katakanlah 10) baris dalam kunci memiliki nilai yang sama, dan saya bisa melakukan GROUP BYpada nilai itu.

Apakah ada cara yang lebih langsung untuk mencapai apa yang baru saja saya jelaskan? Cukup aneh, dan saya mengalami masalah dalam membuat kolom indeks baru (seperti yang saya jelaskan dalam pertanyaan ini ).

EDIT: Pertama-tama perhatikan bahwa ini untuk MySQL. Saya akan menambahkan contoh jika tujuan saya tidak jelas. Dokumen MySQL menunjukkan metode untuk sampai di sana :

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;

Ini menciptakan tabel yang, meskipun bukan yang saya inginkan, semakin dekat:

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

Saya pada dasarnya ingin GROUP BYid, kecuali saya ingin catatan dengan mammalmemiliki satu "grup" untuk ID 1-10, "grup" lain untuk ID 11-20, dll. Namun, saya akan melakukan ini dengan tabel yang ada, dan saya tidak akan selalu ingin "anjing" muncul dengan ID 1. Saya ingin agar pemesanan awal menjadi acak, tetapi kemudian deterministik sejak saat itu.

Michael McGowan
sumber
I would want that initial ordering to be random, but then deterministic from then out.<- katakan apa? Saya pikir apa pun yang Anda lakukan, Anda harus meletakkan catatan di semacam tabel kedua. Bagaimana tepatnya logika bisnis ini berfungsi? Seperti halnya tidak ada yang mengharuskan (misalnya) anjing untuk didahulukan. Dan apa yang Anda maksud dengan I would want the records from *mammal* to have one "group" for IDs 1-10, and another for IDs 11-20... bisakah Anda menggambarkannya dengan tabel lain, yang berfokus pada mamalia, dalam uraian pertanyaan di atas?
jcolebrand
@ jcolebrand Untuk setiap rekaman yang merupakan mamalia, saya ingin menetapkan id unik dari 1 hingga numMammal. Saya tidak terlalu peduli dengan id yang saya dogdapat, tetapi saya tidak ingin itu tergantung pada urutan penyisipan asli.
Michael McGowan
@ jcolebrand Misalkan saya juga memiliki kolom yang berat. Saya mungkin ingin mengambil berat rata-rata mamalia dengan ID dari 1-10 dan berat rata-rata mamalia dengan ID dari 11-20, dll. Itulah pengertian yang saya inginkan GROUP BY. Saya kemudian mungkin ingin memasangkan kelompok 10 untuk menemukan korelasi antara rata-rata. Saya perlu pemesanan acak ini karena jika urutan penyisipan asli disortir berdasarkan berat maka ini akan memberi saya hasil yang salah. Saya harap saya masuk akal.
Michael McGowan
Saya masih berpikir TABEL sampel dalam pertanyaan akan sangat membantu. Tapi saya rasa saya mengerti apa yang Anda inginkan. Saya hanya tidak melihat di mana hal-hal itu adalah domain dari SQL, karena ini bukan tentang set. SQL adalah domain set. Saya akan melakukan logika yang Anda sarankan dalam file php dengan satu (atau dua) loop. SQL akan melakukan loop tunggal yang efektif untuk menetapkan angka-angka.
jcolebrand
@ jcolebrand Mungkin sebaiknya saya tidak melakukan ini dalam SQL, tapi saya pikir aturan praktis yang berguna adalah membiarkan database melakukan pekerjaan untuk Anda. Saya masih belajar batas-batas apa yang harus dan tidak boleh diproses dalam database, tetapi di masa lalu ketika saya mencoba menarik hasil, memprosesnya, dan kemudian menempelkan hasilnya kembali, saya mendapatkan hasil kinerja yang buruk (berjam-jam karena saya mungkin melakukan sesuatu yang salah dalam memasukkan hasil kembali).
Michael McGowan

Jawaban:

5

Bagaimana dengan melakukan sedikit matematika terhadap kolom ID Anda untuk menghasilkan grup secara dinamis?

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

Ini akan memberi Anda kelompok 10 berdasarkan ID catatan. Saya menggunakan tabel hewan Anda di atas untuk menghasilkan data di bawah ini.

Contoh data

 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');

Output Permintaan

 +--------+--------+
 | grp    | id_grp |
 +--------+--------+
 | fish   |      4 |
 | fish   |      5 |
 | mammal |      1 |
 | mammal |      2 |
 | mammal |      3 |
 | bird   |      1 |
 | bird   |      3 |
 +--------+--------+
 7 rows in set (0.00 sec)
nabrond
sumber
Saya berencana melakukan matematika serupa jika saya pertama kali dapat menghasilkan tabel yang bersangkutan. Saya mengalami masalah dalam mendapatkan ID yang ditetapkan dengan benar.
Michael McGowan
Apakah ini membantu @MichaelMcGowan? explainextended.com/2009/03/05/row-sampling atau jimlife.wordpress.com/2008/09/09/...
jcolebrand
@ jcolebrand Terima kasih, saya masih melihat tautan pertama. Saya mencoba pendekatan yang mirip dengan tautan ke-2 dan mengalami masalah dengan itu: dba.stackexchange.com/questions/1932/…
Michael McGowan
2

Dalam SQL umumnya ini akan menjadi:

  • sebuah subselect DISTINCT
  • GABUNG kembali ke tabel utama pada tombol DISTINCT
  • NTILE dengan PARTISI OLEH pada tombol DISTINCT dan OLEH ORDER untuk membuat ember

Ini bukan agregat sehingga GROUP BY tidak diperlukan

Edit:

Sebenarnya, NTILE sudah cukup dengan sendirinya untuk membuat "n ember per set nilai yang berbeda"

gbn
sumber
Saya tidak percaya MySQL mendukung NTILE.
Michael McGowan
Maaf, tautan itu menyiratkan hal itu. Mungkin ada solusi / solusi untuk NTILE di luar sana.
gbn
Solusi Oracle yang luar biasa.
Leigh Riffel
@Leigh Riffel: dan SQL Server. Dan Sybase. Dan PostGres ...
gbn
2
@ Gbn Bukan MySQL yang seharusnya saya jelaskan. Artikel tersebut merujuk pada Oracle.
Leigh Riffel
1

Saya masih belum melihat solusi lengkap (yang sebenarnya berfungsi di MySQL), jadi ini solusi yang mungkin akan saya gunakan:

  1. Hasilkan ID acak di luar SQL sepenuhnya (dalam beberapa jenis skrip)
  2. Terapkan pembagian integer pada ID tersebut untuk mengelompokkannya sesuai.

Saya masih berharap seseorang dapat mengalahkan jawaban ini; Saya tidak mau harus menerima jawaban saya sendiri. Saya sudah mengatakan ini sebelumnya, tetapi saya tahu dari awal bagaimana melakukan # 2; # 1 adalah yang menggangguku. Jika Anda dapat menjawab # 1 maka Anda benar-benar menjawab pertanyaan lain juga, tetapi mungkin untuk menjawab pertanyaan ini dengan beberapa cara lain untuk memotong # 1.

Michael McGowan
sumber
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;
dba4life
sumber