views:

180

answers:

3

So I have an example table called items with the following columns:

  • item_id (int)
  • person_id (int)
  • item_name (varchar)
  • item_type (varchar) - examples: "news", "event", "document"
  • item_date (datetime)

...and a table person with the following columns: "person_id", "person_name".

I was hoping to display a list of the top 2 submitters (+ the COUNT() of items submitted) in a given time period for each item_type. Here's basically what I was hoping the MySQL output would look like:

person_name  | item_type | item_count

Steve Jobs   | document  | 11
Bill Gates   | document  | 6
John Doe     | event     | 4
John Smith   | event     | 2
Bill Jones   | news      | 24
Bill Nye     | news      | 21

How is this possible without making a separate query for each item_type? Thanks in advance!

+2  A: 
SELECT  item_type, person_name, item_count
FROM    (
        SELECT  item_type, person_name, item_count,
                @r := IFNULL(@r, 0) + 1 AS rc,
                CASE WHEN @_item_type IS NULL OR @_item_type <> item_type THEN @r := 0 ELSE 1 END,
                @_item_type := item_type,
        FROM    (
                SELECT  @r := 0,
                        @_item_type := NULL
                ) vars,
                (
                SELECT  item_type, person_name, COUNT(*) AS item_count
                FROM    items
                GROUP BY
                        item_type, person_name
                ORDER BY
                        item_type, person_name, item_count DESC
                ) vo
        ) voi
WHERE   rc < 3
Quassnoi
How efficient is as opposed to using a separate query for each item_type?
Matt
It's more efficient. If you have lots of (item_type, person_name) pairs, it's even noticeably efficient.
Quassnoi
Depends on how many item_types you actually have but doing a single trip to the database is normally a good idea, and this query looks like it won't be very demanding on resource.
Alistair Knock
I'm still getting a MySQL error when running this... anyone?
Matt
It will be really easier if you post the error code and text.
Quassnoi
+1  A: 

I think this should do it:

SELECT person_name,item_type,count(item_id) AS item_count
FROM person
LEFT JOIN items USING person_id
GROUP BY person_id

The "item_type" column is going to be dodgy though, each row represents multiple items, and you're only showing the item_type from one of them. You can list all of them with "GROUP_CONCAT", that's a lot of fun.

GoatRider
You're just grouping, but he needs only the top 2 submitters from each item_type.
Seb
Ah, I didn't see the part about top 2 in each category.
GoatRider
+1  A: 

Something like this shoul work:

SELECT
  p.person_name, i.item_type, COUNT(1) AS item_count
FROM
  person p
  LEFT JOIN item i
    ON p.person_id = i.person_id
GROUP BY
  p.person_id,
  i.item_type
HAVING
  COUNT(1) >= (
    SELECT
      COUNT(1)
    FROM
      item i2
    WHERE
      i2.item_type = i.item_type
    GROUP BY
      i2.person_id
    LIMIT 1,1
  )
Seb
doesn't work: for some event types, returns only 1 top submitter even if there are more for this type.
ax