tags:

views:

90

answers:

3

Consider following tables in MySQL database:

entries:
    creator_id INT
    entry TEXT
    is_expired BOOL
other:
    creator_id INT
    entry TEXT

userdata:
    creator_id INT
    name VARCHAR
    etc...

In entries and other, there can be multiple entries by 1 creator. userdata table is read only for me (placed in other database).

I'd like to achieve a following SELECT result:

+------------+---------+---------+-------+
| creator_id | entries | expired | other |
+------------+---------+---------+-------+
|      10951 |      59 |      55 |    39 | 
|      70887 |      41 |      34 |   108 | 
|      88309 |      38 |      20 |   102 | 
|      94732 |       0 |       0 |    86 |

... where entries is equal to SELECT COUNT(entry) FROM entries GROUP BY creator_id, expired is equal to SELECT COUNT(entry) FROM entries WHERE is_expired = 0 GROUP BY creator_id and other is equal to SELECT COUNT(entry) FROM other GROUP BY creator_id.

I need this structure because after doing this SELECT, I need to look for user data in the "userdata" table, which I planned to do with INNER JOIN and select desired columns.

I solved this problem with selecting "NULL" into column which does not apply for given SELECT:

SELECT
    creator_id,
    COUNT(any_entry) as entries,
    COUNT(expired_entry) as expired,
    COUNT(other_entry) as other
FROM (
    SELECT
        creator_id,
        entry AS any_entry,
        NULL AS expired_entry,
        NULL AS other_enry
    FROM entries
    UNION
    SELECT
        creator_id,
        NULL AS any_entry,
        entry AS expired_entry,
        NULL AS other_enry
    FROM entries
    WHERE is_expired = 1
    UNION
    SELECT
        creator_id,
        NULL AS any_entry,
        NULL AS expired_entry,
        entry AS other_enry
    FROM other
) AS tTemp
GROUP BY creator_id
ORDER BY
    entries DESC,
    expired DESC,
    other DESC
;

I've left out the INNER JOIN and selecting other columns from userdata table on purpose (my question being about combining 3 SELECTs into 1).

  • Is my idea valid? = Am I trying to use the right "construction" for this?
  • Are these kind of SELECTs possible without creating an "empty" column? (some kind of JOIN)
  • Should I do it "outside the DB": make 3 SELECTs, make some order in it (let's say python lists/dicts) and then do the additional SELECTs for userdata?

Solution for a similar question does not return rows where entries and expired are 0.

Thank you for your time.

A: 

Basicly, what you are doing looks correct to me.

I would rewrite it as follows though

SELECT  entries.creator_id
        , any_entry
        , expired_entry
        , other_entry
FROM    (
          SELECT creator_id, COUNT(entry) AS any_entry,
          FROM entries
          GROUP BY creator_id
        ) entries
        LEFT OUTER JOIN (
          SELECT creator_id, COUNT(entry) AS expired_entry,
          FROM entries
          WHERE is_expired = 1
          GROUP BY creator_id        
        ) expired ON expired.creator_id = entries.creator_id
        LEFT OUTER JOIN (
          SELECT creator_id, COUNT(entry) AS other_entry
          FROM other
          GROUP BY creator_id
        ) other ON other.creator_id = entries.creator_id
Lieven
This does not return rows where columns from first 2 tables are =0. With RIGHT OUTER JOIN I get the rows from "other" table also, but that returns NULL for all other columns in those rows. Thanks though.
Martin Tóth
A: 

How about

SELECT creator_id,
   (SELECT COUNT(*)
        FROM entries e
        WHERE e.creator_id = main.creator_id AND
              e.is_expired = 0) AS entries,
   (SELECT COUNT(*)
        FROM entries e
        WHERE e.creator_id = main.creator_id AND
              e.is_expired = 1) as expired,
   (SELECT COUNT(*)
        FROM other
        WHERE other.creator_id = main.creator_id) AS other,
 FROM entries main
 GROUP BY main.creator_id;
Brian Hooper
I had no idea one can reference columns like this (inside nested SELECT), thanks.
Martin Tóth
A: 

This should work (assuming all creator_ids appear in the userdata table.

SELECT userdata.creator_id, COALESCE(entries_count_,0) AS entries_count, COALESCE(expired_count_,0) AS expired_count, COALESCE(other_count_,0) AS other_count
FROM userdata
  LEFT OUTER JOIN
  (SELECT creator_id, COUNT(entry) AS entries_count_
   FROM entries
   GROUP BY creator_id) AS entries_q
    ON userdata.creator_id=entries_q.creator_id
  LEFT OUTER JOIN
  (SELECT creator_id, COUNT(entry) AS expired_count_
   FROM entries
   WHERE is_expired=0
   GROUP BY creator_id) AS expired_q
    ON userdata.creator_id=expired_q.creator_id
  LEFT OUTER JOIN
  (SELECT creator_id, COUNT(entry) AS other_count_
   FROM other
   GROUP BY creator_id) AS other_q
    ON userdata.creator_id=other_q.creator_id;
VeeArr
Thank you, this is closest to what I wanted.
Martin Tóth