This question already has answers here:
What's the difference between INNER JOIN, LEFT JOIN, RIGHT JOIN and FULL JOIN? [duplicate]
(3 answers)
Closed 8 years ago.
I have 3 tables - User table, book1 table, book2 table.
User table is like this -
user_id | gender | l_name | f_name
-------- -------- -------- -------
1 male Doe Jon
2 female Xu Jini
3 female Din Jane
book1 table -
b_id | user_id | amount | date
----- --------- -------- ----------
1 3 98.30 2014-05-14
2 1 65.70 2014-05-07
3 2 14.40 2014-05-06
4 2 55.60 2014-05-07
book2 table -
b_id | user_id | amount | date
----- --------- -------- ----------
1 2 38.20 2014-04-06
2 3 84.40 2014-04-02
3 3 31.30 2014-04-12
4 1 74.40 2014-05-06
The user gives a date range as input and I want to calculate the sales count(COUNT), total amount(SUM) and the max date(MAX) for that date range. After this I want to connect this data to the user table and get the gender and name using the user_id.
I wrote this query to get the data for the given date range from book1 and book2 tables-
SELECT * FROM book1
WHERE date between '2014-04-02' and '2014-05-15'
UNION ALL
SELECT * FROM book2
WHERE date between '2014-04-02' and '2014-05-15'
ORDER BY customer_id;
By this i get all the rows in the book1 and book2 table which satisfy the date range. Now should i use subquery or something else to reach the goal. I think sql should take care till getting the count, sum and max from book tables. Then the connection to the user table should be done in PHP. Am i on the right path? Can everything be done in SQL? I am kinda lost.
Yes, you can do it in SQL using a plain JOIN.
This will basically get all users and join them up with their respective amounts in the period. After that, the results are grouped by user so that we can sum up the amounts.
SELECT u.user_id, u.l_name, u.f_name, SUM(x.amount) `total amount`
FROM user u
JOIN (
SELECT user_id, date, amount FROM book1
UNION ALL
SELECT user_id, date, amount FROM book2
) x
ON u.user_id = x.user_id
AND x.date between '2014-04-02' and '2014-05-15'
GROUP BY u.l_name, u.f_name,u.user_id
An SQLfiddle to test with.
As a side note, learning about joins is really a necessity to work efficiently with SQL databases.
Related
I have the following tables:
Users
id | name
-----------------
1 | Johny Bravo
Orders
id | users_id | number
----------------------
1 | 1 | 111111
2 | 1 | 222222
3 | 1 | 333333
4 | 1 | 444444
Example
id | text | number
------------------
1 | test | 111111
2 | test | 111111
3 | test | 222222
4 | test | 222222
5 | test | 333333
6 | test | 333333
Desired Outcome
id: 1
name: Johny Brawo
count(orders): 4
count(example): 6
My current query, which doesn't work
SELECT users.id, users.name, count(orders.id), count(example.id)
FROM users
LEFT JOIN orders ON orders.users_id=users.id
LEFT JOIN example ON example.number=orders.number
GROUP BY users.id
My current result
id: 1
name: Johny Brawo
count(orders): 8
count(example): 8
Any help would be greatly appreciated.
try count(distinct orders.id), count(distinct example.id)
I've not done any MySQL really, but this works in other Databases...
Starting - a little bit of theory. What does your query do?
First it SELECTs something from users table.
Then it LEFT JOINs with orders table. Number of returned rows is a multiplication of rows from users table and matching rows from orders table. So with only this join you will have 6 rows, each one with Johny Bravo as user, but with different orders data.
Then - another LEFT JOIN. This time with example table. Again - a number of returned rows is a multiplication of rows from orders table and matching rows from example table. So without GROUP BY and COUNT you will have eight rows of result.
Now, the GROUP BY query part. What does it do? It just groups rows with matching GROUP BY column(s). So it will group all rows with same users.id. There are eight of them.
Standard COUNT() will return a number of rows with not null value. As there were eight rows, both counts will return 8.
Now, as #GPW suggested, the solution is a COUNT(DISTINCT x). This function returns a count of unique not null rows.
Thus, the query should look like:
SELECT users.id, users.name, count(DISTINCT orders.id), count(DISTINCT example.id)
FROM users
LEFT JOIN orders ON orders.users_id=users.id
LEFT JOIN example ON example.number=orders.number
GROUP BY users.id
UPDATE - ordering and strict databases
You have also asked about ordering the result. You can order it by any column from your query. As MySQL is not very strict when it comes to grouping, you will also be able to order by any column from users table, as users table results are unique (grouped by id). You can also add, for example ORDER BY COUNT(DISTINCT orders.id) DESC to find users with largest number of orders.
Most databases, though, is more strict in GROUP BY queries. It allows to SELECT only columns with aggregated values or those explicitly contained in GROUP BY clause. So your GROUP BY clause should rather look like
GROUP BY users.id, users.name
I have the following tables in my database that records user errors as a log so i can see who has made errors and at what time.
log, users, errortype and timeperiod.
Users contains,
id|name
1 David
2 Mark
3 Darren
errortype contains
id | typeoferror
1 error type 1
2 error type 2
3 errortype 3
timeperiod contains
id | period
1 7am-11am
2 11am - 3pm
3 3pm - 7pm
4 7pm-11pm
and log contains
id | user | date | time | staff | typeofmiss | timeperiod | dateoferror | notes
1 | 1 |1/1/15|11:23 | 2 | 2 | 3 | 1/1/15| blah
I would like the user column and the staff column both to link to the users table and the typeofmiss column to link to the errortype column and the timeperiod column to link to the timeperiod table so that when i have run a query it would return:
1 - David - 1/1/15 - 11:23 - Mark - errortype 2 - 3pm-11pm - 1/1/15 - blah
I have managed to get a query partly working, but i can't get the link the data from the users table twice i.e. for the user column and staff column, it returns the same name for both columns.
Is there a way to get this to workor do i have to get the data out and then change the numbers to the names using php or something?
Thanks
You need to alias the tables and then specify the (aliased) columns to select.
SELECT
a.id, a.user, a.date, b.name AS user_name, c.name AS staff_name, d.typeoferror
FROM
log AS a
JOIN
users AS b ON a.user = b.id
JOIN
users AS c ON a.staff = c.id
JOIN
errortype d ON a.typeofmiss = d.id
https://dev.mysql.com/doc/refman/5.1/en/join.html
I am trying to figure out how to rank based on 2 different column numbers in laravel but raw mysql will do. I have a list of videos, these videos are inside of competitions and are given votes if someone likes the video. Each video will have a vote number and a competition number. I am trying to rank based on votes within competition. So for example below I have competition 8, I need the rank of all the videos in that competition based on votes. I then need the same for competition 5 etc.
|rank|votes|competition|
------------------
| 1 | 100 | 8 |
------------------
| 2 | 50 | 8 |
------------------
| 3 | 30 | 5 |
------------------
| 1 | 900 | 5 |
------------------
| 2 | 35 | 5 |
------------------
I have tried various group and selectby methods but nothing seems to work, any ideas?
In Mysql you can use user-defined variables to calculate rank,case statement checks if competition is same as the previous row then increment rank by one if different then assign 1 an order by is needed to have correct rank for the video
SELECT t.*,
#current_rank:= CASE WHEN #current_rank = competition
THEN #video_rank:=#video_rank +1
ELSE #video_rank:=1 END video_rank,
#current_rank:=competition
FROM t ,
(SELECT #video_rank:=0,#current_rank:=0) r
ORDER BY competition desc, votes desc
See Demo
If you are confused with the last extra column you can use a subselect
See Demo
You can use windowing functions:
select
competition, votes, rank() over (partition by competition order by votes desc) as rank
from
table1
and thanks for taking the time to try and help me.
Informations
I'm currently using CodeIgniter if it might have anything to do with your answer ;).
Problem
I'm in a hotel site, trying to figure out how to do my reservation rooms.
I want users to select a list of available services and return to them, a list of rooms that contains these services ( all of them ) AND after that, a list that contains at least one. This way I'll show to them a list of rooms that comply with all their need, and one that might do the trick, but doesnt have everything.
Here's how I store my services for my rooms ( Here might lie my problem in fact ... )
Table "services_rooms"
id_services_rooms | id_room | id_service
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
5 | 1 | 5
11 | 2 | 2
12 | 2 | 3
... | ... | ...
How can I manage to do my SQL to ask my server give me all of the rooms that contains the services 1, 2 AND 3, therefore, only my "id_room" 1 should come back ?
I've tried doing some joins / group_bys but the most I got was for exemple, 3 row coming back saying :
Return rows :
ID_ROOM 1 | ID_SERVICE 1
ID_ROOM 1 | ID_SERVICE 2
ID_ROOM 1 | ID_SERVICE 3
Another way to see it, would be like that : I want to ask my server which rooms contains ALL of these services : 1,2,3
It would answer : ID_ROOM 1.
I've seen a couple of other questions talking about merges and such but couldn't quite apply their answers to my problem.
Thanks again.
This is called Relational Division.
SELECT id_room
FROM services_rooms
WHERE id_service IN (1,2,3)
GROUP BY id_room
HAVING COUNT(*) = 3
SQLFiddle Demo
if unique constraint was not enforced on id_service for ech id_room, DISTINCT is required.
SELECT id_room
FROM services_rooms
WHERE id_service IN (1,2,3)
GROUP BY id_room
HAVING COUNT(DISTINCT id_service) = 3
The answer to your question returns all rooms that have at least one of the services. The query is:
SELECT id_room, count(*) as NumServices
FROM services_rooms
WHERE id_service IN (1,2,3)
GROUP BY id_room
HAVING COUNT(*) > 0
order by count(*) desc
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
SQL ORDER BY total within GROUP BY
UPDATE: I've found my solution, which I've posted here. Thanks to everyone for your help!
I'm developing a Facebook application which requires a leaderboard. Scores and time taken to complete the game are recorded and these are organised by score first, then in the case of two identical scores, the time is used. If a user has played multiple times, their best score is used.
The lower the score, the better the performance in the game.
My table structure is:
id
facebook_id - (Unique Identifier for the user)
name
email
score
time - (time to complete game in seconds)
timestamp - (unix timestamp of entry)
date - (readable format of timestamp)
ip
The query I thought would work is:
SELECT *
FROM entries
ORDER BY score ASC, time ASC
GROUP BY facebook_id
The problem I'm having is in some cases it's pulling in the user's first score in the database, not their highest score. I think this is down to the GROUP BY statement. I would have thought the ORDER BY statement would have fixed this, but apparently not.
For example:
----------------------------------------------------------------------------
| ID | NAME | SCORE | TIME | TIMESTAMP | DATE | IP |
----------------------------------------------------------------------------
| 1 | Joe Bloggs | 65 | 300 | 1234567890 | XXX | XXX |
----------------------------------------------------------------------------
| 2 | Jane Doe | 72 | 280 | 1234567890 | XXX | XXX |
----------------------------------------------------------------------------
| 3 | Joe Bloggs | 55 | 285 | 1234567890 | XXX | XXX |
----------------------------------------------------------------------------
| 4 | Jane Doe | 78 | 320 | 1234567890 | XXX | XXX |
----------------------------------------------------------------------------
When I use the query above, I get the following result:
1. Joe Bloggs - 65 - 300 - (Joes First Entry, not his best entry)
2. Jane Doe - 72 - 280
I would have expected...
1. Joe Bloggs - 55 - 285 - (Joe's best entry)
2. Jane Doe - 72 - 280
It's like the Group By is ignoring the Order - and just overwriting the values.
Using MIN(score) with the group by selects the lowest score, which is correct - however it merges the time from the users first record in the database, so often returns incorrectly.
So, how can I select a user's highest score and the associated time, name, etc and order the results by score, then time?
Thanks in advance!
Your query does not actually make sense, because the order by should be after the group by. What SQL engine are you using? Most would give an error.
I think what you want is more like:
select e.facebookid, minscore, min(e.time) as mintime -- or do you want maxtime?
from entries e join
(select e.facebookid, min(score) as minscore
from entries e
group by facebookid
) esum
on e.facebookid = esum.facebookid and
e.score = e.minscore
group by e.facebookid, minscore
You can also do this with window functions, but that depends on your database.
One approach would be this:
SELECT entries.facebook_id, MIN(entries.score) AS score, MIN(entries.time) AS time
FROM entries
INNER JOIN (
SELECT facebook_id, MIN(score) AS score
FROM entries
GROUP BY facebook_id) highscores
ON highscores.facebook_id = entries.facebook_id
AND entries.score = highscores.score
GROUP BY entries.facebook_id
ORDER BY MIN(entries.score) ASC, MIN(entries.time) ASC
If you need more information from the entries table, you can then use this as a subquery, and join again on the information presented (facebook_id, score, time) to get one row per user.
You need to aggregate twice, is the crux of this; once to find the minimum score for the user, and again to find the minimum time for that user and score. You could reverse the order of the aggregation, but I would expect that this will filter most quickly and thus be most efficient.
You might also want to check which is faster, aggregating the second time: using the minimum score or grouping using the score as well.
You need to min the score
SELECT
facebook_id,
name,
email,
min(score) as high_score
FROM
entries
GROUP BY
facebook_id,
name,
email
ORDER BY
min(score) ASC
Thanks for your help. #Penguat had the closest answer.. Here was my final Query for anyone who might have the same issue...
SELECT f.facebook_id, f.name, f.score, f.time FROM
(SELECT facebook_id, name, min(score)
AS highscore FROM golf_entries
WHERE time > 0
GROUP BY facebook_id)
AS x
INNER JOIN golf_entries as f
ON f.facebook_id = x.facebook_id
AND f.score = x.highscore
ORDER BY score ASC, time ASC
Thanks again!
If you want their best time, you want to use the MIN() function - you said that the lower the score, the better they did.
SELECT facebook_id, MIN(score), time, name, ...
FROM entries
GROUP BY facebook_id, time, name, ...
ORDER BY score, time