views:

1245

answers:

3

I'm trying to get the count of documents within 4 specific sections using the following code:

SELECT
    category.id
    , category.title
    , count(ts1.section_id) AS doc1
    , count(ts2.section_id) AS doc2
    , count(ts3.section_id) AS doc3
    , count(ts4.section_id) AS doc4
FROM
    category 
    LEFT JOIN category_link_section AS ts1
        ON (category.id = ts1.category_id AND ts1.section_id = 1)
    LEFT JOIN category_link_section AS ts2
        ON (category.id = ts2.category_id AND ts2.section_id = 2)
    LEFT JOIN category_link_section AS ts3
        ON (category.id = ts3.category_id AND ts3.section_id = 3)
    LEFT JOIN category_link_section AS ts4
        ON (category.id = ts4.category_id AND ts4.section_id = 4)
GROUP BY category.id, ts1.section_id, ts2.section_id, ts3.section_id, ts4.section_id

The table 'category' had an id, title etc. The table 'category_link_section' contains id linkages between category_id, section_id, and doc_id.

If the count is 0 for any column, it displays 0 in that column. But if the result is not 0 it shows the multiplication result of all the section results. So if my 4 count columns were supposed to return: 1, 2, 0, 3; it would actually show 6, 6, 0, 6;

If I use this following code for each specific category I get the results I want:

SELECT
    category.id
    , category.title
    , count(ts1.section_id) AS doc1
FROM
    category 
    LEFT JOIN category_link_section AS ts1
        ON (category.id = ts1.category_id AND ts1.section_id = 1)
GROUP BY category.id, ts1.section_id

but I then need to cycle through the database each time for each section.

So my question is, do I need to step through and call each section in turn, constructing my table outside the SQL, or can this be done in a single query?

+2  A: 

You might want to try something like this:

SELECT
    category.id
    , category.title
    , SUM(IF(ts1.section_id = 1, 1, 0)) AS doc1
    , SUM(IF(ts1.section_id = 2, 1, 0)) AS doc2
    , SUM(IF(ts1.section_id = 3, 1, 0)) AS doc3
    , SUM(IF(ts1.section_id = 4, 1, 0)) AS doc4
FROM
    category 
    LEFT JOIN category_link_section AS ts1
        ON (category.id = ts1.category_id AND ts1.section_id = 1)
GROUP BY category.id, ts1.section_id
VoteyDisciple
+4  A: 

@VoteyDisciple's answer is on the right track, but his query needs some improvements:

SELECT c.id, c.title,
    SUM(ts1.section_id = 1) AS doc1,
    SUM(ts1.section_id = 2) AS doc2,
    SUM(ts1.section_id = 3) AS doc3,
    SUM(ts1.section_id = 4) AS doc4
FROM category AS c
  LEFT JOIN category_link_section AS ts1
    ON (c.id = ts1.category_id)
GROUP BY c.id;


Explanations:

  • The IF() expressions are redundant because equality already returns 1 or 0.
  • Take the ts1.section_id=1 out of the join condition, or you'll never get the other section_id values.
  • Group by c.id only. I assume the OP only wants one row per category, and columns for counts of each section_id value for the respective category. If the query grouped by c.id, ts1.section_id, then there'd be up to four rows per category.
  • Move the commas in the select-list. Commas floating at the start of the line look ugly. ;-)
Bill Karwin
can you explain the improvement?
Thilo
Explanations added.
Bill Karwin
Awesome! Thanks. Works like a charm. :)I'll still be using the commas in front. Helps me see where syntax errors are. ;)Thanks again.
Das123
What I really wanted an explanation for is the Sum(boolean condition) pattern. That is neat. Is that MySQL-only?
Thilo
A boolean expression always yields either 1 or 0 in every SQL database I've used. Strictly in standard SQL they should be `true` or `false`, but practically those are just the integers 1 or 0.
Bill Karwin
A: 

Excellent, SUM on a boolean returns 0 or 1, nice ;--)

I was befuddled by multiple COUNT() in a query returning the row count for the first COUNT() call. SUM on boolean did the trick.

Thanks for the post!

MVC You Know Me