Using results from a query within the same query - mysql

I have a rather big SQL statement that I am working with in MS Access 2010. Here it goes:
SELECT
W.ID AS wid,
W.wpt_ty AS ty,
W.wpt_num AS num,
W.wpt_nxt AS nxt,
W.latdeg AS lat,
W.londeg AS lon,
W.alt AS alt,
W.mission_id AS mid,
W.ctg1 AS ctg1,
W.ctg2 AS ctg2,
W.ctg3 AS ctg3,
W.ctg4 AS ctg4,
W.wpt_index AS indx,
W.vel AS vel,
W.tu AS tu,
R.route_num AS rnum,
R.AC_num AS ac,
R.route_type AS rtype,
R.LastUpdatedOn AS d8,
R.LastUpdatedBy AS auth,
R.flight_wpt_count AS wfcount,
M.mission_name AS msnName,
V.Description AS vstatus,
R.disallowed_reason_id AS did,
CW.wpt_num AS c1num,
CR.matching_route_id AS c1mrid,
CW.wpt_index AS c1indx,
CRU.runway_name AS c1rnwy,
CR.route_num AS c1rnum
FROM Validation AS V
(RIGHT JOIN Runways AS CRU
INNER JOIN (Routes CR
INNER JOIN Waypoints CW ON CR.ID = CW.route_id)
ON Runways.ID = Routes.runway_id
INNER JOIN ((Missions as M
INNER JOIN Routes AS R ON M.ID = R.mission_id)
INNER JOIN Waypoints AS W ON (R.ID = W.route_id)
AND (M.ID = W.mission_id)) ON
V.ID = R.validated
WHERE (((R.matching_route_id)=307543) AND ((R.validated) <> 0 ))
AND (((CW.mission_id)=mid) AND ((CW.wpt_num) = (ctg1))))
If you look at the bottom, you can see am I referencing the values ctg1 and mid on a Right Join while the Inner Joins reference other literal values. Eventually I will want to do the same for ctg2, ctg3, and ctg4
Right now I am running these as 2 separate queries but finding it to be way too slow. If I can join combine the queries (sort of like how I am showing here) it could speed things up greatly. But I am at a loss for how to:
Using select values earlier in said query from the Inner/Left join and push them into values needed on the Right join.
I may be using joins incorrectly, but I thought they had to do with combining data from possible the same tables, just on different pivot points.
How to use the MS Access GUI to help write a query like this.
I know this is for MS Access but I am tagging for MySQL just in case there are similar queries there which can be ported to MS Access?

Have you tried using UNION for this?
It would allow you to execute this query (As two queries, which you mentioned as a possibility), and join the results for your output.
Be warned, it will eat up (only show one of) your duplicates in the results set.
I also suggest reading up on the different types of joins for your own benefit, in the following answer:
MYSQL Joins

Related

Using an alias to form an inner join

I have a problem which I cant figure out, and have looked on google and similar questions on here, but they are just not quite the same.
I am trying to build a MySQL Query which has two parts, the first is easy and i have done this fine, as it uses existing relationships, see...
SELECT
clientsites.SiteName,
clients.ClientName,
pafaddresses.PostTown,
pafaddresses.PostCode,
CONCAT("XXXXXXX", Replace(UPPER(pafaddresses.PostCode),' ','')) AS JouneyKeytemp,
clientsites.SiteType
FROM clientsites
INNER JOIN clients ON clientsites.ClientFk = clients.ClientPk
INNER JOIN pafaddresses ON clients.ActualPAF = pafaddresses.id
You will see from this code that an alias is generated which concats two postcodes and looks like xxxxxxxyyyyyy, this does work but for obvious reasons ive removed the actual postcodes.
What I now what to do is to bring in two fields from an unrelated table called Journeys:
SELECT
JourneyKey,
SingleDistance,
SingleTime
FROM journeys
I want to bring in SingleDistance and SingleTime, where the Journey Key = Generated Alias of JourneyKeyTemp.
I have tried adding the following:
INNER JOIN journeys ON JouneyKeytemp = journeys.JourneyKey
But I just keep getting a syntax error.
Any help would be appreciated.
Repeat the expression in join predicate:
INNER JOIN journeys
ON CONCAT("XXXXXXX", Replace(UPPER(pafaddresses.PostCode),' ','')) = journeys.JourneyKey
Or you can create a subquery:
select * from(
SELECT
clientsites.SiteName,
clients.ClientName,
pafaddresses.PostTown,
pafaddresses.PostCode,
CONCAT("XXXXXXX", Replace(UPPER(pafaddresses.PostCode),' ','')) AS JouneyKeytemp,
clientsites.SiteType
FROM clientsites
INNER JOIN clients ON clientsites.ClientFk = clients.ClientPk
INNER JOIN pafaddresses ON clients.ActualPAF = pafaddresses.id)t
INNER JOIN journeys ON t.JouneyKeytemp = journeys.JourneyKey

Why can't I exclude this row based on a condition?

http://sqlfiddle.com/#!3/3ec1f/119
Here's my fiddle...I want the result to look like this but the query I'm using doesn't do that:
My problem with the query is that I can't seem to exclude "The Kingdom of the Crystal Skull" using the exclusion_flag condition. I also don't know why it seems that Contract 3 (Raiders of the Lost Arc) is not showing up either. I have been toiling with this for hours and have no idea what the problem is. I tried looking into subqueries, but I'm not sure that's the solution...
There's a couple of questions/issues there so I'll try to address them individually.
1) You can't exclude "The Kingdom of the Crystal Skull" using the exclusion_flag because contract_sid 7 and 8 both refer to product_list_sid 3 which includes "The Kingdom of the Crystal Skull" - you would need to create a separate product_list_sid if you wanted a contract which excluded it.
2) "Raiders of the Lost Arc" (contract_sid 3) isn't showing up because it's a "single product" contract, and your query only joins from scope to product_list_join using product_list_id - contract_sid 3 is in the product_sid column so you need a separate join to cater for contracts that use product_sid instead of product_list_sid (I assume that a contract can't use both). This is a pretty dodgy schema design but here's a query that solves that issue. Notice the use of LEFT OUTER JOIN to indicate that the table being joined to might not contain any rows (for example when scope.product_list_sid is NULL but scope.product_sid is not).
SELECT s.contract_sid,
c.contract_description,
ISNULL(p.product_description, p2.product_description) AS product_description
FROM scope s
JOIN contracts c ON (c.contract_sid = s.contract_sid)
LEFT OUTER JOIN
product_list_join plj ON (plj.product_list_sid = s.product_list_sid)
LEFT OUTER JOIN
products p ON (p.product_sid = plj.product_sid)
LEFT OUTER JOIN
products p2 ON (p2.product_sid = s.product_sid)
WHERE s.exclusion_flag = 'N'
ORDER BY s.contract_sid;
Here's the SQLFiddle for my solution: http://sqlfiddle.com/#!3/fc62e/10
Edit: After posting this I realised what you're actually trying to do - the scope table not only provides the details of contracts but also provides specific products to exclude from contracts. Again, this is bad schema design and there should be a separate scope_exclusions table or something, but here's a query that does that and excludes "The Kingdom of the Crystal Skull" as requested:
SELECT inner_query.contract_description,
inner_query.product_description
FROM (
SELECT s.contract_sid,
c.contract_description,
ISNULL(p.product_sid, p2.product_sid) AS product_sid,
ISNULL(p.product_description, p2.product_description) AS product_description
FROM scope s
JOIN contracts c ON (c.contract_sid = s.contract_sid)
LEFT OUTER JOIN
product_list_join plj ON (plj.product_list_sid = s.product_list_sid)
LEFT OUTER JOIN
products p ON (p.product_sid = plj.product_sid)
LEFT OUTER JOIN
products p2 ON (p2.product_sid = s.product_sid)
WHERE s.exclusion_flag = 'N'
) inner_query
WHERE NOT EXISTS ( SELECT 1
FROM scope
WHERE exclusion_flag = 'Y'
AND contract_sid = inner_query.contract_sid
AND product_sid = inner_query.product_sid )
ORDER BY inner_query.contract_description;
SQL Fiddle: http://sqlfiddle.com/#!3/fc62e/14

MySQL Replacing IN and EXISTS with joins in sub sub queries

So, this query is currently used in a webshop to retrieve technical data about articles.
It has served its purpose fine except the amount of products shown have increased lately resulting in unacceptable long loading times for some categories.
For one of the worst pages this (and some other queries) get requested about 80 times.
I only recently learned that MySQL does not optimize sub-queries that don't have a depending parameter to only run once.
So if someone could help me with one of the queries and explain how you can replace the in's and exists's to joins, i will probably be able to change the other ones myself.
select distinct criteria.cri_id, des_texts.tex_text, article_criteria.acr_value, article_criteria.acr_kv_des_id
from article_criteria, designations, des_texts, criteria, articles
where article_criteria.acr_cri_id = criteria.cri_id
and article_criteria.acr_art_id = articles.art_id
and articles.art_deliverystatus = 1
and criteria.cri_des_id = designations.des_id
and designations.des_lng_id = 9
and designations.des_tex_id = des_texts.tex_id
and criteria.cri_id = 328
and article_criteria.acr_art_id IN (Select distinct link_art.la_art_id
from link_art, link_la_typ
where link_art.la_id = link_la_typ.lat_la_id
and link_la_typ.lat_typ_id = 17484
and link_art.la_ga_id IN (Select distinct link_ga_str.lgs_ga_id
from link_ga_str, search_tree
where link_ga_str.lgs_str_id = search_tree.str_id
and search_tree.str_type = 1
and search_tree.str_id = 10132
and EXISTS (Select *
from link_la_typ
where link_la_typ.lat_typ_id = 17484
and link_ga_str.lgs_ga_id = link_la_typ.lat_ga_id)))
order by article_criteria.acr_value
I think this one is the main badguy with sub-sub-sub-queries
I just noticed i can remove the last exist and still get the same results but with no increase in speed, not part of the question though ;) i'll figure out myself whether i still need that part.
Any help or pointers are appreciated, if i left out some useful information tell me as well.
I think this is equivalent:
SELECT DISTINCT c.cri_id, dt.tex_text, ac.acr_value, ac.acr_kv_des_id
FROM article_criteria AS ac
JOIN criteria AS c ON ac.acr_cri_id = c.cri_id
JOIN articles AS a ON ac.acr_art_id = a.art_id
JOIN designations AS d ON c.cri_des_id = d.des_id
JOIN des_texts AS dt ON dt.tex_id = d.des_tex_id
JOIN (SELECT distinct la.la_art_id
FROM link_art AS la
JOIN link_la_typ AS llt ON la.la_id = llt.lat_la_id
JOIN (SELECT DISTINCT lgs.lgs_ga_id
FROM link_ga_str AS lgs
JOIN search_tree AS st ON lgs.lgs_str_id = st.str_id
JOIN link_la_typ AS llt ON lgs.lgs_ga_id = llt.lat_ga_id
WHERE st.str_type = 1
AND st.str_id = 10132
AND llt.lat_typ_id = 17484) AS lgs
ON la.la_ga_id = lgs.lgs_ga_id
WHERE llt.lat_typ_id = 17484) AS la
ON ac.acr_art_id = la.la_art_id
WHERE a.art_deliverystatus = 1
AND d.des_lng_id = 9
AND c.cri_id = 328
ORDER BY ac.acr_value
All the IN <subquery> clauses can be replaced with JOIN <subquery>, where you then JOIN on the column being tested equaling the column returned by the subquery. And the EXISTS test is converted to a join with the table, moving the comparison in the subquery's WHERE clause into the ON clause of the JOIN.
It's probably possible to flatten the whole thing, instead of joining with subqueries. But I suspect performance will be poor, because this won't reduce the temporary tables using DISTINCT. So you'll get combinatorial explosion in the resulting cross product, which will then have to be reduced at the end with the DISTINCT at the top.
I've converted all the implicit joins to ANSI JOIN clauses, to make the structure clearer, and added table aliases to make things more readable.
In general, you can convert a FROM tab1 WHERE ... val IN (SELECT blah) to a join like this.
FROM tab1
JOIN (
SELECT tab1_id
FROM tab2
JOIN tab3 ON whatever = whatever
WHERE whatever
) AS sub1 ON tab1.id = sub1.tab1_id
The JOIN (an inner join) will drop the rows that don't match the ON condition from your query.
If your tab1_id values can come up duplicate from your inner query, use SELECT DISTINCT. But don't use SELECT DISTINCT unless you need to; it is costly to evaluate.

Convert MS SQL Server Query in MYSQL QUERY

I have written a Query,
SELECT dbo.boat.boatno, dbo.boat.boattype, dbo.staff.staffFirstName, dbo.staff.staffLastName,
dbo.branch.branchAddress
FROM dbo.boat INNER JOIN
dbo.BoatOwner ON dbo.boat.OwnerNo = dbo.BoatOwner.OwnerNo INNER JOIN
dbo.branch ON dbo.boat.BranchNo = dbo.branch.branchno INNER JOIN
dbo.staff ON dbo.branch.branchno = dbo.staff.Branchno
WHERE (dbo.branch.branchAddress LIKE '%LONDON%')
But It doesn't work in MYSQL QUERY
How can i convert this into MYSQL QUERY?
You need to know correct table names for MySQL. Assuming a similar structure, I might try:
SELECT b.boatno, b.boattype, s.staffFirstName, s.staffLastName, br.branchAddress
FROM boat b INNER JOIN
BoatOwner bo
ON b.OwnerNo = bo.OwnerNo INNER JOIN
branch br
ON b.BranchNo = br.branchno INNER JOIN
staff s
ON br.branchno = s.Branchno
WHERE br.branchAddress LIKE '%LONDON%';
MySQL does not use the three-part naming that SQL Server does. There is no "schema" in the middle of the name. The additional periods in the column names are probably one source of confusion. Using table aliases should work in both databases and makes the code more readable.
Just a guess from general principles, but perhaps the simpler
SELECT A.boatno, A.boattype, D.staffFirstName, D.staffLastName, C.branchAddress
FROM dbo.boat A, dbo.BoatOwner B, dbo.branch C, dbo.staff D
WHERE B.OwnerNo = A.OwnerNo AND C.branchno = A.BranchNo AND D.Branchno = C.branchno
AND C.branchAddress LIKE '%LONDON%'
may work.
To begin, make sure also that you can SELECT from dbo.boat, dbo.BoatOwner, dbo.branch and dbo.staff using your PHPmyAdmin environment. Sometimes the simple things trip us up...
SELECT b.boatno, b.boattype, s.staffFirstName, s.staffLastName, br.branchAddress
FROM boat b INNER JOIN
BoatOwner bo
ON b.OwnerNo = bo.OwnerNo INNER JOIN
branch br
ON b.BranchNo = br.branchno INNER JOIN
staff s
ON br.branchno = s.Branchno
WHERE br.branchAddress LIKE '%LONDON%';
GROUP BY b.boatno
Isn't that enought?

The left joins making query slow,is there any method to increase the speed of this query

select
b.entry_id,
b.assign_id,
a.profile_type,
a.profile_id,
a.profile_name,
a.profile_status,
b.entry_type,
b.assign_id,
c.chapter_name,
d.section_name,
h.group_name,
i.programme_name,
k.subjectprogramme_name,
j.masterprogramme_name,
l.developmentprogramme_name
from profile_master a
left join profile_assign b on (a.profile_id = b.profile_id)
left join chapter_master c
on (b.entry_id = c.chapter_id and b.entry_type='chapter')
left join section_master d
on (b.entry_id = d.section_id and b.entry_type='section')
left join group_master h
on (b.entry_id = h.group_id and b.entry_type='Group'
and h.year_id='".$this->year."')
left join programme_master i
on (b.entry_id = i.programme_id and b.entry_type='Programme'
and i.year_id='".$this->year."')
left join subjectprogramme_master k
on (b.entry_id = k.subjectprogramme_id and b.entry_type='subjectProgramme'
and k.year_id='".$this->year."')
left join masterprogramme_master j
on (b.entry_id = j.masterprogramme_id and b.entry_type='masterProgramme'
and j.year_id='".$this->year."')
left join developmentprogramme_master l
on (b.entry_id = l.developmentprogramme_id
and b.entry_type='developmentProgramme')
1) Get rid of where coditions from left join. Use WHERE clause for filtering
2) I guess UNION or 7 queries (by each entity separetely) will be much better in your case
This is a hard question to answer without having direct access to the database, so I'll try a general answer!
Use "explain" on this query to see if MySQL suggests some indexes. No doubt it'll suggest a few, because you're accessing a few columns several times, and oftentimes indexes will improve even the slowest OUTER JOIN
You're using lots of checks against $this->year, so that would suggest some composite indexes where e.g. the programme_id and the year_id are both in the same index
Of course, there are solutions that might depend on how you're using the output, e.g.:
If this query is run frequently enough to be a problem for users waiting for it, but infrequently enough for latency not to be an issue (e.g. it's ok to run it based on last night's data), you could run it overnight and cache the results.
You really only do a join when a condition is passed, I suggest doing subselects like so:
SELECT
b.entry_id,
b.assign_id,
a.profile_type,
a.profile_id,
a.profile_name,
a.profile_status,
b.entry_type,
b.assign_id,
CASE b.entry_type
WHEN 'chapter' THEN SELECT(c.chapter_name FROM c WHERE b.entry_id = c.chapter_id)
WHEN 'section' THEN SELECT(d.section_name FROM d WHERE b.entry_id = d.section_id)
WHEN ....
END as name
from profile_master a
left join profile_assign b on (a.profile_id = b.profile_id)
If you insist on having the output be the same, then you need to wrap this select in a outer select like so:
SELECT
entry_id, assign_id, ......
, CASE entry_type WHEN 'chapter' THEN name ELSE null END as chapter_name
, CASE entry_type WHEN 'section' THEN name ELSE null END as section_name
FROM
(select statement like above) sub