MySQL SELECT n records base on GROUP BY

后端 未结 3 936
抹茶落季
抹茶落季 2021-01-19 19:10

Lets say I have SQL records:

Country | Number
USA | 300
USA | 450
USA | 500
USA | 100
UK  | 300
UK  | 400
UK  | 1000

And I am doing something like this

相关标签:
3条回答
  • 2021-01-19 19:37

    Lets name your table TName, then they query would be.

    SELECT * FROM (SELECT ROW_NUMBER() OVER (PARTITION BY X.Country) AS RowNo, *
    FROM (SELECT Country, Name FROM TName ORDER BY Country, Number) X ) Y WHERE Y.RowNo <= 2
    
    0 讨论(0)
  • 2021-01-19 20:00

    Sample data

    create table data (Country varchar(10), Number int);
    insert into data select
    'USA' , 300 union all select
    'USA' , 450 union all select
    'USA' , 500 union all select
    'USA' , 100 union all select
    'FR'  , 100 union all select
    'FR'  , 420 union all select
    'UK'  , 300 union all select
    'UK'  , 400 union all select
    'UK'  , 1000;
    

    The first option is a pseudo rank using variables like The Scrum Meister has shown, but presented here as a single statement

    SELECT Country, Number
    FROM (
        SELECT
            Number,
            @r := case when @c=country then @r+1 else 1 end rownum,
            @c := Country Country 
        FROM (select @r :=0 , @c := '') x, data
        ORDER BY Country, Number DESC
    ) y
    WHERE rownum < 3;
    

    If you are using this in a front end, and only need 2 counts, then you can use this form that returns the counts in a list (single column)

    SELECT
        Country,
        left(x,locate(',',concat(x,','),locate(',',x)+1)-1) Numbers
    FROM (
        SELECT
            a.Country,
            Group_Concat(a.Number) x
        From (
            select country, number
            from data
            order by country, number desc) a
        group by a.Country
    ) b
    

    The result is

    "Country";"Numbers"
    "FR";"420,100"
    "UK";"1000,400"
    "USA";"500,450"
    

    If it is possible for ties to occur, then this variation of the 2nd form removes the ties and shows the "top 2 distinct numbers per country", as records.

    SELECT distinct x.Country, x.Number
    From data x
    inner join
    (
        SELECT
            Country,
            left(x,locate(',',concat(x,','),locate(',',x)+1)-1) Numbers
        FROM (
            SELECT
                a.Country,
                Group_Concat(a.Number) x
            From (
                select distinct country, number
                from data
                order by country, number desc) a
            group by a.Country
        ) b
    ) y on x.Country=y.Country
        and concat(',',y.Numbers,',') like concat('%,',x.Number,',%')
    order by x.Country, x.Number Desc
    

    Result

    "Country";"Number"
    "FR";"420"
    "FR";"100"
    "UK";"1000"
    "UK";"400"
    "USA";"500"
    "USA";"450"
    
    0 讨论(0)
  • 2021-01-19 20:02

    Since MySql does not have a built in RANK function, the query may be slow:

    SET @cRank = 0;
    SET @cCoutnry = '';
    
    SELECT Country, Number
    FROM (
        SELECT Number, @cRank := IF(@cCoutnry = Country, @cRank+1, 1) AS rank, @cCoutnry := Country Country 
        FROM table
        ORDER BY Country, Number DESC
    ) rs
    WHERE rank < 3
    
    0 讨论(0)
提交回复
热议问题