Update column with SUM from another table, PostgreSQL - sql

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).

Related

right way to alias count * in a subquery

I have query below as
select t.comment_count, count(*) as frequency
from
(select u.id, count(c.user_id) as comment_count
from users u
left join comments c
on u.id = c.user_id
and c.created_at between '2020-01-01' and '2020-01-31'
group by 1) t
group by 1
order by 1
when I also try to alias the count(*) as count(t.*) it gives error, can I not alias that with the t from the table? Not sure what I am missing
Thank you
Count(*) stands for the count of all rows returned by a query (with respect to GROUP BY columns). So it makes no sence to specify one of the involved tables. Consider counting rows produced by a join for example. If you need a count of rows of the specific table t you can use count(distinct t.<unique column>)

How to write the correct SQL query to find the most duplicate user?

That's my database design.
I need to find the person with the most albums using SQLite.
I tried this:
SELECT USERS.NAME, COUNT(USERS.NAME) AS 'value_occurrence' FROM USERS
INNER JOIN ALBUMS
ON USERS.ID = ALBUMS.USER_ID
GROUP BY USERS.NAME
ORDER BY 'value_occurrence'
DESC LIMIT 1;
but it didn't work and gave me the wrong result. Please help me find the right way to do this.
The logic is correct, but you may be getting tripped up by incorrect use of single quotes for aliases. Try this version:
SELECT u.NAME, COUNT(u.NAME) AS value_occurrence
FROM USERS u
INNER JOIN ALBUMS a ON a.USER_ID = u.ID
GROUP BY u.NAME
ORDER BY value_occurrence DESC
LIMIT 1;
The problem with ORDER BY 'value_occurrence' is that you are telling SQLite to order by a constant value. That is, every record in the result set will have the same value for ordering, which basically means that SQLite is free to choose any record as being the "first."
Note: As the answer by #Cazzym mentioned, you should be aggregating by the user ID, in case two or more users might have the same name.
The code basically looks fine, but it will return unexpected results where two users have the same name. That's why we have ID columns!
Try
SELECT USERS.NAME, COUNT(USERS.ID) AS 'value_occurrence' FROM USERS
INNER JOIN ALBUMS
ON USERS.ID = ALBUMS.USER_ID
GROUP BY USERS.ID, USERS.NAME
ORDER BY 'value_occurrence'
DESC LIMIT 1;
We can use group by Users.Id, Users.Name because each ID is going to only have one name associated with it, so it's still going to only create a single group per ID.

Ambiguous column name when doing a JOIN

I've got some troubles when doing a join in my statement
This one is OK
select [db_id], COUNT(db_id) AS total
FROM [dbauditor_repo].[dbo].[dbauditor_repo_events]
GROUP BY [db_id]
ORDER BY total DESC
But I'm getting an error when doing the join (Ambiguous column name 'db_id'.)
SELECT [db_name], [db_id], COUNT(db_id) AS total
FROM [dbauditor_repo].[dbo].[dbauditor_repo_events] JOIN [dbauditor_repo].[dbo].[dbauditor_repo_warehouse]
ON ([dbauditor_repo].[dbo].[dbauditor_repo_events].[db_id] = [dbauditor_repo].[dbo].[dbauditor_repo_warehouse].[db_id])
WHERE [db_type] = 'mysql'
GROUP BY [db_id]
ORDER BY total DESC
Any idea ?
Just get in the habit of using table aliases for all column references. That will prevent such errors and other unexpected problems. The best practice is to use table abbreviations. Here is an example:
SELECT rw.[db_name], re.[db_id], COUNT(re.db_id) AS total
FROM [dbauditor_repo].[dbo].[dbauditor_repo_events] re JOIN
[dbauditor_repo].[dbo].[dbauditor_repo_warehouse] rw
ON re.[db_id] = rw.[db_id])
WHERE rw.[db_type] = 'mysql'
GROUP BY rw.[db_name], re.[db_id]
ORDER BY total DESC;
Of course, I have to guess which tables the columns come from. You should fix the query so they come from the right tables.
As a bonus, when using table aliases, queries are easier to write and to read.
You need to use fully qualified name when joining tables and the column exist in more than one table.
Try:
SELECT [dbauditor_repo_events].[db_id]
You also need to include the column db_name in the GROUP BY clause as this column is not being aggregated.
GROUP BY [dbauditor_repo_events].[db_id], [db_name]
It's in your select and group by:
SELECT [db_name], [dbauditor_repo_events].[db_id], COUNT([dbauditor_repo_events].db_id) AS total
FROM [dbauditor_repo].[dbo].[dbauditor_repo_events] JOIN [dbauditor_repo].[dbo].[dbauditor_repo_warehouse]
ON ([dbauditor_repo].[dbo].[dbauditor_repo_events].[db_id] = [dbauditor_repo].[dbo].[dbauditor_repo_warehouse].[db_id])
WHERE [db_type] = 'mysql'
GROUP BY [dbauditor_repo_events].[db_id]
ORDER BY total DESC
You should maybe consider aliasing your tables in the query for readability,e.g.:
SELECT [db_name], e.[db_id], COUNT(e.db_id) AS total
FROM [dbauditor_repo].[dbo].[dbauditor_repo_events] as e
JOIN [dbauditor_repo].[dbo].[dbauditor_repo_warehouse] as w
ON (e.[db_id] = w.[db_id])
WHERE [db_type] = 'mysql'
GROUP BY e.[db_id]
ORDER BY total DESC
Problem:
It is because:
[dbauditor_repo].[dbo].[dbauditor_repo_events]
and
[dbauditor_repo].[dbo].[dbauditor_repo_warehouse]
have the same column name db_id. The Query does not know which table's db_id to be used. Hence this error.
Solution:
Use TableName.FieldName or TableAliasName.FieldName to avoid this error. Change your Query as:
SELECT [db_name],
TAB1.[db_id] ,
COUNT(TAB1.db_id) AS total
FROM
[dbauditor_repo].[dbo].[dbauditor_repo_events] AS TAB1
JOIN
[dbauditor_repo].[dbo].[dbauditor_repo_warehouse] AS TAB2
ON
(TAB1.[db_id] = TAB2.[db_id])
WHERE [db_type] = 'mysql'
GROUP BY [db_name],[TAB1.db_id]
ORDER BY total DESC

Not a single-group group function

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 ;)

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