I am currently learning MySQL and want to write a simple dictionary application.
Currently I have 3 tables:
Language1:
ID | Word
Language2:
ID | Word
Dict:
ID_Lang1 | ID_Lang2
Now I am struggling to make a query that looks in both language tables for the word and via the dict table finds the corresponding word in the other language.
What I was trying was like:
select lang1.ID, lang2.ID from Language1, Language2 WHERE lang1.word OR lang2.word = 'Random'
and to join this somehow together with the Dict table, but could not make it work.
Hope someone will enlighten me!
Maybe something like this?
SELECT *
FROM `dict` d
LEFT JOIN `language1` l1 ON d.`id_lang1` = l1.`id`
LEFT JOIN `language2` l2 ON d.`id_lang2` = l2.`id`
WHERE
l1.`word` LIKE 'random'
OR l2.`word` LIKE 'random'
Performance of query will get hit as Dict becomes larger, but this is just one way to do it:
SELECT L1.ID, L2.ID
FROM Language1 AS L1
INNER JOIN Dict AS D ON D.ID_Lang1 = L1.ID
INNER JOIN Language2 AS L2 ON D.ID_Lang2 = L2.ID
WHERE L1.Word = 'Random'
OR L2.Word = 'Random'
Related
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
Okay, so I know the title is a bit cryptic so I'll do what I can to explain the "problem" I have and the solution I am currently using.
Problem:
An 'object' of work needs to be distributed to the apropriate user based on said object's properties.
The idea is that there is an object of work has properties. Those properties are mapped to skills. A user has skills and is able to work on an object which is within the user's skillset.
There are several [three] property definitions and I currently have the following table structures.
|-- Object to Property Set 1 -- Property Set 1 to Skill --|
Object Table -|-- Object to Property Set 2 -- Property Set 2 to Skill --|-- User Skill -- User Table
|-- Object to Property Set 3 -- Property Set 3 to Skill --|
The query may be a bit easier to understand:
SELECT counts.object_id,
COUNT(DISTINCT counts.object_skill) object_skill_count,
COUNT(DISTINCT counts.user_skill) user_skill_count
FROM
(SELECT object.object_id,
sp.skill_id object_skill,
us.skill_id user_skill
FROM object_table object
LEFT JOIN object_property op ON op.object_id = object.object_id
LEFT JOIN skill_property sp ON sp.property_id = op.property_id
LEFT JOIN user_skill us ON us.skill_id = sp.skill_id
AND us.active = 1
AND us.user_id = {$userid} -- <=- inserted from a PHP script
AND object.state = 1
UNION SELECT object.object_id,
sf.skill_id object_skill,
us.skill_id user_skill
FROM object_table object
LEFT JOIN object_flag obf ON obf.object_id = object.object_id
LEFT JOIN skill_flag sf ON sf.flag_id = obf.flag_id
LEFT JOIN user_skill us ON us.skill_id = sf.skill_id
AND us.active = 1
AND us.user_id = {$userid} -- <=- inserted from a PHP script
AND object.state = 1
UNION SELECT object.object_id,
svf.skill_id object_skill,
us.skill_id user_skill
FROM object_table object
LEFT JOIN object_creator oc ON oc.creator_id = object.creator_id
LEFT JOIN skill_creator sc ON sc.flag_id = oc.flag_id
LEFT JOIN user_skill us ON us.skill_id = sc.skill_id
AND us.active = 1
AND us.user_id = {$userid} -- <=- inserted from a PHP script
AND object.state = 1) counts
GROUP BY counts.object_id
Here we get a count of all the skills an object as well as count the number of skills the user has on that same object. If the two counts match, we know the user can work on the object. If the object's skill count exceeds the user's count, the object is beyond the user's capabilities and will not be assigned to that user.
While the above query works, it slows significantly when thrown at a large[r] table. Would like to know if there is a better way of doing things. And, since the internet is filled with amazing people, here we are.
Retroactive Update:
The Left joins in this case are there because objects can have no properties. This equates to the count 0-0 and thus makes the object workable by anyone.
It looks ok. Conditions placed on data joins instead of where clause, no order by's.
Possible options:
1) Check for missing indexes
http://basitaalishan.com/2013/03/13/find-missing-indexes-using-sql-servers-index-related-dmvs/
2) Change 'left joins' to 'inner joins'
INNER JOIN vs LEFT JOIN performance in SQL Server
3) Use 'UNION ALL' instead of 'Union'
performance of union versus union all
I have a small problem updating rows of one table with data from another one, please help.
Table l with columns
Make
Model
OEMNumberLatest
OemNumberPrevious
cStockCode
cDescription
The two columns cStockCode,cDescription are blank at the moment and are waiting to be populated, and the second table c with columns StockCode and Description
The question is how to move/copy contents of table c into rows of table l?
Where c.StockCode = l.OEMNumberLatest and or c.StockCode = l.OemNumberPrevious
You probably shouldn't move the contents of one table into the other. It is really better to just get the values when you need them, using a join:
select l.*, c.cStockCode, c.cDescription
from table1 l join
c
on c.StockCode = l.OEMNumberLatest or c.StockCode = l.OemNumberPrevious;
This seems like a strange condition, with the or because you can get multiple matches.
In any case, you can convert this to an update easily:
update table1 l join
c
on c.StockCode = l.OEMNumberLatest or c.StockCode = l.OemNumberPrevious
set l.cStockCode = c.cStockCode,
l.cDescription = c.cDescription;
When both conditions match, then one will arbitrarily be used for the update.
This is a example of a update syntax using another table
UPDATE tableL SET
tableL.cStockCode=tableC.StockCode,tableL.cDescription=tableC.Description
FROM tableL
JOIN tableC ON
(tableC.StockCode = tableL.OEMNumberLatest)
OR
(tableC.StockCode = tableL.OemNumberPrevious);
I assument that the contents in tableC is to be filled in tableL , and the JOIN is based on StockCode with OEMNumberLatest or OemNumberPrevious
Try this (save your data before trying, I am not sure of your database structure) :
UPDATE l
SET l.cStockCode = c.StockCode, l.cDescription = c.Description
FROM l
INNER JOIN c ON (c.StockCode = l.OEMNumberLatest OR c.StockCode = l.OemNumberPrevious)
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.
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