在不重复子查询的情况下,在各种 WHERE 子句中重用 MySQL 子查询
Reuse MySQL subquery in various WHERE clause without subquery duplication
使用子查询从翻译 table 中提取一组记录 ID。
然后需要将这组 id 提供给另一个查询的几个 WHERE 子句,以便通过一系列连接从特定的 table (product_listings) 中提取记录。
Table 加入结构
product_brands(1) <-> (n)products(1) <-> (n)product_categories(1) <-> (n)product_listings
子查询 return 的一组 ID 可以是上述 4 table 中的任何一个。
子查询return id 集合
select
record_id
from
translations
where
translations.locale = 'en_CA'
and (
translations.table = 'product_listings'
or translations.table = 'product_categories'
or translations.table = 'products'
or translations.table = 'product_brands'
)
and MATCH (translations.translation) AGAINST ('+jack*' IN BOOLEAN MODE);
此处的主要查询使用 WHERE 子句中的 ID
select
product_listings.*
from
product_listings
left join product_categories on product_categories.ch_id = product_listings.ch_vintage_id
left join products on products.ch_id = product_categories.ch_product_id
left join product_brands on product_brands.ch_id = products.ch_brand_id
where
product_listings.ch_id in (5951765, 252242) <---| Replace these fixed ids
or product_categories.ch_id in (5951765, 252242) <---| with the "record_id" set
or products.ch_id in (5951765, 252242) <---| returned by the subquery
or product_brands.ch_id in (5951765, 252242); <---|
两个查询都完美独立。但是无法成功合并为一个
我发现的唯一肮脏的方法是在每个 WHERE 子句处重复子查询。尝试过它并且有效,但无疑是最有效和最优化的方法。
尝试使用变量,但只能存储一个值 - 不幸的是这不是一个可行的选择。
花了无数小时研究如何避免重复子查询,并以多种方式重写这些子查询,但仍然无法让它工作。
关于如何优雅高效地集成子查询有什么建议吗?
目前正在使用 Mysql Ver 14.14 Distrib 5.7.37,用于 Linux (x86_64)
更新 2022/04/16:添加翻译样本数据 table 和两个查询的预期结果
带有这 2 个 ID 的翻译示例 table
+-----------+----------------+--------+-------------------------------+
| record_id | table | locale | translation |
+-----------+----------------+--------+-------------------------------+
| 5951765 | products | en_CA | Jack Daniel's |
| 252242 | product_brands | en_CA | Dixon's & Jack Daniel's |
+-----------+----------------+--------+-------------------------------+
这是子查询响应
+-----------+
| record_id |
+-----------+
| 5951765 |
| 252242 |
+-----------+
以及使用硬编码 ID 集的主要查询响应(最终预期结果)。我将 select 子句修改为 return 特定列,使 table 可读,而不是 '*'。
前两列是产品中定位的一组 ID,product_brands table 和另外 2 列来自通过连接提取的相应 product_listings 记录。
+------------+----------+--------------+-----------------+
| product_id | brand_id | listing_cspc | listing_format |
+------------+----------+--------------+-----------------+
| 5951765 | 5936861 | 798248 | 6x750 |
| 5951765 | 5936861 | 545186 | 6x750 |
| 5951956 | 252242 | 400669 | 12x750 |
| 5951955 | 252242 | 400666 | 12x750 |
| 5951701 | 252242 | 437924 | 12x750 |
| 5951337 | 252242 | 20244 | 6x750 |
| 5950782 | 252242 | 65166 | 12x750 |
| 5950528 | 252242 | 104941 | 12x750 |
| 5949763 | 252242 | 13990091 | 12x750 |
| 5949750 | 252242 | 614064 | 12x750 |
...
| 1729121 | 252242 | 280248 | 12x750 |
| 1729121 | 252242 | 36414 | 12x750 |
+------------+----------+--------------+-----------------+
如您所见,子查询中的 ID 匹配不同的列。在这种情况下,5951765 是 products.ch_id 而 252242 是 product_brands.ch_id.
下面是考虑到 table 的当前 (1):(n) 关系,我试图实现的目标的直观表示
翻译似乎是这方面的驱动因素,所以我会考虑一个视图并从视图中驱动
Create view yoursubquery as vids;
select
product_listings.*
from vids
left join product_listings pn product_listings.id = vids.record_id
left join product_categories on product_categories.ch_id = product_listings.ch_vintage_id
left join products on products.ch_id = product_categories.ch_product_id
left join product_brands on product_brands.ch_id = products.ch_brand_id
样本数据会很好..
终于!成功了。
根据 @P.Salmon 建议将子查询结果存储在视图中,然后我在该视图上执行了 cross join
并在主查询的 WHERE 子句中使用结果。
但这导致我现在简单地跳过视图,真正的最终解决方案是将子查询放在 cross join
中,从而跳过视图。
时尚且非常高效。
在 croos 连接中使用子查询的最终查询
select
product_listings.*
from
product_listings
cross join (
select
record_id
from
translations
where
translations.locale = 'en_CA'
and (
translations.table = 'product_listings'
or translations.table = 'product_categories'
or translations.table = 'products'
or translations.table = 'product_brands'
)
and MATCH (translations.translation) AGAINST ('+jack*' IN BOOLEAN MODE)
) as vids
left join product_categories on product_categories.ch_id = product_listings.ch_vintage_id
left join products on products.ch_id = product_categories.ch_product_id
left join product_brands on product_brands.ch_id = products.ch_brand_id
where
product_listings.ch_id = vids.record_id
or product_categories.ch_id = vids.record_id
or products.ch_id = vids.record_id
or product_brands.ch_id = vids.record_id
order by
product_brands.ch_id desc,
products.ch_id desc;
使用子查询从翻译 table 中提取一组记录 ID。 然后需要将这组 id 提供给另一个查询的几个 WHERE 子句,以便通过一系列连接从特定的 table (product_listings) 中提取记录。
Table 加入结构
product_brands(1) <-> (n)products(1) <-> (n)product_categories(1) <-> (n)product_listings
子查询 return 的一组 ID 可以是上述 4 table 中的任何一个。
子查询return id 集合
select
record_id
from
translations
where
translations.locale = 'en_CA'
and (
translations.table = 'product_listings'
or translations.table = 'product_categories'
or translations.table = 'products'
or translations.table = 'product_brands'
)
and MATCH (translations.translation) AGAINST ('+jack*' IN BOOLEAN MODE);
此处的主要查询使用 WHERE 子句中的 ID
select
product_listings.*
from
product_listings
left join product_categories on product_categories.ch_id = product_listings.ch_vintage_id
left join products on products.ch_id = product_categories.ch_product_id
left join product_brands on product_brands.ch_id = products.ch_brand_id
where
product_listings.ch_id in (5951765, 252242) <---| Replace these fixed ids
or product_categories.ch_id in (5951765, 252242) <---| with the "record_id" set
or products.ch_id in (5951765, 252242) <---| returned by the subquery
or product_brands.ch_id in (5951765, 252242); <---|
两个查询都完美独立。但是无法成功合并为一个
我发现的唯一肮脏的方法是在每个 WHERE 子句处重复子查询。尝试过它并且有效,但无疑是最有效和最优化的方法。 尝试使用变量,但只能存储一个值 - 不幸的是这不是一个可行的选择。
花了无数小时研究如何避免重复子查询,并以多种方式重写这些子查询,但仍然无法让它工作。
关于如何优雅高效地集成子查询有什么建议吗?
目前正在使用 Mysql Ver 14.14 Distrib 5.7.37,用于 Linux (x86_64)
更新 2022/04/16:添加翻译样本数据 table 和两个查询的预期结果
带有这 2 个 ID 的翻译示例 table
+-----------+----------------+--------+-------------------------------+
| record_id | table | locale | translation |
+-----------+----------------+--------+-------------------------------+
| 5951765 | products | en_CA | Jack Daniel's |
| 252242 | product_brands | en_CA | Dixon's & Jack Daniel's |
+-----------+----------------+--------+-------------------------------+
这是子查询响应
+-----------+
| record_id |
+-----------+
| 5951765 |
| 252242 |
+-----------+
以及使用硬编码 ID 集的主要查询响应(最终预期结果)。我将 select 子句修改为 return 特定列,使 table 可读,而不是 '*'。 前两列是产品中定位的一组 ID,product_brands table 和另外 2 列来自通过连接提取的相应 product_listings 记录。
+------------+----------+--------------+-----------------+
| product_id | brand_id | listing_cspc | listing_format |
+------------+----------+--------------+-----------------+
| 5951765 | 5936861 | 798248 | 6x750 |
| 5951765 | 5936861 | 545186 | 6x750 |
| 5951956 | 252242 | 400669 | 12x750 |
| 5951955 | 252242 | 400666 | 12x750 |
| 5951701 | 252242 | 437924 | 12x750 |
| 5951337 | 252242 | 20244 | 6x750 |
| 5950782 | 252242 | 65166 | 12x750 |
| 5950528 | 252242 | 104941 | 12x750 |
| 5949763 | 252242 | 13990091 | 12x750 |
| 5949750 | 252242 | 614064 | 12x750 |
...
| 1729121 | 252242 | 280248 | 12x750 |
| 1729121 | 252242 | 36414 | 12x750 |
+------------+----------+--------------+-----------------+
如您所见,子查询中的 ID 匹配不同的列。在这种情况下,5951765 是 products.ch_id 而 252242 是 product_brands.ch_id.
下面是考虑到 table 的当前 (1):(n) 关系,我试图实现的目标的直观表示
翻译似乎是这方面的驱动因素,所以我会考虑一个视图并从视图中驱动
Create view yoursubquery as vids;
select
product_listings.*
from vids
left join product_listings pn product_listings.id = vids.record_id
left join product_categories on product_categories.ch_id = product_listings.ch_vintage_id
left join products on products.ch_id = product_categories.ch_product_id
left join product_brands on product_brands.ch_id = products.ch_brand_id
样本数据会很好..
终于!成功了。
根据 @P.Salmon 建议将子查询结果存储在视图中,然后我在该视图上执行了 cross join
并在主查询的 WHERE 子句中使用结果。
但这导致我现在简单地跳过视图,真正的最终解决方案是将子查询放在 cross join
中,从而跳过视图。
时尚且非常高效。
在 croos 连接中使用子查询的最终查询
select
product_listings.*
from
product_listings
cross join (
select
record_id
from
translations
where
translations.locale = 'en_CA'
and (
translations.table = 'product_listings'
or translations.table = 'product_categories'
or translations.table = 'products'
or translations.table = 'product_brands'
)
and MATCH (translations.translation) AGAINST ('+jack*' IN BOOLEAN MODE)
) as vids
left join product_categories on product_categories.ch_id = product_listings.ch_vintage_id
left join products on products.ch_id = product_categories.ch_product_id
left join product_brands on product_brands.ch_id = products.ch_brand_id
where
product_listings.ch_id = vids.record_id
or product_categories.ch_id = vids.record_id
or products.ch_id = vids.record_id
or product_brands.ch_id = vids.record_id
order by
product_brands.ch_id desc,
products.ch_id desc;