My SQL is only returning one field when it should be returning one for each user.
Any idea where I'm going wrong? If you need additional information I can provide, but I'm just not sure where to go with this at the moment.
Here is my SQL:
SELECT uId, uForename, SUM(biProductPrice * biQuantity) AS uTotalSpent
FROM users
LEFT JOIN orders ON uId = ordUserId
LEFT JOIN basket ON ordUserId = bUserId
LEFT JOIN basketitems ON bId = biBasketId
WHERE ordStatus BETWEEN 4 AND 50
GROUP BY uId, uForename
any columns starting with u belong to the users table.
any columns starting with ord belong to the orders table.
any columns starting with b belong to the basket table.
any columns starting with bi belong to the basketitems table.
EDIT:
Everything now works fine except for my SUM, there are only 2 fields with an ordStatus between 4 and 50, so they are the only ones that apply, the biQuantity for one is 8 and the biProductPrice is 100, the other field has a biQuantity of 1 and a biProductPrice of 100, why is it returning a value of 400?
Group by the user and the sum will be returned for each one
SELECT users.id, users.name, SUM(biProductPrice) AS uTotalSpent
FROM users
LEFT JOIN orders ON uId = ordUserId
LEFT JOIN basket ON ordUserId = bUserId
LEFT JOIN basketitems ON bId = biBasketId
WHERE ordStatus BETWEEN 4 AND 50
group by users.uId, users.name
SELECT users.id, users.name, SUM(biProductPrice) AS uTotalSpent
FROM users
LEFT JOIN orders ON uId = ordUserId
LEFT JOIN basket ON ordUserId = bUserId
LEFT JOIN basketitems ON bId = biBasketId
WHERE ordStatus BETWEEN 4 AND 50
group by users.uId, users.name
Related
The main table has 4 columns:
User Activity Table
userActivityId userId therapistId activityId
1 1 1 1
Each of these columns is a table and these values are all foreign keys.
Basically im trying to run a query that will join to the users table and pull their first and last name based off the user Id.Same thing with therapist - join to the therapist table, pull first + last name.And finally Join to the Activity table and pull the activity name and path from the activity Id
The other tables look like this:
User Table
userId fName lName
Therapist Table
therapistId therapistFirstName therapistLastName
Activity Table
activityId activityTitle activityPath
So far my query looks like
SELECT
User_Activities.userId,
User_Activities.therapistId,
User_Activities.activityId,
Activities.activityTitle,
Activities.activityPath,
Users.fName,
users.lName,
Therapists.therapistFirstName,
Therapists.therapistLastName
FROM
User_Activities
INNER JOIN Users
ON User_Activities.userId = Users.userId
INNER JOIN Therapists ON
User_Activities.therapistId = Therapists.therapistId
INNER JOIN Activities ON
Activities.activityId = User_Activities.userActivityId
WHERE
User_Activities.userId = 1;
When I run this query It only returns 1 row as a result. However there are two activities in the User_Activites table assigned to userId 1.
If I change : INNER JOIN Activities ON
Activities.activityId = User_Activities.userActivityId
from an INNER JOIN to the LEFT JOIN it will display the second row, however the activityTitle and activityPath will be displayed as NULL in the second row.
userActivityId userId therapistId activityId activityId activityTitle activityPath fName lName therapistFirstName therapistLastName
1 1 1 1 1 Brain GZZ0zpUQ S C M D
11 1 1 1 NULL NULL NULL S C M D
You have pretty much answered your question. The second activity does not have a valid ActivityId.
If you want all activities for a user, then you should phrase the query as:
SELECT . . .
FROM Users u LEFT JOIN
User_Activities ua
ON ua.userId = u.userId LEFT JOIN
Therapists t
ON ua.therapistId = t.therapistId LEFT JOIN
Activities a
ON a.activityId = ua.userActivityId
WHERE u.userId = 1;
You want to start with the table where you want to keep all the rows. Then use LEFT JOIN to bring in other tables.
Two other changes of note:
Table aliases are used to simplify reading and writing the query. The SELECT needs to change to use the aliases.
The WHERE clause refers to the Users table rather than UserActivities.
I have this query:
SELECT suppliers.id, count(*)
FROM suppliers
INNER JOIN supplier_addresses
ON suppliers.id = supplier_addresses.supplier_id
GROUP BY suppliers.id;
this gives my a table of supplierId and count of its addresses in the supplier_addresses table. But it only shows me suppliers that have at least 1 address.
I want to see in the result also count of 0 addresses...for example:
supplier.id | count(*)
1 3
2 0
3 1
4 9
in my query I dont see the second record.
Use LEFT JOIN
SELECT suppliers.id, count(supplier_addresses.supplier_id )
FROM suppliers
LEFT JOIN supplier_addresses
ON suppliers.id = supplier_addresses.supplier_id
GROUP BY suppliers.id;
Consider the following data set:
users table:
id (int) email (string)
1 first#example.com
2 second#example.com
order_items table:
id (int) user_id (int) generation (string)
1 1 '11'
2 1 '12'
2 1 '12.50'
3 1 '16.00'
4 2 '11'
5 2 '12'
UPDATED question
How can I select users which doesn't have order_items with generation 16.00 and have at least one order_item?
So:
email
second#example.com
1) Returning Users who don't have order item with generation 16 included users with no orders at all.
Assuming you have some kind of id column in order_items table:
select u.* from users u
left outer join order_items oi on (u.id = oi.user_id and oi.generation = 16)
where oi.id is null;
Otherwise use whatever primary key you have in order_items in the where condition to be NULL.
Updated to include answer for the question in comment
2) Returning users who don't have order item with generation 16 but have least one order.
select distinct u.* from users u
left outer join order_items oi16 on (u.id = oi.user_id and oi.generation = 16)
join order_items oiother on (u.id = oiother.user_id and oiother.generation != 16)
where oi16.id is null;
We do the filtering by using a second (normal) join which only returns users where it finds matching rows from the order_items table.
Here we need the distinct because the second join will multiply your rows depending on how many other orders the user have.
Alternatively you can also do a count or sum like this:
select u.*, count(distinct oiother.id) from users u
left outer join order_items oi16 on (u.id = oi.user_id and oi.generation = 16)
join order_items oiother on (u.id = oiother.user_id and oiother.generation != 16)
where oi16.id is null
group by u.id;
This will give you also how many other order items each returned user have. Or omit the count completely and using group by just to return distinct items.
You can use NOT EXISTS() like this:
SELECT * FROM Users u
WHERE NOT EXISTS(SELECT 1 FROM order_items o
WHERE o.userid = u.id
AND o.generation = 16)
That checks if there is a record for this user with order.generation = 16, and if there isn't it selects him.
Or not in()
SELECT * FROM Users u
WHERE u.id NOT IN(SELECT userid FROM order_items o
WHERE o.generation = 16)
That selects the list of users who have order.generation = 16, and select every id except them.
Following query should give you the desired output:
*update*
changed query as per the new result format in the question
As we want the data only from generation table, join with user table is not needed anymore. Here's the updated query:
select id, generation
from mytable where id not in (
select id from mytable
where generation = 16
group by id
);
Here is the SQL fiddle for it.
I cannot find the answer to my problem here on stackoverflow. I have a query that spans 3 tables:
newsitem
+------+----------+----------+----------+--------+----------+
| Guid | Supplier | LastEdit | ShowDate | Title | Contents |
+------+----------+----------+----------+--------+----------+
newsrating
+----+----------+--------+--------+
| Id | NewsGuid | UserId | Rating |
+----+----------+--------+--------+
usernews
+----+----------+--------+----------+
| Id | NewsGuid | UserId | ReadDate |
+----+----------+--------+----------+
Newsitem obviously contains newsitems, newsrating contains ratings that users give to newsitems, and usernews contains the date when a user has read a newsitem.
In my query I want to get every newsitem, including the number of ratings for that newsitem and the average rating, and how many times that newsitem has been read by the current user.
What I have so far is:
select newsitem.guid, supplier, count(newsrating.id) as numberofratings,
avg(newsrating.rating) as rating,
count(case usernews.UserId when 3 then 1 else null end) as numberofreads from newsitem
left join newsrating on newsitem.guid = newsrating.newsguid
left join usernews on newsitem.guid = usernews.newsguid
group by newsitem.guid
I have created an sql fiddle here: http://sqlfiddle.com/#!9/c8add/8
Both count() calls don't return the numbers I want. numberofratings should return the total number of ratings for that newsitem (by all users). numberofreads should return the number of reads for the current user for that newsitem.
So, newsitem with guid d104c330-c319-40e8-8be3-a7c4f549d35c should have 2 ratings and 3 reads for the current user with userid = 3.
I have tried conditional counts and sums, but no success yet. How can this be accomplished?
The main problem that I see is that you're joining in both tables together, which means that you're going to effectively be multiplying out by both numbers, which is why your counts aren't going to be correct. For example, if the Newsitem has been read 3 times by the user and rated by 8 users then you're going to end up getting 24 rows, so it will look like it has been rated 24 times. You can add a DISTINCT to your COUNT of the ratings IDs and that should correct that issue. Average should be unaffected because the average of 1 and 2 is the same as the average of 1, 1, 2, & 2 (for example).
You can then handle the reads by adding the userid to the JOIN condition (since it's an OUTER JOIN it shouldn't cause any loss of results) instead of in a CASE statement for your COUNT, then you can do a COUNT on distinct id values from Usernews. The resulting query would be:
SELECT
I.guid,
I.supplier,
COUNT(DISTINCT R.id) AS number_of_ratings,
AVG(R.rating) AS avg_rating,
COUNT(DISTINCT UN.id) AS number_of_reads
FROM
NewsItem I
LEFT OUTER JOIN NewsRating R ON R.newsguid = I.guid
LEFT OUTER JOIN UserNews UN ON
UN.newsguid = I.guid AND
UN.userid = #userid
GROUP BY
I.guid,
I.supplier
While that should work, you might get better results from a subquery, as the above needs to explode out the results and then aggregate them, perhaps unnecessarily. Also, some people might find the below to be a little clearer.
SELECT
I.guid,
I.supplier,
R.number_of_ratings,
R.avg_rating,
COUNT(*) AS number_of_reads
FROM
NewsItem I
LEFT OUTER JOIN
(
SELECT
newsguid,
COUNT(*) AS number_of_ratings,
AVG(rating) AS avg_rating
FROM
NewsRating
GROUP BY
newsguid
) R ON R.newsguid = I.guid
LEFT OUTER JOIN UserNews UN ON UN.newsguid = I.guid AND UN.userid = #userid
GROUP BY
I.guid,
I.supplier,
R.number_of_ratings,
R.avg_rating
I'm with Tom you should use a subquery to calculate the user count.
SQL Fiddle Demo
SELECT NI.guid,
NI.supplier,
COUNT(NR.ID) as numberofratings,
AVG(NR.rating) as rating,
user_read as numberofreads
FROM newsitem NI
LEFT JOIN newsrating NR
ON NI.guid = NR.newsguid
LEFT JOIN (SELECT NewsGuid, COUNT(*) user_read
FROM usernews
WHERE UserId = 3 -- use a variable #user_id here
GROUP BY NewsGuid) UR
ON NI.guid = UR.NewsGuid
GROUP BY NI.guid,
NI.supplier,
numberofreads;
I have a table called reviews. I get the most current user reviews like this:
SELECT b.item, b.item_id, a.review_id, a.review, c.category, u.username, c.cat_id
FROM reviews a
INNER JOIN items b
ON a.item_id = b.item_id
INNER JOIN master_cat c
ON c.cat_id = b.cat_id
INNER JOIN users AS u
ON u.user_id = a.user_id
ORDER BY a.review_id DESC;
What I want to do is slightly alter it to be more personable for users.
I have another table of user "connections". Kind of like Twitter. When a user follows someone, it gets logged in this table called profile_follow. This has three columns. id, user_id, follow_id. Simply: If I am user #1, and I "follow" user # 3 and user #5, two rows will be added in this table:
profile_follow
------------------------
id | user_id | follow_id
| 1 | 3
| 1 | 5
Here is how I want to change the query above. I want to only show newest reviews, from people you follow.
So I will need at least one more join, for table profile_follow. And I need to pass in a user_id (it's a php function), doing something like `WHERE profile_follow.user_id = '{$user_id}'. I think I will have to add a sub query on this, not use.
Can someone show me how to finish this query? I am not sure how to handle it from here? All of my attempts have been off so far.
I think I need to do something like:
Selectfollow_idwhereuser_id= (logged in user)
And then in the main query:
Select reviews only with profile_follow.follow_id = review.user_id.
I can't figure out how to make this filter work.
Always difficult without testing, but:
SELECT b.item, b.item_id, a.review_id, a.review, c.category, u.username, c.cat_id
FROM reviews a
INNER JOIN items b
ON a.item_id = b.item_id
INNER JOIN master_cat c
ON c.cat_id = b.cat_id
INNER JOIN profile_follow pf
ON pf.follow_id = a.user_id
WHERE profile_follow.user_id = '{$user_id}'
ORDER BY a.review_id DESC;