MySQL在SELECT子查询中引用其他SELECTS

时间:2012-02-11 21:36:37

标签: mysql subquery union ranking

我正在尝试创建一个基于现场浏览习惯对产品进行排名的查询(我在下面提供了一个简化的非动态查询示例)。

我遇到的问题是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

2 个答案:

答案 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个可能的值:1231 + 21 + 32 + 31 + 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 ...并且左边也加入了其他人。你有意做左连接吗?