Not a single-group group function - sql

I have some tables which basically look as follows:
TBL_USER
user_id - number
user_name - varchar
TBL_STUFF
stuff_id - number
stuff_user_id - number
I want to query for all user information including the number of "stuff" they have. I was trying something like this:
select user_id, user_name, count(stuff_id)
from tbl_user
left outer join tbl_stuff on stuff_user_id = user_id
where user_id = 5;
but I get an error which says "not a single-group group function"
Is there some other way I should be doing this?

Well, you are missing the group function ;-)
Try this:
select user_id, user_name, count(stuff_id)
from tbl_user left outer join tbl_stuff on stuff_user_id = user_id
where user_id = 5
group by user_id, user_name;
The last line is the group by clause that tells Oracle to count all rows with the same user_id and user_name combination.

You could also do it like this:
select
user_id,
user_name,
(
SELECT
COUNT(*)
FROM
tbl_stuff
WHERE
stuff_user_id = tbl_user.user_id
) AS StuffCount,
from
tbl_user
where
user_id = 5;

One of your comments states that you don't want to include all the field present in a GROUP BY clause.
#Arion posted a correlated-sub-query re-factor that gives the same values.
The following query uses a standard (un-correlated) sub-query (inline-view) instead. This is because using this inline-view structure can often perform correlated-sub-query equivilents. But, also, because I find them easier to maintain.
WITH
stuff_count
AS
(
SELECT
stuff_user_id AS user_id,
COUNT(*) AS val
FROM
tbl_stuff
GROUP BY
stuff_user_id
)
SELECT
tbl_user.user_id,
tbl_user.user_name,
stuff_count.val
FROM
tbl_user
LEFT JOIN
stuff_count
ON stuff_count.user_id = tbl_user.user_id
WHERE
tbl_user.user_id = 5;
NOTE: When the plan is generated, it only runs the sub-query for the user_id's necessary, not the whole table ;)

Related

Update column with SUM from another table, PostgreSQL

I try to update my users points based on a SUM from another table.
UPDATE users u
INNER JOIN
(
SELECT user_id, SUM(user_score_for_answer) as total
FROM answer_histories
GROUP BY user_id, total
) a ON users.id = a.user_id
SET U.points = a.total
ERROR: syntax error at or near "INNER"
In Postgres, the syntax looks like:
UPDATE users u
SET points = a.total
FROM (SELECT user_id, SUM(user_score_for_answer) as total
FROM answer_histories
GROUP BY user_id
) a
WHERE u.id = a.user_id;
Gordon's answer is great, just getting small error - "aggregate functions are not allowed in GROUP BY" which can be fixed by simply removing total from GROUP BY.
*(I could not put this in comment due to insufficient reputation).

PostgreSQL more than one row returned by a subquery used as an expression

Running PostgreSQL 9.6.
I'm trying to output rows consisting of a value and a list of names.
This is my query:
SELECT name, (SELECT car_name FROM cars WHERE user = id)
FROM users WHERE user_id = 1 ORDER BY name;
But it fails with:
ERROR: more than one row returned by a subquery used as an expression
It of course make sense, but I would like to have the nested query to be outputted as a list or json.
I've tried with row_to_json, but that fails also.
Use an aggregation function, such as string_agg() or json_agg():
SELECT name,
(SELECT string_agg(car_name) FROM cars WHERE user = id)
FROM users
WHERE user_id = 1
ORDER BY name;
You can do the JOIN instead :
SELECT u.name, string_agg(c.car_name)
FROM users u LEFT OUTER JOIN
cars c
ON c.id = u.user
WHERE u.user_id = 1
GROUP BY u.name
ORDER BY u.name;

INNER JOIN Count Unknown column in ON clause

I'm using HeidiSQl and trying to run a query where I get all the users from table 1 and then count all the matching users in table 2.
SELECT DISTINCT problem, title, username, createdate, expiredate
FROM solutionhistory as table1
INNER JOIN (SELECT COUNT(LOGIN_NAME) FROM ra_report_table) as table2
ON table1.username=table2.LOGIN_NAME
WHERE username != 'test'
LIMIT 10
I'm a bit stuck as to where I'm going wrong! I'm getting the error unknown column in on clause for table2.LOGIN_NAME.
The query works if I take the reference to COUNT(LOGIN_NAME) out - if someone could point me in the right direction that would be great!
Your query doesn't make sense on a lot of levels. I am guessing you intend something more like this:
SELECT problem, title, username, createdate, expiredate, table2.cnt
FROM solutionhistory as table1 INNER JOIN
(SELECT LOGIN_NAME, COUNT(LOGIN_NAME) as cnt
FROM ra_report_table
GROUP BY LOGIN_NAME
) as table2
ON table1.username = table2.LOGIN_NAME
WHERE username <> 'test'
LIMIT 10

SQL: Count in join not working

I have an SQL query where I simply join two tables. One table contain comments and the other is the user table. I join the tables to in a simple manner get user information about the user who wrote the comment (username) at the same time get the comment (comment text etc.).
Now I want to count the number of comments to write the correct number of comments on the top of the page. I do this by adding a COUNT, and an alias to save the value.
When I echo numCount, I get the correct value of comments, but I get no comments in my comment loop. As soon as I remove the count, I get all comments again. What am I doing wrong?
SELECT
ncID, ncText, ncDate,
uID, uName, uImageThumb,
COUNT(a.ncID) AS numComments
FROM tblNewsComments a LEFT JOIN tblUsers b
ON a.ncUserID = b.uID
WHERE a.ncNewsID = $newID
ORDER BY ncDate DESC
I am going to assume this is MySQL (or maybe SQLite), since most other RDBMS would fail on this query. The issue is that you are missing a GROUP BY clause, which is required when using an aggregate function like COUNT() unless it is to operate over the entire rowset. MySQL's unusual behavior is to allow the absence of a GROUP BY, or to allow columns in SELECT which are not also in the GROUP BY, producing unusual results.
The appropriate way to do this would be to join in a subquery which returns the COUNT() per ncID.
SELECT
ncID,
ncText,
ncDate,
uID,
uName,
uImageThumb,
/* The count returned by the subquery */
ccount.numComments
FROM
tblNewsComments a
LEFT JOIN tblUsers b ON a.ncUserID = b.uID
/* Derived table returns only ncID and count of comments */
LEFT JOIN (
SELECT ncID, COUNT(*) AS numComments
FROM tblNewsComments
GROUP BY ncID
) ccount ON a.ncID = ccount.ncID
WHERE a.ncNewsID = $newID
ORDER BY ncDate DESC
Edit Whoops - looks like you wanted the count per ncID, not the count per ncUserID as I originally had it.
I don't know what SQL engine you are using, but what you have here is not valid SQL and should be flagged as such.
COUNT is an aggregate function and you can only apply those to groups or a whole table, so in your case you would probably do
SELECT
ncID, ncText, ncDate,
uID, uName, uImageThumb,
COUNT(a.ncID) AS numComments
FROM tblNewsComments a LEFT JOIN tblUsers b
ON a.ncUserID = b.uID
WHERE a.ncNewsID = $newID
GROUP BY ncID, ncText, ncDate,
uID, uName, uImageThumb
ORDER BY ncDate DESC
You're using an AGGREGATE function (Count) but you're needing a GROUP BY to make any sense from that count.
I suggest adding "GROUP BY [all other field names except the COUNT]" to your query
Try this:
SELECT
ncID, ncText, ncDate,
uID, uName, uImageThumb,
(SELECT COUNT(ncID)
FROM
tblNewsComments a
INNER JOIN
tblUsers b
ON a.ncUserID = b.uID)
AS numComments
FROM tblNewsComments a LEFT JOIN tblUsers b
ON a.ncUserID = b.uID
WHERE a.ncNewsID = $newID
ORDER BY ncDate DESC

Problem With DISTINCT!

Here is my query:
SELECT
DISTINCT `c`.`user_id`,
`c`.`created_at`,
`c`.`body`,
(SELECT COUNT(*) FROM profiles_comments c2 WHERE c2.user_id = c.user_id AND c2.profile_id = 1) AS `comments_count`,
`u`.`username`,
`u`.`avatar_path`
FROM `profiles_comments` AS `c` INNER JOIN `users` AS `u` ON u.id = c.user_id
WHERE (c.profile_id = 1) ORDER BY `u`.`id` DESC;
It works. The problem though is with the DISTINCT word. As I understand it, it should select only one row per c.user_id.
But what I get is even 4-5 rows with the same c.user_id column. Where is the problem?
actually, DISTINCT does not limit itself to 1 column, basically when you say:
SELECT DISTINCT a, b
What you're saying is, "give me the distinct value of a and b combined" .. just like a multi-column UNIQUE index
distinct will ensure that ALL values in your select clause are unique, not just user_id. If you want to limit the results to individual user_ids, you should group by user_id.
Perhaps what you want is:
SELECT
`c`.`user_id`,
`u`.`username`,
`u`.`avatar_path`,
(SELECT COUNT(*) FROM profiles_comments c2 WHERE c2.user_id = c.user_id AND c2.profile_id = 1) AS `comments_count`
FROM `profiles_comments` AS `c` INNER JOIN `users` AS `u` ON u.id = c.user_id
WHERE (c.profile_id = 1)
GROUP BY `c`.`user_id`,
`u`.`username`,
`u`.`avatar_path`
ORDER BY `u`.`id` DESC;
DISTINCT works at a row level, not just a column level
If you want the DISTiNCT of only one column then you will have to aggregate the rest of the columns returned (MIN, MAX, SUM, AVG, etc)
SELECT DISTINCT (Name), Min (ID)
From MyTable
Distinct will try to return only unique rows, it will not return only 1 row per user id in your example.
http://dev.mysql.com/doc/refman/5.0/en/distinct-optimization.html
You misunderstand. The DISTINCT modifier applies to the entire row — it states that no two identical ROWS will be returned in the result set.
Looking at your SQL, what value of the several available do you expect to see returned in the created_at column (for instance)? It would be impossible to predict the results of the query as written.
Also, you're using profile_comments twice in your SELECT. It appears that you're trying to obtain a count of how many times each user has commented. If so, what you want to do is use an AGGREGATE query, grouped on user_id and including only those columns that uniquely identify a user along with a COUNT of the comments:
SELECT user_id, COUNT(*) FROM profile_comments WHERE profile_id = 1 GROUP BY user_id
You can add the join to users to get the user name if you want but, logically, your result set cannot include other columns from profile_comments and still produce only a single row per user_id unless those columns are also aggregated in some way:
SELECT user_id, MIN(created_at) AS Earliest, MAX(created_at) AS Latest, COUNT(*) FROM profile_comments WHERE profile_id = 1 GROUP BY user_id