mySQL - How to use a COUNT() statement in a JOIN - mysql

In a mySQL DB there is a table named clients. Each client row has a column client_num. To list all clients I simply use this statement.
SELECT * FROM clients
Now each client may have or may have not some numbers of subclients which is listed in a seperate table named subclients and referenced through the foreign key client_num in the table.
To find out how many subclient a client may have I can query the subclient table in a seperate call like this: Lets say we have a client with the client_num 254.
SELECT count(1) FROM subclients WEHRE sublicents.client_num = 254
So the query returns something like 0, 1, 2, 3 , n
In a JOIN I assume I have to do something like this :
SELECT
c.*,
sc.?????
FROM clients AS c
LEFT JOIN subclients AS sc ON sc.client_num = 254
But I do not know how to incorporate the count() statement in the JOIN for the amount of subclients. Also I do not know if LEFT JOIN is correct.
Any help is appreciated
EDIT :
I must add that I expect all rows to be returned from clients. The additional returned column COUNT(sc.client_num) carries than the amount of subclients. In the Answer 2 below I get only those clients returned which have subclients. Clients which do not have subclients are not returned. How to fix that ?

You can use the following query to get the count of all sub-clients for each client:
SELECT
c.*,
COUNT(sc.client_num)
FROM clients AS c
LEFT JOIN subclients AS sc ON sc.client_num = c.client_num
WHERE c.client_num = 254
GROUP BY sc.client_sum
solution using a sub-select:
SELECT *, (SELECT COUNT(*) FROM subclients WHERE client_num = clients.client_num)
FROM clients
WHERE client_num = 254
demo: http://sqlfiddle.com/#!9/4930a/8/1

The solution I read from a similar query on Stack Overflow was to use a sub-select. If your only purpose of the join is to Count I would suggest that the join is unneeded. So that:
SELECT clients.*, (SELECT COUNT(*) FROM subclients
WHERE subclients.client_num = clients.id ) AS numb FROM clients WHERE ....
And then for a row of clients, they will have a column numb which will be number of subclients that that client has.
A reference.

Related

How to Merge Two Select Query in Mysql

I have below table structure in my users table where user and driver are saved with a unique id and separated with user_type column. user ratings are being saved in rider_avg_ratings column and driver ratings are being saved in driver_avg_ratings column.
when a user submit a request it is being saved in requesttable with both userid and nearby driver id. now I have to return both driver ratings and user ratings from users table in a single query.Problem is when I join request.userid=users.id it is returning rider_avg_ratings to get the driver_avg_ratings i need to join users.id=request.driver_id how can I get both user and driver ratings from a single query
From above two table by joinning request.user_id=users.id I need to return driver_avg_ratings=4.38 and rider_avg_ratings=1.25
SELECT r.user_id as userId, u.rider_avg_ratings as ratings
FROM user as u
INNER JOIN request as r on u.id = r.user_id
UNION
SELECT r1.driver_id as userId, u1.driver_avg_ratings as ratings
FROM user as u1
INNER JOIN request as r1 on u1.id = r1.driver_id
This query will fetch the desired result.
Your union query should produce a The used SELECT statements have a different number of columns error. You need to figure out why your code isn't checking for errors and make it do so. Also, if this is the entire statement, the parentheses around it don't belong.
You need to make the two unioned selects return the same number of parameters. Note that as in the second select's select columns will be ignored and result column names will come from the first select only. Minimally, to make the query run, you would need to add ,NULL,NULL,... to your second select, but it seems likely you want more information to be able to identify which user the driver_avg_ratings is for, like the id, name, etc the first query is returning.
It's often helpful when processing the results to add something indicating which select a row came from, too, e.g. SELECT "pending_request_users" AS type, ... UNION ALL SELECT "all_users_avg_rating", ....
Note that the different SELECTs unioned together return entire rows. If your only goal is to add driver ratings to the rows already returned, you don't want a union, you may just want to add:
SELECT ..., driver.driver_avg_ratings
FROM user u
...
INNER JOIN user AS driver ON driver.id=req.driver_id
(or left join if there may not be a driver_id). But it's hard to tell for sure if that's what you want. Provide sample data and your desired results from it. In any case, do make your code detect errors properly.
for example:
Department
1 A
2 B
3 A
4 C
5 B
6 D
7 E
8 F
You could do something like
SELECT
1 AS Deptnumber
, Dept
FROM tbl_students
WHERE Dept IN ('A', 'B', 'C')
UNION
SELECT
2 AS DeptNumber
, Dept
FROM tbl_students
WHERE Dept IN ('D', 'E')
UNION
SELECT
3 AS Deptnumber
, Dept
FROM tbl_students
WHERE Dept IN ('F')

Best way to structure SQL queries with many inner joins?

I have an SQL query that needs to perform multiple inner joins, as follows:
SELECT DISTINCT adv.Email, adv.Credit, c.credit_id AS creditId, c.creditName AS creditName, a.Ad_id AS adId, a.adName
FROM placementlist pl
INNER JOIN
(SELECT Ad_id, List_id FROM placements) AS p
ON pl.List_id = p.List_id
INNER JOIN
(SELECT Ad_id, Name AS adName, credit_id FROM ad) AS a
ON ...
(few more inner joins)
My question is the following: How can I optimize this query? I was under the impression that, even though the way I currently query the database creates small temporary tables (inner SELECT statements), it would still be advantageous to performing an inner join on the unaltered tables as they could have about 10,000 - 100,000 entries (not millions). However, I was told that this is not the best way to go about it but did not have the opportunity to ask what the recommended approach would be.
What would be the best approach here?
To use derived tables such as
INNER JOIN (SELECT Ad_id, List_id FROM placements) AS p
is not recommendable. Let the dbms find out by itself what values it needs from
INNER JOIN placements AS p
instead of telling it (again) by kinda forcing it to create a view on the table with the two values only. (And using FROM tablename is even much more readable.)
With SQL you mainly say what you want to see, not how this is going to be achieved. (Well, of course this is just a rule of thumb.) So if no other columns except Ad_id and List_id are used from table placements, the dbms will find its best way to handle this. Don't try to make it use your way.
The same is true of the IN clause, by the way, where you often see WHERE col IN (SELECT DISTINCT colx FROM ...) instead of simply WHERE col IN (SELECT colx FROM ...). This does exactly the same, but with DISTINCT you tell the dbms "make your subquery's rows distinct before looking for col". But why would you want to force it to do so? Why not have it use just the method the dbms finds most appropriate?
Back to derived tables: Use them when they really do something, especially aggregations, or when they make your query more readable.
Moreover,
SELECT DISTINCT adv.Email, adv.Credit, ...
doesn't look to good either. Yes, sometimes you need SELECT DISTINCT, but usually you wouldn't. Most often it is just a sign that you haven't thought your query through.
An example: you want to select clients that bought product X. In SQL you would say: where a purchase of X EXISTS for the client. Or: where the client is IN the set of the X purchasers.
select * from clients c where exists
(select * from purchases p where p.clientid = c.clientid and product = 'X');
Or
select * from clients where clientid in
(select clientid from purchases where product = 'X');
You don't say: Give me all combinations of clients and X purchases and then boil that down so I just get each client once.
select distinct c.*
from clients c
join purchases p on p.clientid = c.clientid and product = 'X';
Yes, it is very easy to just join all tables needed and then just list the columns to select and then just put DISTINCT in front. But it makes the query kind of blurry, because you don't write the query as you would word the task. And it can make things difficult when it comes to aggregations. The following query is wrong, because you multiply money earned with the number of money-spent records and vice versa.
select
sum(money_spent.value),
sum(money_earned.value)
from user
join money_spent on money_spent.userid = user.userid
join money_earned on money_earned.userid = user.userid;
And the following may look correct, but is still incorrect (it only works when the values happen to be unique):
select
sum(distinct money_spent.value),
sum(distinct money_earned.value)
from user
join money_spent on money_spent.userid = user.userid
join money_earned on money_earned.userid = user.userid;
Again: You would not say: "I want to combine each purchase with each earning and then ...". You would say: "I want the sum of money spent and the sum of money earned per user". So you are not dealing with single purchases or earnings, but with their sums. As in
select
sum(select value from money_spent where money_spent.userid = user.userid),
sum(select value from money_earned where money_earned.userid = user.userid)
from user;
Or:
select
spent.total,
earned.total
from user
join (select userid, sum(value) as total from money_spent group by userid) spent
on spent.userid = user.userid
join (select userid, sum(value) as total from money_earned group by userid) earned
on earned.userid = user.userid;
So you see, this is where derived tables come into play.

SQL Select Many to Many

I'm using MYSQL 5.X and I'm having trouble figuring out a Query;
I have 2 entities with a many to many relationship.
client and service, therefore I have 3 tables:
clients, clients_has_services and services.
I need to Select EVERY Service that is NOT related to a particular client.
for example:
I have client with ID 1, and 4 Services in Total (Service1, Service2, Service3, Service4), client 1 has a relationship with Service1) so I need to retrieve all Other services (Service2, Service3, Service4)
Suggestions?
First, get a set all services.
Join that to the row from clients for the "particular client", so you have a set of all services for the client.
The "trick" is to use an anti-join pattern to exclude rows where you have "matches" in the clients_have_services table.
If you have the unique identifier for the client (and you only need the list for a single client), something like this:
SELECT s.*
FROM services s
LEFT
JOIN clients_have_services h
ON h.service_id = s.id
AND h.client_id = 42
WHERE h.service_id IS NULL
ORDER BY s.id
The outer join returns all rows from services, along with any "matching" rows from the clients_have_services table. The "trick" is the predicate in the WHERE clause that excludes any rows where a match was found, leaving just the services that are not related to the particular client.
If you are doing this for multiple clients... you'd need to also return the client cross joined with services (as cross product of clients and services), and then exclude the matches.
For example:
SELECT c.id AS client_id
, s.*
FROM clients c
CROSS
JOIN services s
LEFT
JOIN clients_have_services h
ON h.service_id = s.id
AND h.client_id = c.id
WHERE h.service_id IS NULL
ORDER BY c.id, s.id
There are a couple of other query patterns that will return an equivalent result, for example, a NOT EXISTS
SELECT s.*
FROM services s
WHERE NOT EXISTS
( SELECT 1
FROM clients_have_services h
WHERE c.client_id = 42
AND h.service_id = s.id
)
ORDER BY s.id
Assuming that your client_has_services rectifies the many-to-many so you have one to many on both sides, so your client_has_services should have:
ID (Key), Client_ID, Service_ID
SELECT * FROM services WHERE services.id != ANY (select client_has_services.services_id from client_has_services where client_has_services.client_id = ID_num_provided)

Select Query in MySQL for multiple tables

I m working in MySQL and have to write a select query.
I have related data in four tables. Now, the parent table may have data whose child data may not be present in lower tables.
I want to write a single query to get data from all four tables, irrespective of situation that data is present in child tables or not.
I have tried to write nested select and joins as below, but m not getting the data.
select * from property p where p.Re_ID in
(select Re_id from entry e where e.Re_ID in
(select Re_id from category c where c.Re_ID in
(select id from re)))
Please help me how to get data from all 4 tables.
If cir_registry is the master, you can select from that and LEFT JOIN the other tables in the order they're distant from cir_registry;
SELECT r.ID rId, r.Description rDescription, c.ID cId ...
...
p.Data_Type pDataType
FROM cir_registry r
LEFT JOIN cir_category c ON c.Registry_ID = r.ID
LEFT JOIN cir_entry e ON e.Category_ID = c.ID
LEFT JOIN cir_property p ON p.Entry_IDInSource = e.IDInSource
You should also alias the columns as above, otherwise, for example, p.ID and c.ID will both show up in the result set as ID, and you'll only be able to access one of them.
You might need UNION? Something like:
Select * FROM cir_registry
Union
Select * FROM cir_entry
Union
Select * FROM cir_property
Check the official doc here: http://dev.mysql.com/doc/refman/5.0/en/union.html

More Efficient Version of this Correlated Subquery

I've got 3 different tables and I need to pull data once from 2 of them, and twice from the third. The tables are jobs, customers, and customers_attributes. I'm trying to pull data for a specific job, and part of that data is information about the customer who owns the job. Customer data is stored in customers_attributes where the type of data is defined as an integer that corresponds with a type(using strings here for simplicity's sake) and then a content field contains the data itself.
In this case, I need to pull 2 rows from the customers_attributes table that correspond to the customer that corresponds to the job. One row for 'PhoneNumber', and another row for 'CustomerInfo'. I used an INNER JOIN for one of them, but because I can't put WHERE values for both, I used a subquery for the other one. I think this is really nasty and I'm sure there's got to be a cleaner way of doing it:
SELECT jobs.*, customers.Name AS CustomerName,
customers_attributes.Content AS PhoneNumber,
( SELECT `Content`
FROM customers_attributes
WHERE Type = 'CustomerInfo' AND ForeignCustomer = jobs.Customer
LIMIT 1) AS CustomerInfo
FROM jobs
INNER JOIN customers ON jobs.Customer = customers.ID
INNER JOIN customers_attributes ON jobs.Customer = customers_attributes.ForeignCustomer
WHERE jobs.ID = $jobID AND customers_attributes.Type = 'PhoneNumber'
LIMIT 1
I should mention that a customer could have multiple rows for the same attribute if they have more than 1 job, and this query ideally should either return the latest information, or the information that was submitted at the same time as the job(based on corresponding ID orders).
Just join the same table again under a different alias.
SELECT j.*, c.Name AS CustomerName,
ca.Content AS PhoneNumber,
ca2.Content as CustomerInfo
FROM jobs j
INNER JOIN customers c ON j.Customer = c.ID
INNER JOIN customers_attributes ca ON (j.Customer = ca.ForeignCustomer)
INNER JOIN customers_attributes ca2 ON (j.Customer = ca2.ForeignCustomer)
WHERE j.ID = '$jobID'
AND ca.Type = 'PhoneNumber'
AND ca2.Type = 'CustomerInfo'
LIMIT 1
Warning
It looks like you're using PHP. If you must insist on using the outdated mysql_ library and not the much improved mysqli_ lib.
Please remember to use mysql_real_escape_string() and to quote your $vars.
If not you'll be hid by SQL-injection problems.