在 MySQL 中使用 LEFT JOINing 多个表搜索多个值
search for multiple values with LEFT JOINing several tables in MySQL
我在尝试通过 LEFT JOINing 多个 table 搜索 MySQL 中的多个值时遇到了一个大问题,并使用可能的额外参数进行过滤 "all search"。请允许我详细说明一个示例案例:
我有以下数据库结构:
wp_posts
id | post_name
--------------
1 | sword
2 | bow
3 | shield
wp_postmeta
id | post_id | meta_key | meta_value
------------------------------------
1 | 1 | user | 101
2 | 1 | power | 5
3 | 1 | defense | 0
4 | 1 | range | 1
5 | 1 | condi | old
6 | 2 | user | 102
7 | 2 | range | 10
8 | 2 | condi | new
9 | 3 | user | 101
10 | 3 | power | 0
11 | 3 | defense | 10
我正在使用以下内容构建 PHP MySQL 搜索:
1) "all search" 输入。搜索任何 post_name
或任何 meta_value
。
2) 过滤器输入选择meta_key
并输入meta_value
例如在 power
= '5'
上搜索 "sword" 过滤器
SELECT *
FROM wp_posts as p
LEFT JOIN wp_postmeta as pm ON p.id = pm.post_id
WHERE (post_name LIKE '%$search_str%' OR meta_value LIKE '%$search_str%')
/* extra filter */
AND (meta_key = '$filter_key' AND meta_value = '$filter_val')
GROUP BY p.id
我的问题:
我总是希望 wp_posts
中的每条记录有 1 个结果。
但是由于我的 LEFT JOIN,每个 post_key
和 post_value
都会产生不同的行。
因此,如果我使用多个过滤器(在多个 meta_values 上)搜索内容,我将一无所获。
例如在 range
= 1
.
上的所有搜索 + 过滤器中搜索 old
SELECT * FROM wp_posts as p
LEFT JOIN wp_postmeta as pm ON p.id = pm.post_id
WHERE (post_name LIKE '%old%' OR meta_value LIKE '%old%')
AND (meta_key = 'range' AND meta_value LIKE '%1%')
GROUP BY p.id
→‖0 个结果...
所以我知道原因,但我不知道好的解决方法...
我知道我可以组 concat 列,但是我不能保持 meta_key 和 meta_value 的设置对吗?关于执行此操作的好方法有什么想法吗?
(等到你听到我正在添加带有标签的第三个 table 并将其添加到我的全搜索框......)
我会使用 exists subquery 来解决这个问题:
SELECT * FROM wp_posts as p
LEFT JOIN wp_postmeta as pm ON p.id = pm.post_id
WHERE (post_name LIKE '%old%' OR meta_value LIKE '%old%')
AND EXISTS (SELECT 1 FROM wp_postmeta pm2 WHERE pm2.meta_key = 'range' AND pm2.meta_value LIKE '%1%' and pm2.post_id=pm.post_id)
GROUP BY p.id
但请注意,您的查询取决于未设置 sql 模式的完整组。 sql 模式现在在 mysql 的较新版本中默认启用。
你可以用两个 EXISTS()
条件来做到这一点:
SELECT * FROM wp_posts p
WHERE (post_name like '%old%'
or EXISTS(SELECT 1 FROM wp_postmeta p2 WHERE p.id = p2.id AND p2.meta_value LIKE '%OLD%'))
AND EXISTS(SELECT 1 FROM wp_postmeta p3 WHERE p.id = p3.id AND p3.meta_value LIKE '%1%' AND p3.meta_key = 'range')
另一个解决方案,它(恕我直言)更接近您的原始查询:
SELECT DISTINCT * FROM wp_posts as p
INNER JOIN wp_postmeta as pm1 ON p.id = pm1.post_id
LEFT JOIN wp_postmeta as pm ON p.id = pm.post_id
WHERE (p.post_name LIKE '%old%' OR pm.meta_value LIKE '%old%')
AND (pm1.meta_key = 'range' AND pm1.meta_value LIKE '%1%')
-- GROUP BY p.id
我使用另一个 (INNER) JOIN 按必备条件 范围 LIKE '%1%' 进行过滤。我不确定它会如何表现,但找不到原因,为什么它会表现更差。
我希望 MySQL 首先在 pm1
中搜索 meta_key = 'range'
(应该被索引),看看是否 meta_value LIKE '%1%'
,如果是 - 找到相关的 post.如果 post_name LIKE '%old%'
select post 行,如果不是 - 在 wp_postmeta
的所有相关行中搜索 meta_value LIKE '%old%'
。如果找到一行 - 跳过搜索(因为 DISTINCT
)和 select post 行。如果没有跳过 post 行。
它甚至可能表现得更好,因为您跳过了所有根本没有 "range" 的 post。
我在尝试通过 LEFT JOINing 多个 table 搜索 MySQL 中的多个值时遇到了一个大问题,并使用可能的额外参数进行过滤 "all search"。请允许我详细说明一个示例案例:
我有以下数据库结构:
wp_posts
id | post_name
--------------
1 | sword
2 | bow
3 | shield
wp_postmeta
id | post_id | meta_key | meta_value
------------------------------------
1 | 1 | user | 101
2 | 1 | power | 5
3 | 1 | defense | 0
4 | 1 | range | 1
5 | 1 | condi | old
6 | 2 | user | 102
7 | 2 | range | 10
8 | 2 | condi | new
9 | 3 | user | 101
10 | 3 | power | 0
11 | 3 | defense | 10
我正在使用以下内容构建 PHP MySQL 搜索:
1) "all search" 输入。搜索任何 post_name
或任何 meta_value
。
2) 过滤器输入选择meta_key
并输入meta_value
例如在 power
= '5'
SELECT *
FROM wp_posts as p
LEFT JOIN wp_postmeta as pm ON p.id = pm.post_id
WHERE (post_name LIKE '%$search_str%' OR meta_value LIKE '%$search_str%')
/* extra filter */
AND (meta_key = '$filter_key' AND meta_value = '$filter_val')
GROUP BY p.id
我的问题:
我总是希望 wp_posts
中的每条记录有 1 个结果。
但是由于我的 LEFT JOIN,每个 post_key
和 post_value
都会产生不同的行。
因此,如果我使用多个过滤器(在多个 meta_values 上)搜索内容,我将一无所获。
例如在 range
= 1
.
old
SELECT * FROM wp_posts as p
LEFT JOIN wp_postmeta as pm ON p.id = pm.post_id
WHERE (post_name LIKE '%old%' OR meta_value LIKE '%old%')
AND (meta_key = 'range' AND meta_value LIKE '%1%')
GROUP BY p.id
→‖0 个结果...
所以我知道原因,但我不知道好的解决方法...
我知道我可以组 concat 列,但是我不能保持 meta_key 和 meta_value 的设置对吗?关于执行此操作的好方法有什么想法吗?
(等到你听到我正在添加带有标签的第三个 table 并将其添加到我的全搜索框......)
我会使用 exists subquery 来解决这个问题:
SELECT * FROM wp_posts as p
LEFT JOIN wp_postmeta as pm ON p.id = pm.post_id
WHERE (post_name LIKE '%old%' OR meta_value LIKE '%old%')
AND EXISTS (SELECT 1 FROM wp_postmeta pm2 WHERE pm2.meta_key = 'range' AND pm2.meta_value LIKE '%1%' and pm2.post_id=pm.post_id)
GROUP BY p.id
但请注意,您的查询取决于未设置 sql 模式的完整组。 sql 模式现在在 mysql 的较新版本中默认启用。
你可以用两个 EXISTS()
条件来做到这一点:
SELECT * FROM wp_posts p
WHERE (post_name like '%old%'
or EXISTS(SELECT 1 FROM wp_postmeta p2 WHERE p.id = p2.id AND p2.meta_value LIKE '%OLD%'))
AND EXISTS(SELECT 1 FROM wp_postmeta p3 WHERE p.id = p3.id AND p3.meta_value LIKE '%1%' AND p3.meta_key = 'range')
另一个解决方案,它(恕我直言)更接近您的原始查询:
SELECT DISTINCT * FROM wp_posts as p
INNER JOIN wp_postmeta as pm1 ON p.id = pm1.post_id
LEFT JOIN wp_postmeta as pm ON p.id = pm.post_id
WHERE (p.post_name LIKE '%old%' OR pm.meta_value LIKE '%old%')
AND (pm1.meta_key = 'range' AND pm1.meta_value LIKE '%1%')
-- GROUP BY p.id
我使用另一个 (INNER) JOIN 按必备条件 范围 LIKE '%1%' 进行过滤。我不确定它会如何表现,但找不到原因,为什么它会表现更差。
我希望 MySQL 首先在 pm1
中搜索 meta_key = 'range'
(应该被索引),看看是否 meta_value LIKE '%1%'
,如果是 - 找到相关的 post.如果 post_name LIKE '%old%'
select post 行,如果不是 - 在 wp_postmeta
的所有相关行中搜索 meta_value LIKE '%old%'
。如果找到一行 - 跳过搜索(因为 DISTINCT
)和 select post 行。如果没有跳过 post 行。
它甚至可能表现得更好,因为您跳过了所有根本没有 "range" 的 post。