我正在尝试创建一个基于现场浏览习惯对产品进行排名的查询(我在下面提供了一个简化的非动态查询示例)。
我遇到的问题是SELECT子查询中引用其他列的UNIONs。
有没有人知道这个聪明的解决方法?
SELECT p.*, pi.piid, c.title AS cat_title, c.fn AS cat_fn, b.fn AS brand_fn, b.title AS brand_title,
(
SELECT SUM(prod_rank) AS rank FROM (
(
SELECT 2.95 AS prod_rank FROM prod_link_cat WHERE pid = p.pid AND link_cid = 1
) UNION ALL (
SELECT 2.8 AS prod_rank FROM prod_link_cat WHERE p id = p.pid AND link_cid = 3
) UNION ALL (
SELECT 0.5 AS prod_rank FROM prod_link_cat WHERE pid = p.pid AND link_cid = 2
)
) AS tbl1
) AS rank
FROM prod p
LEFT JOIN prod_link_cat plc ON plc.pid = p.pid AND plc.position = 1
LEFT JOIN cat c ON plc.link_cid = c.cid AND c.live = 1
LEFT JOIN brand b ON b.bid = p.bid AND b.live = 1
LEFT JOIN prod_link_prod_img plpi ON plpi.pid = p.pid AND plpi.position = 1
LEFT JOIN prod_img pi ON pi.piid = plpi.link_piid AND pi.live = 1
WHERE p.live = 1
GROUP BY p.pid
ORDER BY (RAND() * rank)
LIMIT 20
答案 0 :(得分:1)
对于这个特定的查询,有一个简单但易于解决的问题。
根据您的查询判断我假设prod_link_cat
看起来像这样,其中link_cid
不一定总是填充:
pid|link_cid|other_columns...
1 | 1 | ...
1 | 2 | ...
1 | 3 | ...
2 | 1 | ...
当您从此表中选择一个常量值时,没有理由将其输入3次...此外,常量仅基于link_cid
值1,2或3的存在。这意味着有8个可能的值:1
,2
,3
,1 + 2
,1 + 3
,2 + 3
,1 + 2 + 3
和{{ 1}}。
最佳解决方案是在这些组合上创建另一个具有组合排名分数的表。这有两个好处,首先,如果您需要更新它,那么您不必更改所有代码。其次,您可以将排名作为外键放入nothing
中唯一的表中,并且非常容易实现。
您也可以使用函数来获得相同的结果。
因此,要优化查询,您必须删除有问题的部分并将其转换为左外连接。你会松开3个独特的索引扫描,2个联合和一个总和,所以它可能值得付出努力!
答案 1 :(得分:1)
要删除内联字段查询(使用联合),我会重写它并将其作为左连接移动,基于与p.Live = 1的外部“prod”条件相同的条件。查询其他所有内容时没有任何意义其中p.Live =其他东西。
SELECT
p.*,
pi.piid,
c.title AS cat_title,
c.fn AS cat_fn,
b.fn AS brand_fn,
b.title AS brand_title,
COALESCE( PreQuery.ProdRankSum, 0 ) as ProdRankSum
FROM
prod p
LEFT JOIN
( SELECT
p2.id,
SUM( if( plc.link_cid = 1, 2.95, 0.00 )
+ if( plc.link_cid = 2, .50, 0.00 )
+ if( plc.link_cid = 3, 2.80, 0.00 )) ProdRankSum
FROM
prod p2
JOIN prod_link_cat plc
ON p2.ID = plc.pid
AND plc.link_cid in ( 1, 2, 3 )
WHERE
p2.Live = 1
GROUP BY
p2.id ) PreQuery
ON p.id = PreQuery.id
LEFT JOIN prod_link_cat plc
ON p.pid = plc.pid AND plc.position = 1
LEFT JOIN cat c
ON plc.link_cid = c.cid AND c.live = 1
LEFT JOIN brand b
ON p.bid = b.bid AND b.live = 1
LEFT JOIN prod_link_prod_img plpi
ON p.pid = plpi.pid AND plpi.position = 1
LEFT JOIN prod_img pi
ON plpi.link_piid = pi.piid AND pi.live = 1
WHERE
p.live = 1
GROUP BY
p.pid
ORDER BY
(RAND() * COALESCE( PreQuery.ProdRankSum, 0 ))
LIMIT 20
现在,在所有这些之后,你可能不得不对排名做一些事情,例如设置除0以外的COALESCE()值...例如.001否则,对于那些产品,它总是为零没有产品链接cat ...并且按随机时间排序0将始终为零,因此到顶部。 (或者改为ORDER BY ... DESC)
但是,如果您只想要那些明确拥有1,2或3类链接的产品,我会略微重写这个查询。让我知道。
此外,您的原始查询正在左边连接到产品链接cat为position = 1,然后是cat,其中cat是LIVE = 1 ...并且左边也加入了其他人。你有意做左连接吗?