Get n grouped categories and sum others into one

前端 未结 3 776
别跟我提以往
别跟我提以往 2020-12-21 03:57

I have a table with the following structure:

Contents (
  id
  name
  desc
  tdate
  categoryid
  ...
)
         


        
3条回答
  •  被撕碎了的回忆
    2020-12-21 05:02

    The quick fix, to make the 'Others' row conditional would be to add a simple HAVING clause to that query.

    HAVING COUNT(c.id) > 0
    

    (If there are no other rows in the contents table, then COUNT(c.id) is going to be zero.)

    That only answers half the question, how to make the return of that row conditional.


    The second half of the question is a little more involved.

    To get the whole resultset in one query, you could do something like this

    (this is not tested yet; desk checked only.. I'm not sure if postgresql accepts a LIMIT clause in an inline view... if it doesn't we'd need to implement a different mechanism to limit the number of rows returned.

      SELECT IFNULL(t.name,'Others') AS name
           , t.catid                 AS catid
           , COUNT(o.id)             AS data 
        FROM contents o
        LEFT 
        JOIN category oa
          ON oa.id = o.category_id
        LEFT
        JOIN ( SELECT COALESCE(ca.name,'Unknown') AS name
                    , ca.id                       AS catid
                    , COUNT(c.id)                 AS data
                 FROM contents c
                 LEFT
                 JOIN category ca
                   ON ca.id = c.categoryid
                GROUP 
                   BY COALESCE(ca.name,'Unknown')
                    , ca.id
                ORDER
                   BY COUNT(c.id) DESC
                    , ca.id DESC
                LIMIT 7
             ) t
          ON ( t.catid = oa.id OR (t.catid IS NULL AND oa.id IS NULL)) 
       GROUP
          BY ( t.catid = oa.id OR (t.catid IS NULL AND oa.id IS NULL)) 
           , t.catid
       ORDER
          BY COUNT(o.id) DESC
           , ( t.catid = oa.id OR (t.catid IS NULL AND oa.id IS NULL)) DESC
           , t.catid DESC
       LIMIT 7
    

    The inline view t basically gets the same result as the first query, a list of (up to) 7 id values from category table, or 6 id values from category table and a NULL.

    The outer query basically does the same thing, joining content with category, but also doing a check if there's a matching row from t. Because t might be returning a NULL, we have a slightly more complicated comparison, where we want a NULL value to match a NULL value. (MySQL conveniently gives us shorthand operator for this, the null-safe comparison operator <=>, but I don't think that's available in postgresql, so we have to express differently.

         a = b OR (a IS NULL AND b IS NULL)
    

    The next bit is getting a GROUP BY to work, we want to group by the 7 values returned by the inline view t, or, if there's not matching value from t, group the "other" rows together. We can get that to happen by using a boolean expression in the GROUP BY clause.

    We're basically saying "group by 'if there was a matching row from t'" (true or false) and then group by the row from 't'. Get a count, and then order by the count descending.

    This isn't tested, only desk checked.

提交回复
热议问题