在不重复子查询的情况下,在各种 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;