【问题标题】:MySql retrieve products and pricesMySql 检索产品和价格
【发布时间】:2013-06-07 08:28:30
【问题描述】:

我想检索所有产品的列表,以及给定时期的相关价格。

产品表:

Id
Name
Description

价格表:

Id 
Product_id
Name
Amount
Start
End
Duration

这里最重要的一点是,一个产品可以有多个价格,即使是在同一时期,但不是在相同的持续时间。 例如,“2013-06-01 -> 2013-06-08”的价格和“2013-06-01 -> 2013-06-05”的价格

所以我的目标是在给定的时期内检索所有产品的列表,例如按 10 个产品分页,并加入该时期内存在的价格。

这样做的基本方法是:

SElECT *
FROM product
LEFT JOIN prices ON ...
WHERE prices.start >= XXX And prices.end <= YYY
LIMIT 0,10

使用这个简单的解决方案时的问题是,我不能只检索 10 个产品,而是 10 个产品*价格,这在我的情况下是不可接受的。

所以解决办法是:

SElECT *
FROM product
LEFT JOIN prices ON ...
WHERE prices.start >= XXX And prices.end <= YYY
GROUP BY product.id
LIMIT 0,10

但这里的问题是,我只会为每个产品检索“1”个价格。

所以我想知道处理这个问题的最佳方法是什么。 例如,我可以使用组函数,如“group_concat”,并在字段中检索字符串中的所有价格,如“200/300/100”等。这看起来很奇怪,需要在服务器语言端进行工作才能转换为可读信息,但它可以工作。

另一种解决方案是为每个价格使用不同的列,具体取决于持续时间:

SELECT 
   IF( NOT ISNULL(price.start) AND price.duration = 1, price.amount , NULL) AS price_1_day
   ---- same here for all possible durations ---
From ...

我想Thta 也可以(但我不确定这是否可行),但我可能需要创建大约 250 列来涵盖所有可能性。 这是一个安全的选择吗?

任何帮助将不胜感激

【问题讨论】:

  • 由于您仅限于 10 种产品,因此您有 2 个选项:如您所说使用 GROUP_CONCAT 或有 2 个查询,一个用于产品,一个用于价格,并加入客户部分的数据。第二个会给你更多的灵活性
  • 如果使用组连接,请记住默认长度限制为 1024 个字符。除非您有很多很多价格,否则这应该不是问题。如果需要,可以通过 group_concat_max_len 系统变量进行更改。
  • 您想要记录 10 个产品或每个产品最多 10 个记录,我对您的方法有点困惑。
  • 我想要第 10 个产品记录,以及所有相关价格。将我的帖子编辑得更清楚

标签: mysql database database-design


【解决方案1】:

我相信 group_concat 将是最好的方法,因为它的目的是聚合与特定列相关的多条数据。

但是,根据 peterm 的 SQL fiddle 进行调整,如果使用用户定义的变量,这可以在 1 个查询中完成。 (如果忽略设置变量的初始查询)

http://dev.mysql.com/doc/refman/5.7/en/user-variables.html

SET @productTemp := '', @increment := 0;

SElECT
  @increment := if(@productTemp != Product_id, @increment + 1, @increment) AS limiter,
  @productTemp :=Product_id as Product_id,
  Product.name,
  Price.id as Price_id,
  Price.start,
  Price.end
FROM 
  Product
LEFT JOIN
  Price ON Product.Id=Price.Product_id
WHERE 
  `start` >= '2013-05-01' AND `end` <= '2013-05-15'
GROUP BY
 Price_id
HAVING 
  limiter <=2

我们在这里所做的只是增加用户定义的 var "incrementer" 只有当产品 id 与遇到的最后一个不同时。

由于不能在 WHERE 条件中使用别名,我们必须按唯一 ID(在本例中为价格 ID)进行 GROUP,以便我们可以使用 HAVING 减少结果。在这种情况下,我有一个完整的结果集,应该包括 3 个产品 ID,减少到仅显示 2 个。

请注意:这不是我推荐的大型数据集或生产环境中的解决方案。甚至 mysql 手册也强调了用户定义的变量可能会根据优化器采用的路径而表现得有些不规律。但是,我过去曾使用它们对一些内部统计数据产生了很大影响。

小提琴:http://sqlfiddle.com/#!2/96c92/3

【讨论】:

    【解决方案2】:

    没有样本数据和所需的输出很难判断,但您可以尝试这样的方法

    SElECT p.*, q2.*
      FROM 
    (
      SElECT Product_id
        FROM Price
       WHERE `start` >= '2013-05-01' AND `end` <= '2013-05-15'
       GROUP BY Product_id
      LIMIT 0,10
    ) q1 JOIN 
    (
        SELECT *
          FROM Price
         WHERE `start` >= '2013-05-01' AND `end` <= '2013-05-15'
    ) q2 ON q1.Product_id = q2.Product_id JOIN product p
         ON q1.Product_id = p.Id
    

    这里是SQLFiddle演示

    【讨论】:

    • 我也想过这个解决方案,但是用两个等效的查询检索相同的元素是不是有点矫枉过正? MySql 会很好地处理这个问题,比如重用某种缓存吗?我的最终查询要复杂得多(价格部分下有很多连接和条件),并且启动两次这个请求可能会花费我数百毫秒
    • 顺便说一句,我也有“SELECT * FROM product WHERE id IN (SELECT id FROM product LEFT JOIN prices .....”的想法,但我也不确定性能,因为“WHERE IN”部分的查询肯定会尝试扫描所有行
    • @elwood 这当然涉及更多工作,但您的要求也是如此。但要确定它必须在真实数据上进行测试。如果您为所有连接和过滤条件设置了所有索引,恕我直言,它应该可以正常工作。
    • @elwood 这种方法不会为您提供您所要求的结果,因为 Product 表中的前 10 种产品可能没有所需时间间隔的价格。所以你必须从价格表中选择那些。
    • @elwood 答案有帮助吗?您的问题需要更多帮助吗?
    猜你喜欢
    • 2019-08-06
    • 2013-05-18
    • 2021-05-19
    • 2015-04-22
    • 1970-01-01
    • 1970-01-01
    • 2017-12-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多