Overview
I have two tables as can be seen below:
user_planes
----------------------------------
|id |user_id|plane_id|fuel|status|
----------------------------------
| 2 1 1 1 Ready |
----------------------------------
shop_planes
------------------------
|id |name|fuel_capacity|
------------------------
| 1 bob 3 |
------------------------
Foreign Key Primary Key
user_planes.plane_id <-> shop_planes.id
I want to be able to get every field (SELECT *) in user_planes and name and fuel_capacity based on the following criteria:
WHERE user_planes.user_id = ? - Parameter which will be added to the query through PHP.
WHERE user_planes.status = 'Ready'
WHERE user_planes.fuel < shop_planes.fuel_capacity
The Issue and My Attempts
I've tried JOIN however it retrieves data which doesn't fit that criteria, meaning it gets extra data which is from shop_planes and not user_planes.
SELECT * FROM `user_planes` WHERE fuel IN (SELECT shop_planes.fuel_capacity FROM shop_planes WHERE fuel < shop_planes.fuel_capacity) AND user_planes.user_id = 1 AND status = 'Ready'
and
SELECT * FROM `user_planes` INNER JOIN `shop_planes` ON user_planes.fuel < shop_planes.fuel_capacity AND user_planes.user_id = 1 AND user_planes.status = 'Ready'
I've searched Stackoverflow and looked through many questions but I've not been able to figure it.
I've looked up many tutorials but still can't get the desired result.
The desired result is that the query should use the data stored in user_planes to retrieve data from shop_planes while at the same time not getting any excess data from shop_planes.
Disclaimer
I really struggle using JOIN queries, I could use multiple separate queries however I wish to optimise my queries hence I'm trying to bring it in to one query.
If their isn't clarity in the question, please do say, I'll update it to the best of my ability.
Note - Is there an easy query builder option available either through phpmyadmin or an alternative software?
Thanks in advance.
Your last attempt was not a bad one, the only thing you missed there was the join criteria you described at the beginning of your post. I also moved the other filters to the where clause to better distinguish between join condition and the filters.
SELECT `user_planes`.*
FROM `user_planes`
INNER JOIN `shop_planes` ON user_planes.plane_id = shop_planes.id
WHERE user_planes.fuel < shop_planes.fuel_capacity AND user_planes.user_id = 1 AND user_planes.status = 'Ready'
First you need the base JOIN
SELECT up.* -- only user_plane fields
FROM shop_planes sp -- CREATE alias for table or field
JOIN user_planes up
ON sp.id = up.plane_id
Case 1: apply a filter in where condition with php parameter.
SELECT up.*
FROM shop_planes sp
JOIN user_planes up
ON sp.id = up.plane_id
WHERE up.user_id = ?
Case 2: apply a filter in where condition with string constant
SELECT up.*
FROM shop_planes sp
JOIN user_planes up
ON sp.id = up.plane_id
WHERE user_planes.status = 'Ready'
Case 3: aply filter comparing fields from both tables
SELECT up.*
FROM shop_planes sp
JOIN user_planes up
ON sp.id = up.plane_id
WHERE up.fuel < sp.fuel_capacity
Try something like:
SELECT
up.id AS User_Plane_ID
, up.[user_id]
, up.plane_id
, up.fuel
, up.[status]
, sp.name AS shop_Plane_Name
, sp.fuel_capacity AS shop_Plane_Fuel_Capacity
FROM User_Planes up
INNER JOIN Shop_Planes sp ON up.plane_id = sp.id
AND up.fuel < sp.Fuel_Capacity
WHERE up.[status] = 'Ready'
AND up.[user_id] = ?
Definitely find a tutorial for JOINs, and don't use SELECT *. With SELECT *, you may end up querying much more than you actually need and it can cause problems if the table changes. You'll enjoy your day much more if you explicitly name the columns you want in your query.
I've aliased some of the columns (with AS) since some of those column names may be reserved words. I've also moved the JOIN criteria to include a filter on fuel
Related
I want to optimize these SQL queries using if-else but how I should use it? .
if this query result contain 'ALL'
SELECT
bdsubcategory.subcategoryID as ID,
bdsubcategory.subcategoryName as Name
FROM
phonebook.newsms_subscription
INNER JOIN bdsubcategory ON bdsubcategory.subcategoryID = newsms_subscription.subcategoryID
INNER JOIN newsms_client ON newsms_subscription.clientID =newsms_client.clientID
INNER JOIN newsms_person ON newsms_subscription.personID = newsms_person.personID
WHERE
newsms_subscription.isActive = 1 AND
newsms_person.personID = '856'
Then i want to query this
SELECT
bdsubcategory.subcategoryID as ID,
bdsubcategory.subcategoryName as Name
FROM
phonebook.newsms_subscription
INNER JOIN bdsubcategory ON bdsubcategory.subcategoryID = newsms_subscription.subcategoryID
INNER JOIN newsms_person ON newsms_subscription.personID = newsms_person.personID
WHERE
newsms_subscription.isActive = 1
GROUP BY subcategoryName
ORDER BY subcategoryName
otherwise take query1 result .
The problem is that if we do not refactor your project, then you always have to evaluate query1 and see whether it contains All or not. If it does not contain All, then you need to evaluate query2 as well. This can hardly be optimized, let's see a few approaches:
Quickening query1
Since All might be not be the very last evaluated element, adding it to the filter and limiting it is a good idea to quicken query1:
SELECT
COUNT(*)
FROM
phonebook.newsms_subscription
INNER JOIN bdsubcategory ON bdsubcategory.subcategoryID = newsms_subscription.subcategoryID
INNER JOIN newsms_client ON newsms_subscription.clientID =newsms_client.clientID
INNER JOIN newsms_person ON newsms_subscription.personID = newsms_person.personID
WHERE
newsms_subscription.isActive = 1 AND
newsms_person.personID = '856' AND
bdsubcategory.subcategoryName = 'ALL'
LIMIT 0, 1
So, you could create a stored procedure which evaluates query1' (query1' is the quickened version of query1, as seen above) and if there is a result, then we need to execute query1. Otherwise we need to execute query2. This way you still execute two queries, but the first query is optimized.
Refactoring
Note that the second query does not change. You could create a table where you could cache its results, using a periodic job. Then, you could skip the second table to
SELECT ID, Name
FROM MyNewTable;
without the many joins. You would also cache the results of the first query into a table where the items having ALL would be stored and query that table.
One option would be to use a CASE.
Change this:
newsms_person.personID = '856'
To this:
'Y' = CASE WHEN UPPER('856') = 'ALL' THEN 'Y'
WHEN newsms_person.personID = '856' THEN 'Y'
ELSE 'N' END
Alternatively, a stored procedure could be used to first validate whether the personID seems valid, then returns the appropriate data.
I have two tables.
Table number 1 (Lab_test): with integer columns named labtest_id (pk) and project_no. Second table (Project) has two columns, first column named project_id (int) and second column project_name (string). Also I have two parameters which are passed to the query.
I have to make a query which will extract labtest_id and proj_num. proj_num have to be obtained in following way: if Lab_test.project_no is not null, take this value, else go to referencing table Project and take project_name.
I tried with bunch of combinations using SQL CASE IF-THEN-ELSE statements, but no luck:
SELECT lab.labtest_id, proj_num,
FROM Lab_test lab
(CASE
WHEN lab.project_no IS NOT NULL THEN lab.project_no
ELSE (SELECT proj.project_name
FROM Project proj
WHERE proj.project_id = lab.project_no)
) AS proj_num
WHERE lab.status = 'DONE' AND lab.user_id = 436
You can use left join and coalesce() to replace the case when expression that you've used to check null
SELECT lab.labtest_id, coalesce(lab.project_no,proj.project_name) as proj_num
FROM Lab_test lab
left join Project proj on proj.p,roject_id = lab.project_no
where lab.status = 'DONE' AND lab.user_id = 436
if Lab_test.project_no is not null, take this value, else go to referencing table Project and take project_name.
I don't believe you. I think you have the logic backwards -- use the referenced table if it is available. Otherwise use the number.
That would suggest:
SELECT l.labtest_id, COALESCE(p.project_name, l.project_no) as proj_num
FROM Lab_test l LEFT JOIN
Project p
ON p.p,roject_id = l.project_no
WHERE l.status = 'DONE' AND l.user_id = 436;
If the project_no is a number, then you might have a type conversion problem. If that is an issue:
SELECT l.labtest_id,
COALESCE(p.project_name, CAST(l.project_no as CHAR)) as proj_num
FROM Lab_test l LEFT JOIN
Project p
ON p.p,roject_id = l.project_no
WHERE l.status = 'DONE' AND l.user_id = 436;
I have DB with two tables:
rmonth and alternatives
The rmonth is an aggregated table of data for each alternative a complete month - if they have any - otherwise the row don't exist in the rmonth table.
Now I want to join them, and this is my code:
SELECT
COALESCE(rmAntal, 0) AS sumMonth, aID, aText, rmUnitID
FROM
alternatives
LEFT JOIN
rmonth ON aID = rmAltID
WHERE aToQuestID = 4418
AND rmMonth = 3
AND rmYear = 2018
AND rmUnitID IN (10603,10960,10496)
GROUP BY aID, rmUnitID
ORDER BY aID ASC
But it doesn't give me the rows not existing in rmonth.
So this scenario gives me the result as I want it - except that it can't handle where the alternative does not exist for that specific unitID in rmonth.
I want them listed with just 0 in sumMonth.
Unfortunately that's where my MySQL-knowledge is limited.
Thanks.
You could add an OR operator, for example
...
WHERE aToQuestID = 4418 AND rmMonth IS NULL OR (
AND rmMonth = 3
AND rmYear = 2018
AND rmUnitID IN (10603,10960,10496)
)
...
This way, you'll get all your alternatives data, even when it's counter part in rmonth is null.
I am running a SQL query to obtain results with the column applicationstatus that aren't S to tell which position is still open. There are 3 application status U=Unsuccessful, O = ongoing and S = successful. This works fine. Below is the code I am running.
SELECT DISTINCT position.position_ID, Title, EmployerName, Industry
FROM position
JOIN application ON position.position_id = application.position_id
JOIN employer ON position.employer_id = employer.employer_id
WHERE applicationstatus != 'S'
The problem I am facing is with the above query code, let's assume position_ID 3212 has received 3 applications, with one successful application; I get 2 results for the mentioned position(Excluding the successful one) like this.
Is there a way to filter it so that if a position already has a successful application, then the rows with the same position ID will be ignored?
Add a NOT EXISTS condition to exclude positions with successful applications:
SELECT DISTINCT
p.position_ID,
Title,
EmployerName,
Industry
FROM position p
JOIN application a
ON p.position_id = a.position_id
JOIN employer e
ON p.employer_id = e.employer_id
WHERE
applicationstatus != 'S'
AND NOT EXISTS(
SELECT 1
FROM application
WHERE
position_id = a.position_id
AND applicationstatus = 'S'
)
Note that I've rewritten your query to use meaningful aliases. You should also do this to improve readability and maintainability of your code.
Here is a brief explanation of what I'm trying to accomplish; my query follows below.
There are 4 tables and 1 view which are relevant for this particular query (sorry the names look messy, but they follow a strict convention that would make sense if you saw the full list):
Performances may have many Performers, and those associations are stored in PPerformer. Fans can have favorites, which are stored in Favorite_Performer. The _UpcomingPerformances view contains all the information needed to display a user-friendly list of upcoming performances.
My goal is to select all the data from _UpcomingPerformances, then include one additional column that specifies whether the given Performance has a Performer which the Fan added as their favorite. This involves selecting the list of Performers associated with the Performance, and also the list of Performers who are in Favorite_Performer for that Fan, and intersecting the two arrays to determine if anything is in common.
When I execute the below query, I get the error #1054 - Unknown column 'up.pID' in 'where clause'. I suspect it's somehow related to a misuse of Correlated Subqueries but as far as I can tell what I'm doing should work. It works when I replace up.pID (in the WHERE clause of t2) with a hard-coded number, and yes, pID is an existing column of _UpcomingPerformances.
Thanks for any help you can provide.
SELECT
up.*,
CASE
WHEN EXISTS (
SELECT * FROM (
SELECT RID FROM Favorite_Performer
WHERE FanID = 107
) t1
INNER JOIN
(
SELECT r.ID as RID
FROM PPerformer pr
JOIN Performer r ON r.ID = pr.Performer_ID
WHERE pr.Performance_ID = up.pID
) t2
ON t1.RID = t2.RID
)
THEN "yes"
ELSE "no"
END as pText
FROM
_UpcomingPerformances up
The problem is scope related. The nested Selects make the up table invisible inside the internal select. Try this:
SELECT
up.*,
CASE
WHEN EXISTS (
SELECT *
FROM Favorite_Performer fp
JOIN Performer r ON fp.RID = r.ID
JOIN PPerformer pr ON r.ID = pr.Performer_ID
WHERE fp.FanID = 107
AND pr.Performance_ID = up.pID
)
THEN 'yes'
ELSE 'no'
END as pText
FROM
_UpcomingPerformances up