Search one table and use result to search another table - mysql

I want to make a search on one table that returns a value to be used in search on different table.
I have this code, which looks for a team code in the club table:
SELECT Team, Teamcode
FROM epl.club
WHERE Teamcode =
(SELECT Teamcode
FROM epl.club
WHERE Team='Manchester City');
Now I want to use the resulting Teamcode for a select on the matches table.
I have this code that searches the matches table and finds all the matches with a given team code but I need it to get the code from the first search above.
Select *
from epl.matches
where HomeTeam = 35
or AwayTeam = 35
and FTR like "A"
or FTR like "H";
Another thing I don't understand is that I want to make that it would just return the line only if HomeTeam=35'= and '=FTR is A or FTR is H or if AwayTeam=35 and FTR is A or FTR is H, but what the code does is that it returns all the lines even if they contain the 35 but only contain the H or A in the FTR column.

You have to use parentheses in your boolean expression:
SELECT *
FROM epl.matches
WHERE (HomeTeam = 35 or AwayTeam = 35)
AND (FTR like "A" or FTR like "H")
This is because AND has a higher operator precedence as OR.
You can combine the queries with a join:
SELECT Team, Teamcode FROM epl.club c
INNER JOIN epl.matches m ON (m.HomeTeam = c.Teamcode or m.AwayTeam = c.Teamcode)
WHERE (c.Team = 'Manchester City')
AND (m.FTR like "A" or m.FTR like "H")
Additional info:
Here is a very simple explanation how a 'INNER JOIN' can be understood - just if you don't know this already. If you have two tables:
{ Table: Club }----------------| { Table: Matches }----------|
| | | |
| Teamcode | Team | | HomeTeam | AwayTeam | FTR |
|----------+-------------------| |----------+----------+-----|
| 35 | Manchester City | | 38 | 39 | A |
| 38 | Arsenal London | | 38 | 35 | A |
| 39 | Leeds United | | 35 | 39 | H |
|----------+-------------------| | 38 | 35 | A |
| 39 | 38 | H |
|----------+----------+-----|
an INNER JOIN between the tables club and matches means that of all row combinations of the two tables only the rows are included in the result if the join condition m.HomeTeam = c.Teamcode or m.AwayTeam = c.Teamcode is met. If you restrict club.Team to 'Manchester City' you would have the following result for the join:
{ Table: Join Result }------|
| |
| HomeTeam | AwayTeam | FTR |
|----------+----------+-----|
| 38 | 35 | A |
| 35 | 39 | H |
| 38 | 35 | A |
|----------+----------+-----|
It takes some time to get used to declarative style of the join syntax but it helps you to structure your queries (opposed to multiple FROM tables and nested SELECT subqueries). Furthermore, the SQL query optimizer can handle an INNER JOIN better then nested subqueries in most cases.

First query could just be:
SELECT
Team,
Teamcode
FROM epl.club
WHERE Team='Manchester City';
Why a subquery on the same table when you can access directly the Team field?
Then you can do:
SELECT *
FROM epl.matches
WHERE HomeTeam = (SELECT Teamcode FROM epl.club WHERE Team='Manchester City')
OR AwayTeam = (SELECT Teamcode FROM epl.club WHERE Team='Manchester City')
AND FTR like "A"
OR FTR like "H";

Related

MySQL GROUP_CONCAT with SUM() and multiple JOINs inside subquery

I'm very average with MySQL, but usually I can write all the needed queries after reading documentation and searching for examples. Now, I'm in the situation where I spent 3 days re-searching and re-writing queries, but I can't get it to work the exact way I need. Here's the deal:
1st table (mpt_companies) contains companies:
| company_id | company_title |
------------------------------
| 1 | Company A |
| 2 | Company B |
2nd table (mpt_payment_methods) contains payment methods:
| payment_method_id | payment_method_title |
--------------------------------------------
| 1 | Cash |
| 2 | PayPal |
| 3 | Wire |
3rd table (mpt_payments) contains payments for each company:
| payment_id | company_id | payment_method_id | payment_amount |
----------------------------------------------------------------
| 1 | 1 | 1 | 10.00 |
| 2 | 2 | 3 | 15.00 |
| 3 | 1 | 1 | 20.00 |
| 4 | 1 | 2 | 10.00 |
I need to list each company along with many stats. One of stats is the sum of payments in each payment method. In other words, the result should be:
| company_id | company_title | payment_data |
--------------------------------------------------------
| 1 | Company A | Cash:30.00,PayPal:10.00 |
| 2 | Company B | Wire:15.00 |
Obviously, I need to:
Select all the companies;
Join payments for each company;
Join payment methods for each payment;
Calculate sum of payments in each method;
GROUP_CONCAT payment methods and sums;
Unfortunately, SUM() doesn't work with GROUP_CONCAT. Some solutions I found on this site suggest using CONCAT, but that doesn't produce the list I need. Other solutions suggest using CAST(), but maybe I do something wrong because it doesn't work too. This is the closest query I wrote, which returns each company, and unique list of payment methods used by each company, but doesn't return the sum of payments:
SELECT *,
(some other sub-queries I need...),
(SELECT GROUP_CONCAT(DISTINCT(mpt_payment_methods.payment_method_title))
FROM mpt_payments
JOIN mpt_payment_methods
ON mpt_payments.payment_method_id=mpt_payment_methods.payment_method_id
WHERE mpt_payments.company_id=mpt_companies.company_id
ORDER BY mpt_payment_methods.payment_method_title) AS payment_data
FROM mpt_companies
Then I tried:
SELECT *,
(some other sub-queries I need...),
(SELECT GROUP_CONCAT(DISTINCT(mpt_payment_methods.payment_method_title), ':', CAST(SUM(mpt_payments.payment_amount) AS CHAR))
FROM mpt_payments
JOIN mpt_payment_methods
ON mpt_payments.payment_method_id=mpt_payment_methods.payment_method_id
WHERE mpt_payments.company_id=mpt_companies.company_id
ORDER BY mpt_payment_methods.payment_method_title) AS payment_data
FROM mpt_companies
...and many other variations, but all of them either returned query errors, either didn't return/format data I need.
The closest answer I could find was MySQL one to many relationship: GROUP_CONCAT or JOIN or both? but after spending 2 hours re-writing the provided query to work with my data, I couldn't do it.
Could anyone give me a suggestion, please?
You can do that by aggregating twice. First for the sum of payments per method and company and then to concatenate the sums for each company.
SELECT x.company_id,
x.company_title,
group_concat(payment_amount_and_method) payment_data
FROM (SELECT c.company_id,
c.company_title,
concat(pm.payment_method_title, ':', sum(p.payment_amount)) payment_amount_and_method
FROM mpt_companies c
INNER JOIN mpt_payments p
ON p.company_id = c.company_id
INNER JOIN mpt_payment_methods pm
ON pm.payment_method_id = p.payment_method_id
GROUP BY c.company_id,
c.company_title,
pm.payment_method_id,
pm.payment_method_title) x
GROUP BY x.company_id,
x.company_title;
db<>fiddle
Here you go
SELECT company_id,
company_title,
GROUP_CONCAT(
CONCAT(payment_method_title, ':', payment_amount)
) AS payment_data
FROM (
SELECT c.company_id, c.company_title, pm.payment_method_id, pm.payment_method_title, SUM(p.payment_amount) AS payment_amount
FROM mpt_payments p
JOIN mpt_companies c ON p.company_id = c.company_id
JOIN mpt_payment_methods pm ON pm.payment_method_id = p.payment_method_id
GROUP BY p.company_id, p.payment_method_id
) distinct_company_payments
GROUP BY distinct_company_payments.company_id
;

Count the filtered data from multiple tables in SQL

I have 2 tables config_location_workstation and asset_workstation where both have the columns floor and workstation_number.
I want a query to return something like this :
FLOOR| SUM PRODUCTION WORKSTATION | SUM HEAD COUNT
This code below came from another source which I was able to pull up the sum but the numbers are incorrect because what I want to get is the number of all Production workstation.
SELECT config_location_workstation1.floor,
(SELECT COUNT(config_location_workstation2.workstation_number)
FROM config_location_workstation AS config_location_workstation2
WHERE config_location_workstation2.floor = config_location_workstation1.floor) AS SUM_FLOOR,
(SELECT COUNT(asset_workstation2.workstation_number)
FROM asset_workstation AS asset_workstation2
WHERE asset_workstation2.floor = config_location_workstation1.floor) AS SUM_HEAD
FROM config_location_workstation AS config_location_workstation1
INNER JOIN asset_workstation AS asset_workstation1
ON (config_location_workstation1.workstation_number = asset_workstation1.workstation_number)
WHERE config_location_workstation1.workstation_name = 'NORTH PRODUCTION'
GROUP BY config_location_workstation1.floor
The problem is WHERE is not working on this code.
The Workstation Column sum is invalid. It is pulling up all the entries. I only need to query all PRODUCTION workstations
Here's the current output.
+-------+-------------+------+------+
| Floor | Head Count | Workstations|
+-------+-------------+------+------+
| 18TH | 696 | 576 |
| 19TH | 381 | 463 |
| 20TH | 380 | 760 |
+-------+-------------+------+------+
Expected output for all Production workstations
+-------+-------------+------+------+
| Floor | Head Count | Workstations|
+-------+-------------+------+------+
| 18TH | 696 | 497 |
| 19TH | 381 | 388 |
| 20TH | 380 | 659 |
+-------+-------------+------+------+
Probably you could try using a subquery
select * from (
SELECT config_location_workstation1.floor,config_location_workstation1.workstation_name as z,
(SELECT COUNT(config_location_workstation2.workstation_number)
FROM config_location_workstation AS config_location_workstation2
WHERE config_location_workstation2.floor = config_location_workstation1.floor) AS SUM_FLOOR,
(SELECT COUNT(asset_workstation2.workstation_number)
FROM asset_workstation AS asset_workstation2
WHERE asset_workstation2.floor = config_location_workstation1.floor) AS SUM_HEAD
FROM config_location_workstation AS config_location_workstation1
INNER JOIN asset_workstation AS asset_workstation1
ON (config_location_workstation1.workstation_number = asset_workstation1.workstation_number)
GROUP BY config_location_workstation1.floor
) x
WHERE z = 'NORTH PRODUCTION'
UPDATE:
select x.*,y.* from ( SELECT *, COUNT(workstation_name) as COUNT_FLOOR
FROM config_location_workstation WHERE workstation_name LIKE '%PRODUCTION%' GROUP BY floor)x
join
(SELECT *, COUNT(floor) as COUNT_USERS FROM asset_workstation GROUP BY floor) y on
x.workstation_number = y.workstation_number

Mysql join query return duplicate row

SELECT m.*
, p.image_url
, r.acceptance_status
from playermessage m
join playerprofile p
on p.player_id = m.sender_id
join requesttempstorage r
on r.requester_id = m.sender_id
where m.player_id = 48
This query is acting strange it gives me back two duplicate rows back but when I check the table playermessage there are no duplicate rows only this query would show the same message twice to a user while there is only one message can anybody spot the mistake.
| player_id | player_message | date_sent | sender_id | image_url | acceptance_status |<br>
+-----------+---------------------------------------------------------+---------<br>------------+-----------+--------------------+-------------------+<br>
| 48 | imran wants to be a part of the pakistan cricket team | 2018-05-17 18:58:08 | 50 | uploads/imran.jpg | 1 |<br>
| 48 | fakhar wants to be a part of the pakistan cricket team | 2018-05-17 19:13:27 | 51 | uploads/fakhar.jpg | 1 |<br>
| 48 | shadab wants to be a part of the pakistan cricket team | 2018-05-18 11:09:49 | 52 | uploads/shadab.jpg | 1 |<br><strong>
| 48 | asif wants to be a part of the pakistan cricket team | 2018-05-18 11:20:51 | 53 | uploads/asif.jpeg | 0 </strong>|<br>
<strong>| 48 | asif wants to be a part of the pakistan cricket team | 2018-05-18 11:20:51 | 53 | uploads/asif.jpeg | 0 |</strong><br>
+-----------+---------------------------------------------------------+---------------------+-----------+-----------------
The problem lies in the last two results(eg. ), It's returning two message when just all another message there is only one why is it doing it to last two results.
One of these two queries is going to return two rows:
SELECT p.* FROM playerprofile p WHERE p.player_id = 53
or
SELECT r.* FROM requesttempstorage r WHERE r.requester_id = 53
The JOIN operation is finding all matching rows, and returning all of them.
If we have a row in playermessage with sender_id = 53, and
if there are two rows in playerprofile with player_id = 53, then we expect two rows to be returned.
If we had three rows in playerprofile with player_id = 53, then the join operation would return three rows.
If we have zero rows in playerprofile with player_id = 53, then we won't get any rows returned from playermessage with sender_id = 53.
If we also have two rows in requesttempstorage with requester_id = 53, that will also double the number of rows returned.
All of the columns from playermessage will be duplicated on each of those rows.
That's exactly how an inner join operation is designed to operate.

Comparing between several fields and a table

Say I've got the next to tables: Drugs, and Clients:
idNum | drugName
118 | drug1
118 | drug2
120 | drug1
120 | drug2
120 | drug3
121 | drug1
121 | drug3
122 | drug2
Clients:
idNum | Name | lastName |
118 | name1 | last1 |
119 | name2 | last2 |
120 | name3 | last3 |
121 | name4 | last4 |
122 | name5 | last5 |
I want to create a new table containing all id's, Names and last names of clients who took the same drugs as client with the idNum='118'
Which means i need to get a table with only '120'.
So far i've got:
SELECT Clients.idNum,firstName, lastName
FROM Clients, Drugs
WHERE Drugs.idNum=Clients.idNum
AND Clients.idNum<>'118'
AND Clients.idNum IN
(Select idNum From Drugs Where drugName IN
(Select drugName from Drugs where idNum='118'))
But this gives me also 121 and 122.
I guess 'IN' is something like 'Exists', So if someone has only 1 of the drugs the condition is enough. How do i actually compare between a table which i get from here:
(Select drugName from Drugs where idNum='118')
to a field in 'Drugs'?
Or maybe, How do i create a table for each of the customers and then compare it with the table?
This is a tricky query. The idea is to generate the list of drugs for each client that matches client 118. This is the "master" list. Then, use left outer join to bring in the actual drugs for each client. When a match fails, then filter out the client:
select master.idNum, master.firstName, master.lastName
from (select d.drugName, c.*
from Drugs d cross join
Clients c
where d.idnum = 118
) master left outer join
Drugs d
on master.drugName = d.drugName and
master.idNum = d.idNum
group by master.idNum, master.firstName, master.lastName
having count(d.drugName) = count(master.drugName);
The SQL Fiddle is here. If you want to additionally filter out client 118, then change the nested where clause to something like d.idnum = 118 and c.idnum <> 118.
You should also learn proper join syntax and the use of table aliases.

MySql: Multiple Left Join giving wrong output

I'm having a little trouble about using multiple Left Joins in a query. Some of the tables have one-to-one relationship with the left-table and some have one-to-many relation. The query looks like this:
Select
files.filename,
coalesce(count(distinct case
when dm_data.weather like '%clear%' then 1
end),
0) as clear,
coalesce(count(distinct case
when dm_data.weather like '%lightRain%' then 1
end),
0) as lightRain,
coalesce(count(case
when kc_data.type like '%bicycle%' then 1
end),
0) as bicycle,
coalesce(count(case
when kc_data.type like '%bus%' then 1
end),
0) as bus,
coalesce(count(case
when kpo_data.movement like '%walking%' then 1
end),
0) as walking,
coalesce(count(case
when kpo_data.type like '%pedestrian%' then 1
end),
0) as pedestrian
from
files
left join
dm_data ON dm_data.id = files.id
left join
kc_data ON kc_data.id = files.id
left join
kpo_data ON kpo_data.id = files.id
where
files.filename in (X, Y, Z, ........)
group by files.filename;
Here, dm_data table has a one-to-one relation with 'files' table (thats why I'm using 'Distinct'), whereas kc_data and kpo_data data has one-to-many relationship with the 'files' table. (kc_data and kpo_data can have 10 to 20 rows against one files.id). This query works fine.
The problem arises when I add another left join with another one-to-many table pd_markings (which can have 100s of rows against one files.id).
Select
files.filename,
coalesce(count(distinct case
when dm_data.weather like '%clear%' then 1
end),
0) as clear,
coalesce(count(distinct case
when dm_data.weather like '%lightRain%' then 1
end),
0) as lightRain,
coalesce(count(case
when kc_data.type like '%bicycle%' then 1
end),
0) as bicycle,
coalesce(count(case
when kc_data.type like '%bus%' then 1
end),
0) as bus,
coalesce(count(case
when kpo_data.movement like '%walking%' then 1
end),
0) as walking,
coalesce(count(case
when kpo_data.type like '%pedestrian%' then 1
end),
0) as pedestrian,
**coalesce(count(case
when pd_markings.movement like '%walking%' then 1
end),
0) as walking**
from
files
left join
dm_data ON dm_data.id = files.id
left join
kc_data ON kc_data.id = files.id
left join
kpo_data ON kpo_data.id = files.id
left join
**kpo_data ON pd_markings.id = files.id**
where
files.filename in (X, Y, Z, ........)
group by files.filename;
Now all the values become multiple of each other. Any ideas???
Note that the first two columns return 1 or 0 value. Thats the desired result actually, as one-to-one relationship tables will only have either 1 or 0 rows against any files.id, so if I don't use 'Distinct' then the resulting value is wrong (i guess because of the other tables which are returning more then one row against same file.id) No, unfortunately, my tables don't have their own unique ID columns except the 'files' table.
You need to flatten the results of your query, in order to obtain a right count.
You said you have one-to-many relationship from your files table to other table(s)
If SQL only has a keyword LOOKUP instead of cramming everything in JOIN keywords, it shall be easy to infer if the relation between table A and table B is one-to-one, using JOIN will automatically connotes one-to-many. I digress. Anyway, I should have already inferred that your files is one-to-many against dm_data; and also, the files against kc_data is one-to-many too. LEFT JOIN is another hint that the relationship between first table and second table is one-to-many; this is not definitive though, some coders just write everything with LEFT JOIN. There's nothing wrong with your LEFT JOIN in your query, but if there are multiple one-to-many tables in your query, that will surely fail, your query will produce repeating rows against other rows.
from
files
left join
dm_data ON dm_data.id = files.id
left join
kc_data ON kc_data.id = files.id
So with this knowledge that you indicate files is one-to-many against dm_data, and it is one-to-many also against kc_data. We can conclude that there's something wrong with chaining those joins and grouping them on one monolithic query.
An example if you have three tables, namely app(files), ios_app(dm_data), android_app(kc_data), and this is the data for example for ios:
test=# select * from ios_app order by app_code, date_released;
ios_app_id | app_code | date_released | price
------------+----------+---------------+--------
1 | AB | 2010-01-01 | 1.0000
3 | AB | 2010-01-03 | 3.0000
4 | AB | 2010-01-04 | 4.0000
2 | TR | 2010-01-02 | 2.0000
5 | TR | 2010-01-05 | 5.0000
(5 rows)
And this is the data for your android:
test=# select * from android_app order by app_code, date_released;
.android_app_id | app_code | date_released | price
----------------+----------+---------------+---------
1 | AB | 2010-01-06 | 6.0000
2 | AB | 2010-01-07 | 7.0000
7 | MK | 2010-01-07 | 7.0000
3 | TR | 2010-01-08 | 8.0000
4 | TR | 2010-01-09 | 9.0000
5 | TR | 2010-01-10 | 10.0000
6 | TR | 2010-01-11 | 11.0000
(7 rows)
If you merely use this query:
select x.app_code,
count(i.date_released) as ios_release_count,
count(a.date_released) as android_release_count
from app x
left join ios_app i on i.app_code = x.app_code
left join android_app a on a.app_code = x.app_code
group by x.app_code
order by x.app_code
The output will be wrong instead:
app_code | ios_release_count | android_release_count
----------+-------------------+-----------------------
AB | 6 | 6
MK | 0 | 1
PM | 0 | 0
TR | 8 | 8
(4 rows)
You can think of chained joins as cartesian product, so if you have 3 rows on first table, and has 2 rows on second table, the output will be 6
Here's the visualization, see that there is 2 repeating android AB for every ios AB. There are 3 ios AB, so what would be the count when you do COUNT(ios_app.date_released)? That will become 6; the same with COUNT(android_app.date_released), this will also be 6. Likewise there's 4 repeating android TR for every ios TR, there are are 2 TR in ios, so that would give us a count of 8.
.app_code | ios_release_date | android_release_date
----------+------------------+----------------------
AB | 2010-01-01 | 2010-01-06
AB | 2010-01-01 | 2010-01-07
AB | 2010-01-03 | 2010-01-06
AB | 2010-01-03 | 2010-01-07
AB | 2010-01-04 | 2010-01-06
AB | 2010-01-04 | 2010-01-07
MK | | 2010-01-07
PM | |
TR | 2010-01-02 | 2010-01-08
TR | 2010-01-02 | 2010-01-09
TR | 2010-01-02 | 2010-01-10
TR | 2010-01-02 | 2010-01-11
TR | 2010-01-05 | 2010-01-08
TR | 2010-01-05 | 2010-01-09
TR | 2010-01-05 | 2010-01-10
TR | 2010-01-05 | 2010-01-11
(16 rows)
So what you should do is flatten each result before you join them to other tables and queries.
If your database is capable of CTE, please use so. It's very neat and very self-documenting:
with ios_app_release_count_list as
(
select app_code, count(date_released) as ios_release_count
from ios_app
group by app_code
)
,android_release_count_list as
(
select app_code, count(date_released) as android_release_count
from android_app
group by app_code
)
select
x.app_code,
coalesce(i.ios_release_count,0) as ios_release_count,
coalesce(a.android_release_count,0) as android_release_count
from app x
left join ios_app_release_count_list i on i.app_code = x.app_code
left join android_release_count_list a on a.app_code = x.app_code
order by x.app_code;
Whereas if your database has no CTE capability yet, like MySQL, you should do this instead:
select x.app_code,
coalesce(i.ios_release_count,0) as ios_release_count,
coalesce(a.android_release_count,0) as android_release_count
from app x
left join
(
select app_code, count(date_released) as ios_release_count
from ios_app
group by app_code
) i on i.app_code = x.app_code
left join
(
select app_code, count(date_released) as android_release_count
from android_app
group by app_code
) a on a.app_code = x.app_code
order by x.app_code
That query and the CTE-style query will show the correct output:
app_code | ios_release_count | android_release_count
----------+-------------------+-----------------------
AB | 3 | 2
MK | 0 | 1
PM | 0 | 0
TR | 2 | 4
(4 rows)
Live test
Incorrect query: http://www.sqlfiddle.com/#!2/9774a/2
Correct query: http://www.sqlfiddle.com/#!2/9774a/1
I question your distinct usage here - the way it is written it will return 1 or 0. Which means a count distinct will only ever return 0, 1 or 2.
I assume you have unique ID columns in each of your tables. You can change the case to return the ID value, then count distinct that. If your join returns multiple of the same row from your pd_markings table, a distinct count on the ID will return, well, only the distinct count of rows.