在检查行是否存在的两种方法之间做出决定(更多子查询 VS 更多左连接)
Deciding between two methods to check if a row exists (more subqueries VS more left joins)
我想使用来自不同表的连接来获取一个产品信息。我有另外三个表(评论、线程、奖励),我想检查是否存在与该特定产品相关的记录。如果它们存在,return 一个非空值,否则为空。将来有可能将更多此类检查添加到查询中。
您更喜欢哪个查询来测试记录是否存在?
将 exists
用于多个子查询:
$sql = "SELECT p.product_id,p.name,m.model,m.model_id,b.brand,me.merchant,
EXISTS(SELECT 1 FROM review WHERE product_id = :id) AS has_review,
EXISTS(SELECT 1 FROM thread WHERE product_id = :id) AS has_thread,
EXISTS(SELECT 1 FROM award WHERE product_id = :id) AS has_award
FROM product p
INNER JOIN model m ON m.model_id = p.model_id
INNER JOIN brand b ON b.brand_id = m.brand_id
INNER JOIN merchant me ON me.merchant_id = m.merchant_id
WHERE p.product_id = :id
LIMIT 1";
$dbh->prepare($sql);
使用多个左连接:
$sql = "SELECT p.product_id,p.name,m.model,m.model_id,b.brand,me.merchant,
(t.product_id is not null) AS has_thread,
(r.product_id is not null) AS has_review,
(a.product_id is not null) AS has_award
FROM product p
INNER JOIN model m ON m.model_id = p.model_id
INNER JOIN brand b ON b.brand_id = m.brand_id
INNER JOIN merchant me ON me.merchant_id = m.merchant_id
LEFT JOIN review r ON re.product_id = p.product_id
LEFT JOIN thread t ON t.product_id = p.product_id
LEFT JOIN award a ON a.product_id = a.product_id
WHERE p.product_id = :id
LIMIT 1";
第一个更好。
为了性能,对于任何一个版本,您都需要在 review(product_id)
、thread(product_id)
和 award(product_id)
上建立索引。
为什么使用 EXISTS
更好?当三个表中不存在匹配的行时,这两个版本应该是等效的(减去第二个查询的最后一个 on
子句中的拼写错误)。但是,当确实存在行时,第二个版本将创建这些行的笛卡尔积,从而影响结果和性能。
注意:我倾向于使用相关子查询来编写EXISTS
子句,因此参数只被引用一次:
EXISTS (SELECT 1 FROM review r WHERE r.product_id = p.product_id) AS has_review,
EXISTS (SELECT 1 FROM thread t WHERE t.product_id = p.product_id) AS has_thread,
EXISTS (SELECT 1 FROM award a WHERE a.product_id = p.product_id) AS has_award,
我想使用来自不同表的连接来获取一个产品信息。我有另外三个表(评论、线程、奖励),我想检查是否存在与该特定产品相关的记录。如果它们存在,return 一个非空值,否则为空。将来有可能将更多此类检查添加到查询中。
您更喜欢哪个查询来测试记录是否存在?
将 exists
用于多个子查询:
$sql = "SELECT p.product_id,p.name,m.model,m.model_id,b.brand,me.merchant,
EXISTS(SELECT 1 FROM review WHERE product_id = :id) AS has_review,
EXISTS(SELECT 1 FROM thread WHERE product_id = :id) AS has_thread,
EXISTS(SELECT 1 FROM award WHERE product_id = :id) AS has_award
FROM product p
INNER JOIN model m ON m.model_id = p.model_id
INNER JOIN brand b ON b.brand_id = m.brand_id
INNER JOIN merchant me ON me.merchant_id = m.merchant_id
WHERE p.product_id = :id
LIMIT 1";
$dbh->prepare($sql);
使用多个左连接:
$sql = "SELECT p.product_id,p.name,m.model,m.model_id,b.brand,me.merchant,
(t.product_id is not null) AS has_thread,
(r.product_id is not null) AS has_review,
(a.product_id is not null) AS has_award
FROM product p
INNER JOIN model m ON m.model_id = p.model_id
INNER JOIN brand b ON b.brand_id = m.brand_id
INNER JOIN merchant me ON me.merchant_id = m.merchant_id
LEFT JOIN review r ON re.product_id = p.product_id
LEFT JOIN thread t ON t.product_id = p.product_id
LEFT JOIN award a ON a.product_id = a.product_id
WHERE p.product_id = :id
LIMIT 1";
第一个更好。
为了性能,对于任何一个版本,您都需要在 review(product_id)
、thread(product_id)
和 award(product_id)
上建立索引。
为什么使用 EXISTS
更好?当三个表中不存在匹配的行时,这两个版本应该是等效的(减去第二个查询的最后一个 on
子句中的拼写错误)。但是,当确实存在行时,第二个版本将创建这些行的笛卡尔积,从而影响结果和性能。
注意:我倾向于使用相关子查询来编写EXISTS
子句,因此参数只被引用一次:
EXISTS (SELECT 1 FROM review r WHERE r.product_id = p.product_id) AS has_review,
EXISTS (SELECT 1 FROM thread t WHERE t.product_id = p.product_id) AS has_thread,
EXISTS (SELECT 1 FROM award a WHERE a.product_id = p.product_id) AS has_award,