1

I have two tables, users table that contains all the users in my system and other table called action with all the actions done by the users in my system, what I want to get is a query where i can count the actions by the user and if I don't have any action for one user get 0.

My two tables:

Users Table:

Name       Place
----------------------
James      Supermarket
Alex       Bank 
Ignatius   BookShop

Action table:

Name   Action
--------------------
James  Buy
James  Buy
Alex   Complaint
Alex   Buy
Alex   Buy

I want something like this:

Name       Transactions   Place
-------------------------------
James      2              Supermarket
Alex       3              Bank
Ignatius   0              BookShop

I tryied with:

SELECT users.name, 
   (SELECT Count(action.name) 
    FROM   action, 
           users 
    WHERE  action.name = users.name 
    GROUP  BY action.name) 
    users.place
FROM 
action, 
users 
WHERE  action.name = users.name 

But is always giving me this error:

ERROR: more than one row returned by a subquery used as an expression SQL state: 21000

Any idea about how to do it?

3 Answers 3

3

You should do a left join first, then apply group by to its result:

SELECT
    u.name
,   COUNT(a.name) AS Transactions
,   u.place
FROM users u
LEFT OUTER JOIN action a ON a.name = u.name 
GROUP BY u.name, u.place

Left join ensures that a user record is not discarded even if it does not have associated transactions in the action table.

Sign up to request clarification or add additional context in comments.

2 Comments

hi, @dasblinkenlight it's working fine but I do not want include u.place in the group by, when I remove it is givinig me the error that say I have to include, I cannot include. How could I do it?
@JoanTriay Assuming that name uniquely identifies a user, it makes no difference that you use place in group by, because both fields come from the same table, and one of the fields (name) uniquely identifies the row.
2

Never use commas in the FROM clause. Always use proper, explicit JOIN syntax. That said, you don't need any joins at all for this query, if you want to use correlated subqueries:

SELECT u.name, 
       (SELECT Count(*) 
        FROM action a 
        WHERE a.name = u.name 
       ) as cnt,
       u.place
FROM users u;

For performance, you want an index on action(name). With such an index, this may be faster than alternative versions using left join and group by.

Notes:

  • All the tables have table aliases, which are abbreviations for the table name.
  • No joins are needed for this version of the query.
  • No group by in the subquery.
  • No where clause is needed in the outer query.

1 Comment

Your solution works fine, slower, but it's good. thanks a lot
1

As with previous answer a left join would see you through. If you want to include place in the results then do the left join after summarising

SELECT a.name,
        ISNULL(b.cnt, 0) AS Transactions,
        a.place
FROM users a
LEFT JOIN
   (SELECT name,
            Count(name) cnt
    FROM   action 
    GROUP  BY name) b
ON a.name =  b.name

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.