更新
我的最终目标是根据产品的有效期对类别进行排序。因此,产品最接近过期的类别排在第一位,所有产品过期的类别最后一位。感谢Greg,请参阅问题底部的完整答案。
/UPDATE
除了第二行(SELECT MIN(date_expires…关于如何重写这一行有什么想法吗?
SELECT c.category_id,
name,
c.image,
description,
(SELECT MIN(date_expires)
FROM product
WHERE date_expires >= NOW() AND
p.product_id = p2c.product_id) AS expiring
FROM category c
LEFT JOIN category_description cd
ON (c.category_id = cd.category_id)
LEFT JOIN product_to_category p2c
ON (c.category_id = p2c.category_id)
LEFT JOIN product p
ON (p2c.product_id = p.product_id)
WHERE c.parent_id = 0 AND c.status = '1'
GROUP BY c.category_id
ORDER BY expiring
一些样本数据:
category:
category_id = 62
category_id = 64
category_id = 63
product_to_category:
product_id = 43 category_id = 62
product_id = 50 category_id = 63
product_id = 56 category_id = 63
product_id = 58 category_id = 62
product_id = 59 category_id = 63
product_id = 60 category_id = 63
product_id = 61 category_id = 63
product_id = 62 category_id = 63
product_id = 63 category_id = 64
product:
product_id = 43 date_expires = 2012-07-11 20:35:00
product_id = 50 date_expires = 2012-06-29 00:00:00
product_id = 56 date_expires = 2012-07-13 00:00:00
product_id = 58 date_expires = 2012-07-26 15:01:00
product_id = 59 date_expires = 2012-07-16 14:12:00
product_id = 60 date_expires = 2012-07-18 08:15:00
product_id = 61 date_expires = 2012-08-02 13:04:00
product_id = 62 date_expires = 2012-08-24 00:00:00
product_id = 63 date_expires = 2012-07-12 19:16:00
答案我的最终目标是获得
SELECT c.category_id, minDateExpires.Expiring,
CASE WHEN Expiring > 1 THEN 1 ELSE 2 END as available
FROM category c
LEFT JOIN product_to_category p2c ON (c.category_id = p2c.category_id)
LEFT JOIN product p ON (p2c.product_id = p.product_id)
LEFT JOIN (
SELECT MIN(date_expires) AS expiring, category_id FROM product p join product_to_category p2c on p2c.product_id = p.product_id WHERE date_expires >= GETDATE() GROUP BY category_id
) minDateExpires on p2c.category_id = minDateExpires.category_id
ORDER BY available, expiring
如果您正在为OpenCart特别查找此项,并且您正在使用Categories模块,请转到catalog/model/controlog/category.php并将函数getCategoriesPag()编辑为:
public function getCategoriesPag($data) {
$query = $this->db->query("SELECT c.category_id, name, c.image, description, minDateExpires.Expiring, CASE WHEN Expiring > 1 THEN 1 ELSE 2 END as available FROM " . DB_PREFIX . "category c LEFT JOIN " . DB_PREFIX . "category_description cd ON (c.category_id = cd.category_id) LEFT JOIN " . DB_PREFIX . "category_to_store c2s ON (c.category_id = c2s.category_id) LEFT JOIN " . DB_PREFIX . "product_to_category p2c ON (c.category_id = p2c.category_id) LEFT JOIN " . DB_PREFIX . "product p ON (p2c.product_id = p.product_id) LEFT JOIN (SELECT MIN(date_expires) AS expiring, category_id FROM product p JOIN product_to_category p2c on p2c.product_id = p.product_id WHERE date_expires >= NOW() GROUP BY category_id) minDateExpires on p2c.category_id = minDateExpires.category_id WHERE c.parent_id = 0 AND cd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND c2s.store_id = '" . (int)$this->config->get('config_store_id') . "' AND c.status = '1' GROUP BY c.category_id ORDER BY available, Expiring, c.sort_order");
return $query->rows;
}
您需要将自己的date_expires字段添加到数据库和后端管理面板中。
我不相信这句话是真的:
最初的问题在于子查询看不到您在主查询中加入的表。因此,子查询中不存在p.product_id和p2c.product_id。
由于该条件是主查询中外部联接的一部分,因此已知该条件为true或将计算为null,从而导致null结果。现在,您的示例数据表明您无论如何都不需要外部联接,并且我可以看到许多行都有未来的到期日期。
我相信你是想把它写成你的子查询。它可能值得一试,尽管我们得到的其他一些信息表明它仍然不是最终的解决方案。
(
SELECT MIN(date_expires)
FROM product as p2
WHERE date_expires >= NOW() AND
p2.product_id = p.product_id
) AS expiring
编辑进行我刚才建议的更改,但也去掉GROUP BY子句。
编辑2
1)您似乎在多个表中拥有可能属于单个产品表的属性。我认为你对此有充分的理由。
2)Mysql允许您混合聚合列和非聚合列,但这是一种糟糕的做法,如果我的理论被证明是正确的,那也是我们都被困了一段时间的主要原因。
3)所有左外部联接似乎都在执行通常使用内部联接的操作。你不会后悔第一次做对了。
4)过期日期可以按照您第一次编写子查询的方式查找,也可以按照另一个答案的方式查找。如果真的存在一对一的关系,那么MIN()和GROUP BY是不必要的,并且可能会混淆某些人。(事实上,请参阅编辑#3。)
5)由于您的子查询对任何过期时间在过去的产品的评估结果为null,因此您可能需要按第二列排序,或者只使用过期日期,而不管它是过去的还是将来的。
编辑3
SELECT
c.category_id,
MIN(name) as name,
c.image /* better as a subquery if you can't do an aggregate on a LOB */
MIN(c.description) as description,
/* don't even need the subquery because you've already joined the product table */
CASE
WHEN MIN(p.date_expires) >= NOW() THEN MIN(p.date_expires)
ELSE NULL /* this may not be what you really wanted */
END as expiring_my_first_guess,
/* what was really intended */
MIN( CASE WHEN p.date_expires >= NOW() THEN p.date_expires ELSE NULL END ) as expiring,
FROM category c
INNER JOIN category_description cd
ON (c.category_id = cd.category_id)
INNER JOIN product_to_category p2c
ON (c.category_id = p2c.category_id)
INNER JOIN JOIN product p
ON (p2c.product_id = p.product_id)
WHERE c.parent_id = 0 AND c.status = '1'
GROUP BY c.category_id
ORDER BY expiring
现在更新答案,数据样本可用
仍然假设它是Microsoft SQL Server,此查询产生以下结果
SELECT c.category_id, minDateExpires.Expiring
FROM category c
LEFT JOIN product_to_category p2c ON (c.category_id = p2c.category_id)
LEFT JOIN product p ON (p2c.product_id = p.product_id)
LEFT JOIN (
SELECT MIN(date_expires) AS expiring, Product_ID FROM product WHERE date_expires >= GETDATE() GROUP BY Product_ID
) minDateExpires on p.product_id = minDateExpires.product_id
ORDER BY expiring
结果:
category_id Expiring
63 NULL
63 NULL
62 NULL
64 NULL
63 2012-07-16 14:12:00.000
63 2012-07-18 08:15:00.000
62 2012-07-26 15:01:00.000
63 2012-08-02 13:04:00.000
63 2012-08-24 00:00:00.000
空列之所以存在,是因为这些产品id没有最小日期>=当前日期/时间。您是否真的超过了按类别id而非产品id分组的最短日期时间?在这种情况下,您的查询可能类似于:
SELECT c.category_id, minDateExpires.Expiring
FROM @category c
LEFT JOIN @product_to_category p2c ON (c.category_id = p2c.category_id)
LEFT JOIN @product p ON (p2c.product_id = p.product_id)
LEFT JOIN (
SELECT MIN(date_expires) AS expiring, Category_ID FROM @product p join @product_to_category p2c on p2c.product_id = p.product_id WHERE date_expires >= GETDATE() GROUP BY Category_id
) minDateExpires on p2c.category_id = minDateExpires.category_id
ORDER BY expiring
这会给你
64 NULL
63 2012-07-16 14:12:00.000
63 2012-07-16 14:12:00.000
63 2012-07-16 14:12:00.000
63 2012-07-16 14:12:00.000
63 2012-07-16 14:12:00.000
63 2012-07-16 14:12:00.000
62 2012-07-26 15:01:00.000
62 2012-07-26 15:01:00.000