SELECT * FROM
((SELECT 1 precedence, `sp_content`
FROM `loc-ae_siteparts`
WHERE `sp_name` = 'name_of_some_sitepart' AND `sp_lang` = 'de' AND `sp_corrected`='1'
LIMIT 1)
UNION
(SELECT 2 precedence, `sp_content`
FROM `loc-ae_siteparts`
WHERE `sp_name` = 'name_of_some_sitepart' AND `sp_lang` = 'en'
LIMIT 1)
UNION
...
) x
ORDER BY precedence
LIMIT 1
MySQL multi-table SELECT
-
08-10-2022 - |
Question
I'm stuck a little with a combine MySQL query. It's a part of a multi-language, multi-destination site, so I need to retrieve some text parts of the site from multiple table, with fallback. Order of possible hits:
corrected@local_tbl > english@local_tbl > corrected@global_tbl > english@global_tbl.
Because it'll be highly used, I'd like to keep it fast and with low number of connections, but returning only 1 row. I tried a few approaches as follows, but I'm sure there is a better solution.
Subquery in FROM clause: It dies if any of subquery gives 0 rows. Even if it works, needs some php interpret:
SELECT * FROM
(SELECT `sp_content` AS sp_local FROM `loc-ae_siteparts` WHERE `sp_name` = 'name_of_some_sitepart' AND `sp_lang` = 'de' AND `sp_corrected`='1' LIMIT 1) as local,
(SELECT `sp_content` AS sp_local_en FROM `loc-ae_siteparts` WHERE `sp_name` = 'name_of_some_sitepart' AND `sp_lang` = 'en' LIMIT 1) as local_en,
(SELECT `sp_content` AS sp_global, `sp_corrected` as sp_global_corrected FROM `loc-global_siteparts` WHERE `sp_name` = 'name_of_some_sitepart' AND `sp_lang` = 'de' LIMIT 1) as global,
(SELECT `sp_content` AS sp_global_en FROM `loc-global_siteparts` WHERE `sp_name` = 'name_of_some_sitepart' AND `sp_lang` = 'en' LIMIT 1) as global_en
TEMP Table: Here I am concerned about performance, can't use memory engine because it involves a text field. Wasted nuke for a birdie?
CREATE TEMPORARY TABLE IF NOT EXISTS `random_tbl_name` AS (SELECT `sp_content` FROM `loc-global_siteparts` LIMIT 0);
INSERT INTO `random_tbl_name` SELECT `sp_content` FROM `loc-ae_siteparts` WHERE `sp_name` = 'name_of_some_sitepart' AND `sp_lang` = 'de' AND `sp_corrected` = '1' LIMIT 1;
INSERT INTO `random_tbl_name` SELECT `sp_content` FROM `loc-ae_siteparts` WHERE `sp_name` = 'name_of_some_sitepart' AND `sp_lang` = 'en' LIMIT 1;
INSERT INTO `random_tbl_name` SELECT `sp_content` FROM `loc-global_siteparts` WHERE `sp_name` = 'name_of_some_sitepart' AND `sp_lang` = 'de' AND `sp_corrected` = '1' LIMIT 1;
INSERT INTO `random_tbl_name` SELECT `sp_content` FROM `loc-global_siteparts` WHERE `sp_name` = 'name_of_some_sitepart' AND `sp_lang` = 'en' LIMIT 1;
SELECT * FROM `random_tbl_name` LIMIT 1;
EDIT: Thanks for all answers, they were really helpful.
Solution
OTHER TIPS
The reason you get no result when one of the subqueries is empty is, with
select * from
s1, s2, s3, s4
you have an implicit join. And when one of the sub-results is empty, the resulting join is empty as well.
To solve this, you can use your subqueries as columns. This will give you the same result, but with NULL values, where the subselect returns no rows
SELECT (SELECT `sp_content` AS sp_local FROM `loc-ae_siteparts` WHERE `sp_name` = 'name_of_some_sitepart' AND `sp_lang` = 'de' AND `sp_corrected`='1' LIMIT 1) as local,
(SELECT `sp_content` AS sp_local_en FROM `loc-ae_siteparts` WHERE `sp_name` = 'name_of_some_sitepart' AND `sp_lang` = 'en' LIMIT 1) as local_en,
(SELECT `sp_content` AS sp_global, `sp_corrected` as sp_global_corrected FROM `loc-global_siteparts` WHERE `sp_name` = 'name_of_some_sitepart' AND `sp_lang` = 'de' LIMIT 1) as global,
(SELECT `sp_content` AS sp_global_en FROM `loc-global_siteparts` WHERE `sp_name` = 'name_of_some_sitepart' AND `sp_lang` = 'en' LIMIT 1) as global_en
I've done similar things in the past like this:
SELECT COALESCE(ls1.sp_content, ls2.sp_content) AS sp_content
FROM (SELECT 1) a
LEFT JOIN loc-ae_siteparts ls1
ON ls1.sp_name = 'name_of_some_sitepart' AND ls1.sp_lang = 'de' AND ls1.sp_corrected = '1'
LEFT JOIN loc-ae_siteparts ls2
ON ls2.sp_name = 'name_of_some_sitepart' and ls2.sp_lang = 'en'
LIMIT 1