Find average SQL statement multiple tables - sql

Trying to get the average with a amount of times a user has purchases brocolli and then with the price at that time. A 0 if the user has not purchased any. This is working and saying it can't see the Name column. What am I missing?
SELECT U.ID, U.NAME, COALESCE(AVG(P.PRICE),0) AS SELLPRICE FROM USERS AS U
LEFT JOIN PURCHASES AS P ON U.ID=P.USERID AND P.FoodId=1 GROUP BY U.ID;
EDIT
SELECT P.USERID, U.NAME, AVG(P.PRICE) AS "Sell Price"
FROM PURCHASES AS P INNER JOIN USERS AS U ON CASE WHEN P.ID NOT NULL THEN
"Sell Price" ELSE 0 WHERE P.FOODID=1
I also tried simplifying to just use the purchasers table and get wrong results but maybe I can tweek this as it runs.
SELECT AVG(A.Price),ID FROM PURCHASES AS A WHERE FOODID=1 GROUP BY ID;
To be honest this was in part this issue with the compiler I was using as it was browser site compiler so even when I had it working on my machine it was giving different results on the site. I ended using an inner join on the two tables.
Update
This ran correctly for the answer. Thank you.
SELECT U.ID, U.NAME, AVG(P.PRICE) FROM USERS AS U LEFT JOIN PURCHASES AS P ON U.ID = P.USERID AS P AND P.FoodId=1 GROUP BY U.ID, P.NAME;

If I understand correctly, you are trying to get the average purchase price of broccoli for each user. You are closer to achieving this with your second query, you just need to group by the UserId column (to get the average per user), not the Id column (this would give you the average per purchase - kind of meaningless) of the Purchases table. So, i think all you need is:
SELECT AVG(PRICE), USERID FROM PURCHASES WHERE FOODID=1 GROUP BY USERId
If you want to add some user information to your output, like their name, you will need to join with the Users table:
SELECT AVG(P.PRICE), P.USERID, U.NAME
FROM PURCHASES AS P INNER JOIN USERS AS U ON P.USERID = U.ID
WHERE P.FOODID=1
GROUP BY P.USERId, U.NAME
Your first query I'm afraid has more than one syntax issues. For example, the name aver_output doesn't correspond to a table or subquery column. If you were trying to name your average price column, you would need the AS keyword (or just lose the comma). Also, you have a subquery and next to it you have a table (Users) without any correlation between them. You must specify how you want to join the two, i.e. whether you want an inner, outer, left or right join. In most systems you can also use a comma to indicate a join, but you don't even have that.
In any case, even if you do fix the syntax, the subquery is unnecessary, as you can achieve the same thing without one.
Edit (after edits to the original post):
The best way to include the users who have not ever purchased any broccoli, is to perform a left join to the PURCHASES table, as in your last attempt. However, you need to group by the name of the user, because it appears in your select list. Grouping by the name of the product is not necessary in this case. So, I suggest:
SELECT U.ID, U.NAME, AVG(P.PRICE) FROM USERS AS U LEFT JOIN PURCHASES AS P ON U.ID = P.USERID AND P.FoodId=1 GROUP BY U.ID, U.NAME;

You need a LEFT JOIN of USERS to PURCHASES and GROUP BY user:
SELECT
U.ID, U.NAME,
COALESCE(AVG(P.PRICE), 0) AS "Sell Price",
COUNT(P.USERID) AS "Number of purchases"
FROM USERS AS U LEFT JOIN PURCHASES AS P
ON P.USERID = U.ID AND P.FOODID = 1
GROUP BY U.ID, U.NAME

SELECT U.Id, AVG(U.NAME)
FROM (SELECT PRICE, USERID, ID
FROM PURCHASES WHERE FOODID=1) P
Join USERS U on U.Id = P.USERId
GROUP BY U.ID
But actually, all you should need is
SELECT U.Id, AVG(U.NAME)
FROM PURCHASES P Join USERS U
on U.Id = P.USERId
WHERE P.FOODID = 1
GROUP BY U.ID
except I still don't see why you are asking for average of Name column...
What exactly are you trying to do?

Related

Best approach for limiting rows coming back in SQL when joining for a sum

I need to get back a list of users and the total amount that they have ordered. In reality my query is more complex but I think this sums it up. My issue is, if a user made 5 orders for example, I'll get back their name and the total they've ordered 5 times due to the join (having 5 rows in the order table for that user).
What's the recommended approach for when you need to total the records in one table that has multiple rows without requiring many rows to come back? distinct could work but is this the best? (especially when my select chooses more information than what's below)
SELECT user.name, sum(order.amount) FROM USER user
INNER JOIN USER_ORDERS order
ON (user.user_id = order.user_id)
Are you just looking for GROUP BY?
SELECT u.name, SUM(o.amount)
FROM USER u JOIN
USER_ORDERS uo
ON u.user_id = uo.user_id
GROUP BY u.name, u.user_id;
Note that this has included user_id in the GROUP BY, just in case two users have the same name.
If you want all users, even those without orders, then you want a LEFT JOIN:
SELECT u.name, SUM(o.amount)
FROM USER u LEFT JOIN
USER_ORDERS uo
ON u.user_id = uo.user_id
GROUP BY u.name, u.user_id;
Or a correlated subquery:
SELECT u.name,
(SELECT SUM(o.amount)
FROM USER_ORDERS uo
WHERE u.user_id = uo.user_id
)
FROM USER u;
You could use the analytic version of SUM.
SELECT u.name, SUM(o.amount) OVER(PARTITION BY u.name)
FROM USER u JOIN
USER_ORDERS uo
ON u.user_id = uo.user_id;

PostgreSQL: Get the count of rows in a join query

I am trying to get some data joining few tables. I have an audit table where I store the audits for actions performed by users. I am trying to get the list of users in the order of the number audits they have and the number of audits. I have the following query:
SELECT s.user_created,
u.first_name,
u.last_name,
u.email,
a.message as audits
FROM cbrain_school s
inner join ugrp_user u on s.user_created = u.user_id
inner join audit a on u.user_id = a.user_id
order by u.created_time desc;
This query will give me 1 row per entry in the audit table. I just want 1 row per user and the count of entries in the audit table ordered by the number of audits.
Is there any way to do that. I was getting an error when I tried to include count() in the above query
First of all you are joining with the table cbrain_school. Why? You are selecting no data from this table (except for s.user_created which is simply u.user_id). I suppose you want to limit the users show to the cbrain_school.user_created? Then use EXISTS or IN to look this up.
select u.user_id, u.first_name, u.last_name, u.email, a.message as audits
from ugrp_user u
inner join audit a on u.user_id = a.user_id
where u.user_id in (select user_created from cbrain_school)
order by u.created_time desc;
This shows much better that cbrain_school.user_created is mere criteria. (But the query result is the same, of course.) It's a good habit to avoid joins, when you are not really interested in the joined rows.
Now you don't want to show each message anymore, but merely count them per user. So rather then joining messages, you should join the message count:
select u.user_id, u.first_name, u.last_name, u.email, a.cnt
from ugrp_user u
inner join
(
select user_id, count(*) as cnt
from audit
group by user_id
) a on u.user_id = a.user_id
where u.user_id in (select user_created from cbrain_school)
order by u.created_time desc;
(You could also join all messages and only then aggregate, but I don't recommend this. It would work for this and many other queries, but is prone to errors when working with multiple tables, where you might suddenly count or add up values multifold. It's a good habit to join before aggregating.)

sql - How to have multiple select/from statements in one query

I'm trying to pull a report where each column is selecting from a specific table set. However, one of the columns needs to pull from a completely different table set and still be included in the same report. Of course, this doesn't work:
select u.first_name, ticket_work.time_spent
FROM tickets LEFT OUTER JOIN ticket_work ON ticket_work.ticket_id = tickets.id JOIN users u
(select count(tickets.id) FROM tickets JOIN users u)
where tickets.assigned_to = u.id
...
So just the part (select count(tickets.id) FROM tickets JOIN users u) needs to be selecting from the different table set but still be included in the report.
I'm a little confused by your question. Are you wanting to return the user, the count of tickets for that user, and the amount of time spent overall? If so, something like this should work:
select u.id, u.first_name,
SUM(tw.time_spent) summed_time_spent,
COUNT(DISTINCT t.id) count_tickets
FROM users u
LEFT JOIN tickets t
ON u.id = t.assigned_to
LEFT JOIN ticket_work tw
ON tw.ticket_id = t.id
GROUP BY u.id, u.first_name
Your questions is unclear, but just generally, it sounds like you're trying to join to a derived table (i.e., a query). In that case, do this:
SELECT...
FROM...
table_A A LEFT JOIN
(SELECT keyfield, valuefield FROM table_b WHERE ...) B
ON A.keyfield = B.keyfield
Does that make sense? To make a derived table, you put a query inside of parenthesis, give it an alias ('B' in this case), and then join it to your other tables as though it were a regular table.
Don't know about your table structure but you may use a sub query for such requirement
select u.first_name, ticket_work.time_spent,(select count(tickets.id) FROM tickets where ticket.id=ticket_work.ticket_id) as myCount
FROM tickets LEFT OUTER JOIN ticket_work ON ticket_work.ticket_id = tickets.id JOIN users u
where tickets.assigned_to = u.id

SQL QUERY to get Count of particular column between two Range of Dates

Here I have a doubt regarding sql query.
In this scenario I have a table called tblcrime : where we will get the sum(crime) here I track MainID and sum(crime) query will be like this :
SELECT sum(o.crimeID) as crimeNumber,u.UserID
from tblcrime o
inner join tblSubContractor ts on
o.MainID=ts.SubContractorID
from here I will chk the tblUSER with these subcontractorID :
inner join tblUser u on
u.SubContractorID=ts.SubContractorID
and my doubt is that up to here I will get the total sum of crime and appropriate userid., for e.g.
UserID : 520 Totalcrime:6000
but there is another table called tblAudit where we will get logondate and userid, which is tracking here.. so I want to display crime based on userlogin(userid) ...since last login. So that when user login it shows in a jquery notification that "60 crimes has been done since last login".
I want help in query format.
I'm not sure, if I understand your question right, but may this be, what you are looking for?
SELECT sum(o.crimeID) as crimeNumber,u.UserID
from tblcrime o
inner join tblSubContractor ts on o.MainID=ts.SubContractorID
inner join tblUser u on u.SubContractorID=ts.SubContractorID
where
u.UserID = theOneYouAreLookingFor
AND crimedate >= lastLogOn
GROUP BY u.UserID
Firstly, I suspect that the call to the SUM function should really be to COUNT. The former adds the values of the specified column together, whereas the latter gives you a row count.
Secondly, does your tblcrime table store the date that crimes are added? I'll assume it does, let's call the column DateAdded. The following query should work:
SELECT COUNT(o.crimeID) AS crimeNumber,
u.UserID
FROM tblcrime o
INNER JOIN tblSubContractor ts on o.MainID = ts.SubContractorID
INNER JOIN tblUser u on u.SubContractorID = ts.SubContractorID
INNER JOIN tblAudit a on a.userid = u.UserID
WHERE a.logondate < o.DateAdded
GROUP BY u.UserID
You could find the max auditdate for that user:
SELECT sum(o.crimeID) as crimeNumber,u.UserID
from tblcrime o
inner join tblSubContractor ts on
o.MainID=ts.SubContractorID
inner join tblUser u on
u.SubContractorID=ts.SubContractorID
where o.crimeDate >= (select max(auditdate) from tblAudit where UserID = #UserID)

SQL Query without nested queries

Let's say we have these tables;
table user:
- id
- username
- email
table user2group:
- userid
- groupid
table group:
- id
- groupname
How do I make one query that returns all users, and the groups they belong to (as an array in the resultset or something..)
select u.id, u.username, u.email, g.groupid, g.groupname
from user u
join user2group ug on u.userid=ug.userid
join group g on g.groupid=ug.groupid
order by u.userid
As you are looping through the result set, each time you see a new userid make a new user object (or whatever) and add the groups to it.
Eric's answer is great, but I would use a LEFT JOIN instead of an INNER to get users that do not belong to any group as well.
SELECT
u.id,
u.username,
u.email,
g.groupid,
g.groupname
FROM
user u
LEFT JOIN user2group ug ON u.userid = ug.userid
LEFT JOIN group g ON g.groupid = ug.groupid
ORDER BY
u.userid
Both of the above are more or less correct (deepends if each user has a group or not). But they will also both give a result set with several entries for each user.
There are ways of concatenating every group member into one comma separated string, I'd suggest you read about it here:
http://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/
Another method I personally like is to use bit values instead of the relational table user2group
table user then gets a int (or bigint) field group, and each group ID is assigned one bit value (ie: 1,2,4,8,16 and so on) The value of the user table's group field is then the sum of the groupID it's assigned to. To query if its got a group you do:
where (group AND groupID = groupID)