我是否经历了以下情况并且没有找到解决此问题的好方法。我正在进行API的优化,所以我正在寻找最快的解决方案。
以下描述并不完全是我正在做的事情,但我认为它代表了问题。
我们说我有一张产品表:
+----+----------+
| id | name |
+----+----------+
| 1 | product1 |
| 2 | product2 |
+----+----------+
我有一个每个产品的附件表,按语言分开:
+----+----------+------------+-----------------------+
| id | language | product_id | attachment_url |
+----+----------+------------+-----------------------+
| 1 | bb | 1 | image1_bb.jpg |
| 1 | en | 1 | image1_en.jpg |
| 1 | pt | 1 | image1_pt.jpg |
| 2 | bb | 1 | image2_bb.jpg |
| 2 | pt | 1 | image2_pt.jpg |
+----+----------+------------+-----------------------+
我打算按照请求中选择的语言获取正确的附件。如您所见,我可以为每个产品添加几个附件。我们使用Babel(bb
)作为通用语言,因此每次我都没有使用正确的语言时,我应该获得babel版本。同样重要的是要考虑附件表的主键是id
+ language
的组合。
因此,假设我尝试获取pt
中的所有数据,我创建SQL查询的第一个选项是:
SELECT p.id, p.name,
GROUP_CONCAT( '{',a.id,',',a.attachment_url, '}' ) as attachments_list
FROM products p
LEFT JOIN attachments a
ON (a.product_id=p.id AND (a.language='pt' OR a.language='bb'))
问题在于,通过此查询,我总是得到bb
数据,而我只想在没有正确语言的附件时获取它。
我已经尝试过更改附件的子查询:
(SELECT * FROM attachments GROUP BY id ORDER BY id ASC, language DESC)
但是请求的时间加倍。
我也尝试在DISTINCT
中使用GROUP_CONCAT
,但只有在每行的整个结果相同时它才有效,所以它对我不起作用。
有谁知道我可以直接应用于查询的其他任何解决方案?
修改
结合@Vulcronos和@Barmar的答案使得最终解决方案至少比我最初建议的解决方案快2倍。
只是为其他正在寻找它的人添加一些上下文。我正在使用Phalcon。因为它,我把这些碎片放在一起很麻烦,因为Phalcon PHQL不支持子查询,也不支持我必须使用的其他许多东西。
对于我的方案,我必须提供大约1.2MB的JSON内容,超过2100个对象,使用自定义查询使得总请求时间比Phalcon本机关系管理方法快3倍(hasMany()
,hasManyToMany()
等),比原始解决方案快了10倍(使用了很多find()
方法)。
答案 0 :(得分:1)
尝试进行两次连接而不是一次:
SELECT p.id, p.name,
GROUP_CONCAT( '{',COALESCE(a.id, b.id),',',COALESCE(a.attachment_url, b.attachment_url), '}' ) as attachments_list
FROM products p
LEFT JOIN attachments a
ON (a.product_id=p.id AND a.language='pt')
LEFT JOIN attachments b
ON (a.product_id=p.id AND a.language='bb')
然后使用COALESCE返回b而不是a如果a不存在。如果以上操作不起作用,您也可以使用子选择。
答案 1 :(得分:1)
OR
条件往往会使查询变慢,因为很难用索引对它们进行优化。尝试使用两种不同的语言单独加入。
SELECT p.id, p.name,
IFNULL(apt.attachment_url, abb.attachment_url) AS attachment_url
FROM products AS p
JOIN attachments AS abb ON abb.product_id = p.id
LEFT JOIN attachments AS apt ON alang.product_id = p.id AND apt.language = 'pt'
WHERE abb.language = 'bb'
这假设所有产品都有bb
附件,而pt
是可选的。
答案 2 :(得分:0)
我遗漏了产品的加入,因为它与此问题无关。它只需要在结果集中包含产品名称。
SELECT a.product_id, a.id, a.attachment_url FROM attachments a
WHERE a.language = ?
OR (a.language = 'bb'
AND NOT EXISTS
(SELECT * FROM attachments
WHERE language = ?
AND id = a.id
AND product_id = a.product_id));
注意:这样的问题通常有很多可能的解决方案。这不一定是效率最高的。