Question

The following statement gets row counts for user_ids from various tables/conditions where the users are within specific computers of a specific account. It works as expected. An example output would be something like this :

Array
(
    [0] => Array
        (
            [computer_name] => COMPUTER_1
            [username] => Steve
            [t1count] => 13
            [t2count] => 
            [t3count] => 23
            [t4count] => 64
        )
    ... and so on for each

the statement :

$stmt = $db->prepare("
    SELECT c.computer_name, users.username, t1count, t2count, t3count, t4count 
    FROM
        ( SELECT account_id, computer_id, computer_name
            FROM computers
            WHERE account_id = ".$_SESSION['user']['account_id']."
            ORDER BY computer_id ASC LIMIT 0, ".$_SESSION['user']['licenses']."
        ) as c
        LEFT JOIN users
            on users.computer_id = c.computer_id

        LEFT JOIN
            (SELECT user_id, COUNT(user_id) as t1count
            FROM t1
            WHERE t1.title LIKE 'started'
            GROUP BY user_id) as t_t1
                on t_t1.user_id = users.user_id

        LEFT JOIN
            (SELECT user_id, COUNT(user_id) as t2count
            FROM t2
            GROUP BY user_id) as t_t2
                on t_t2.user_id = users.user_id

        LEFT JOIN
            (SELECT user_id, COUNT(user_id) as t3count
            FROM t1
            WHERE t1.title LIKE 'blocked'
            GROUP BY user_id) as t_t3
                on t_t3.user_id = users.user_id     

        LEFT JOIN
            (SELECT user_id, COUNT(user_id) as t4count
            FROM t1
            WHERE t1.title LIKE 'closed'
            GROUP BY user_id) as t_t4
                on t_t4.user_id = users.user_id

            ... and so on for each

    WHERE c.account_id = ?
");

I want to also return a totalsum = t1count + t2count + t3count + t4count in this statement as well, but can't seem to get anything working. In this situation I cannot do outside processing (adding the values that are returned)... it needs to be in the statement. I am also open to any suggestions in what I already have if there are better options.

Was it helpful?

Solution

What are you trying to do with these values? SUM(t1count,t2count,t3count,t4count) AS totalsum should work to get a count of counts.

Your query is a bit hard to read. You have implicit and explicit JOINs. You are grouping numerous times and pulling columns when you can generally do this all in one shot. If you share an SQLFiddle, I will be able to clean this up, but in short:

SELECT 
    c.computer_name, 
    users.username, 
    count(t_t1.user_id) AS t1count, 
    count(t_t2.user_id) AS t2count, 
    count(t_t3.user_id) AS t3count, 
    count(t_t4.user_id) AS t4count, 
    (count(t_t1.user_id)+count(t_t2.user_id)+count(t_t3.user_id)+count(t_t4.user_id)) AS totalsum FROM users
LEFT JOIN computers AS c ON users.computer_id=c.computer_id AND c.account_id=?
LEFT JOIN t1 AS t_t1 ON t_t1.user_id = users.user_id AND t_t1.title LIKE "started"
LEFT JOIN t2 AS t_t2 ON t_t2.user_id = users.user_id
LEFT JOIN t1 AS t_t3 ON t_t3.user_id = users.user_id AND t_t3.title LIKE 'blocked'
LEFT JOIN t1 AS t_t4 ON t_t4.user_id = users.user_id AND t_t4.title LIKE 'closed'
WHERE c.account_id = ?
AND t_t1.title LIKE started
GROUP BY users.user_id;

This may need tweaking, as I stated, but it is a lot cleaner and easier to read and should accomplish something very similar.

Alternately, if you can't get the query to run the way you want it to when you change it to entirely explicit joins, rather than using SUM, try adding the values together like I did in the example above. It should prevent them from aggregating in the same way.

EDIT

After viewing your SQLFiddle, I have doctored up a solution which does away with nested queries. The positive is that it is cleaner. The negative is that it requires you to specify the users using an IN clause.

SELECT computers.account_id,computers.computer_id,computers.computer_name,users.user_id,users.username,count(distinct t_count1.log_id) AS count1,count(distinct t_count2.log_id) AS count2,count(distinct t_count3.log_id) AS count3, count(distinct t_count4.event_id) AS count4,
(count(distinct t_count1.log_id) + count(distinct t_count2.log_id) + count(distinct t_count3.log_id) + count(distinct t_count4.event_id)) AS totalcount

FROM users
INNER JOIN computers ON computers.computer_id=users.computer_id
LEFT JOIN logs AS t_count1 ON t_count1.type LIKE 'type1' AND t_count1.user_id=users.user_id
LEFT JOIN logs AS t_count2 ON t_count2.type LIKE 'type2' AND t_count2.user_id=users.user_id
LEFT JOIN logs AS t_count3 ON t_count3.type LIKE 'type3' AND t_count3.user_id=users.user_id
LEFT JOIN events AS t_count4 ON t_count4.user_id = users.user_id
WHERE computers.account_id=1 AND computers.computer_id in (1,2)
GROUP BY users.user_id
ORDER BY users.user_id ASC,computers.computer_id ASC;

If you choose to keep your current query structure for any reason, adapting it like so should make it work for you:

select *,ifnull(count1,0)+ifnull(count2,0)+ifnull(count3,0)+ifnull(count4,0) AS totalcount from
  ( select account_id, computer_id, computer_name
     from computers
     order by computer_id asc limit 0, 2
  ) as c

  left join users
    on users.computer_id = c.computer_id

  left join
    (select user_id, count(user_id) as count1
       from logs
       where logs.type like 'type1'
       group by user_id) as t_count1
    on t_count1.user_id = users.user_id

  left join
    (select user_id, ifnull(count(user_id),0) as count2
       from logs
       where logs.type like 'type2'
       group by user_id) as t_count2
    on t_count2.user_id = users.user_id

  left join
    (select user_id, count(user_id) as count3
       from logs
       where logs.type like 'type3'
       group by user_id) as t_count3
    on t_count3.user_id = users.user_id

  left join
    (select user_id, count(user_id) as count4
       from events
       group by user_id) as t_count4
      on t_count4.user_id = users.user_id

   where c.account_id = 1;

My advice would be to follow along each query to understand what you are asking SQL to do. To actually add all values together, you should be counting the number of records that are returned. Counting values of your primary keys helps you to count records. Also, using your other example, ifnull in the second example make sure that null values are not going to interfere with adding. "If a value is null make it 0 instead."

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top