My current MySQL join is not working - php

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

Related

Left Join and SUM on two tables, MYSQL

I have a table that contains information about an item, and another table that references the owner of that item, like so:
baseItem
--------
itemID | 1 | 2 | 3 | 4 |
itemSize | 5 | 1 | 5 | 3 |
itemCost | 100 | 50 | 1 | 99 |
itemOwner
--------
ownerID | 1 | 1 | 3
itemID | 1 | 4 | 2
What I'm after are the SUMS of itemSize and itemCOST based on the owner. I've looked around but none of the answers I've seen make sense? Here's the best I could come up with, which clearly isn't working:
SUM itemCost FROM baseItem.itemCost LEFT JOIN itemID ON itemOwner.itemid = baseItem.itemid
SELECT ownerId, sum(itemCost) as OwnerCost, sum(itemSize) as OwnerSize
FROM itemOwner
LEFT JOIN baseItem
ON itemOwner.itemid = baseItem.itemid
GROUP BY ownerId
A SELECT statement lists which fields you want to read from the table; in this case you want two values: the id of the owner, and the sum of the values of the items they own. However, since you're using sum (an aggregate function), you must GROUP your elements over some parameter. In this case, you want to group them by ownerId.
A FROM clause references a table; you can start with either baseItem or itemOwner, it makes no difference in this case. You can think of LEFT JOIN as a cartesian product that creates a new table, which contains every element from the cartesian product of both, filtered by the ON clause. However will always have all the items in the left table which is itemOwner and when there are no matching rows in the baseItem all the fields will be NULL. The SUM function will act as if those are 0s and should return you a 0 sum for non matching rows in the baseItem table.
Maybe it is not working because it is invalid SQL statement. Try following code
SELECT SUM(baseItem.itemCost) FROM baseItem
LEFT JOIN itemOwner ON itemOwner.itemId = baseItem.itemId

how to query for at least X results with a specific attribute

I am trying to get a certain amount of rows of which another amount of rows satisfy a specific condition.
I'll explain.
table 1:
ID | NAME
1 | Thomas
2 | Jason
3 | Oleg
4 | Matt
5 | Sheldon
6 | Jenny
table 2:
ID | ACTIVE
1 | 1
2 | 0
3 | 1
4 | 1
5 | 0
6 | 1
Query:
SELECT tbl_1.ID, tbl_1.NAME, tbl_2.ACTIVE
FROM tbl_1 JOIN tbl_2 ON
tbl_1.ID = tbl_2.ID
WHERE tbl_2.ACTIVE=1
LIMIT 5
in this example I would like to get a minimum number of 5 users, of which 3 are active.
of course the query above will not do the job right, as it limits the total rows to 5. But 3 of the rows in the result (or less if no more exist) MUST be active.
the other way I can think of getting this done, is a union, but my query is so cumbersome, long and complex.
Any ideas?
Use ORDER BY instead:
SELECT tbl_1.ID, tbl_1.NAME, tbl_2.ACTIVE
FROM tbl_1 JOIN
tbl_2
ON tbl_1.ID = tbl_2.ID
ORDER BY (tbl_2.ACTIVE = 1) DESC
LIMIT 5;
This puts the active users at the top of the list and then fills in the rest with other users.
Note: The ORDER BY clause could simply be ORDER BY tbl_2.ACTIVE DESC. I left the boolean logic so you could see the similarity to the WHERE clause.
The way to at least x results is to use the count aggregate and the keyword having
select f1, count(*) records
from yourTable
where whatever
group by f1
having count(*) > x

MySQL Find an ID based on 2 ids

i would like to see if player A (9) and player B (14) have ever both entered the same round, one round has many entries by many players. this is the middle table of a many to many relationship between rounds and players
table: entries
id | roundID | PlayerID
5 | 7 | 14
4 | 6 | 2
3 | 5 | 14
2 | 5 | 9
1 | 4 | 9
Im looking to return round ID 5 obviously, but what SQL statement does this need? a JOIN?
i could do it by getting all rounds played by player A and B seperately looping through As rounds and looping through Bs rounds on each iteration of A to look for a match, but that seems needlessly costly.
Something like this should work, basically getting a count of all the PlayerID enteries per roundID for only the specified players and restricting to show only ones with multiples.
SELECT
roundID
FROM
entries
WHERE
PlayerID IN (9, 14)
GROUP BY
roundID
HAVING
COUNT(*)>1
If I understand the question correctly, something as simple as a SELECT DISTINCT will work here:
SELECT DISTINCT roundID
FROM entries
WHERE PlayerID IN (9, 14)
Use INNER JOIN with subqueries as follow
SELECT * FROM (SELECT * FROM tests WHERE player_id='9') t9 INNER JOIN (SELECT * FROM tests WHERE player_id='14') t14 ON t9.round_id = t14.round_id

how to optimize my query?

I have 3 tables country_data, user_data and topic_data with table structures as given.
country_data:
name | code
---------------|---------------
India | IN
United States | US
Australia | AU
user_data:
user_ip | topic_code | country
---------------|---------------|---------------
192.168.1.1 | topic_code_1 | India
192.168.1.2 | topic_code_2 | United States
192.168.1.3 | topic_code_3 | Australia
topic_data:
name | code
---------------|---------------
topic_1 | topic_code_1
topic_2 | topic_code_2
topic_3 | topic_code_3
I have about one hundred thousand(100,000) rows in user_data table.
What I want is, I need to filter the count of users from each country with its corresponding country code for a given topic. For example, I need the count of users who viewed topic_2 in each country. The requered output format is
country_code | count
---------------|---------------
IN | 150
US | 120
AU | 100
Now please check my query:
SELECT cd.code, COUNT(ud.country) as count
FROM topic_data as td, user_data as ud, country_data as cd
WHERE td.name = 'topic_1' AND td.code = ud.topic_code AND ud.country = cd.name
GROUP BY ud.country
This one takes about 2 seconds to complete the execution in phpmyadmin. In the php webpage, it takes 15 seconds to load the page even in the server. by removing the group by in the query, ie GROUP BY ud.country, it takes more than 30 seconds to execute and the output is with the last country code and total of all countries visits. what am I doing wrong? please help.
----UPDATE----
Altered the tables using foreign keys and so as my queries too. now it works with lightning speed. thanks for those who helped.
The query doesn't look too bad IMO. However the normalization of the data looks a bit strange, e.g. why would you have a country (name) field on user_data table, just to join into country on name to look up the code? Instead, the more logical thing to me would be to reference country by country code (or other indexed key constraint). This would also save a join to country, if you just need the code as per your example query. If user_data is a high volume table, you will want to keep the data in it to a minimum to reduce IO when reading (density).
Also, as an aside, joining using JOIN instead of in the WHERE clause will improve the readability of your code, IMO:
SELECT cd.code, COUNT(ud.country) as count
FROM topic_data as td
INNER JOIN user_data as ud
ON td.code = ud.topic_code
INNER JOIN country_data as cd
ON ud.country = cd.name
WHERE td.name = 'topic_1'
GROUP BY ud.country;
To address the performance issues, check that the following indexes are in place:
Index on topic_data.name
Index on the foreign keys user_data.topic_code and user_data.country (or user_data.country_code if you do change the foreign key to user_data.country_code)
try this instead:
use below database structure for using numerical matching in INNER JOIN statement may decrease search time,
so index your id column of tables (e.g. primary key):
**country_data**
id|name | code
--|---------------|---------------
1 |India | IN
2 |United States | US
3 |Australia | AU
**user_data**
user_ip | topic_id | county_id
---------------|-----------|---------------
192.168.1.1 | 1 | 1
192.168.1.2 | 2 | 2
192.168.1.3 | 3 | 3
**topic_data**
id|name
--|------------
1 |topic_1
2 |topic_2
3 |topic_3
and run multiple INNER JOIN statment like:
SELECT cd.code, count(ud.topic_code) as count
FROM ud
INNER JOIN cd ON cd.id = ud.country
INNER JOIN td ON td.id = ud.topic_code
WHERE td.code='topic_1'
GROUP BY ud.country;

MySQL Merging my selects together

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

Categories