【问题标题】:Build SQL query with JOIN and limits使用 JOIN 和限制构建 SQL 查询
【发布时间】:2017-08-18 16:23:20
【问题描述】:

请帮助我构建 PostgreSQL 查询。 有2个表:products(id, title)和price(id, product_id, price_type, moment, value)

moment - 时间戳,可以是过去也可以是未来

假设 price_type 只有两个选项:retailpurchase

但是一种产品可能在不同的时刻有很多零售价。

我需要选择所有具有实际零售和购买价格的产品,比现在少。

我能做到

SELECT 
      products.id, 
      products.title_translations AS title,
      retail_prices.moment AS ret_moment,
      pur_prices.value AS purchase,
      retail_prices.value AS retail
FROM products 
LEFT OUTER JOIN prices AS pur_prices ON products.id=pur_prices.product_id AND pur_prices.price_type='purchase' AND pur_prices.moment<current_timestamp
LEFT OUTER JOIN prices AS retail_prices ON products.id=retail_prices.product_id AND retail_prices.price_type='retail' AND retail_prices.moment<current_timestamp
ORDER BY products.id;

它有效,但返回 具有所有价格的产品,但我只需要最后的价格(按时刻)。

【问题讨论】:

  • 你的意思是现在的价格最近?
  • 所以零售价和购买价应该在同一条线上,但可能有不同的“时刻”,对吧?
  • 是的,在结果时刻没有问题

标签: sql postgresql join left-join


【解决方案1】:

只需使用ROW_NUMBER 查找当前时间之前的最后价格是多少

with last_prices as (
    SELECT 
          products.id, 
          products.title_translations AS title,
          prices.moment,
          prices.value,
          prices.price_type,
          ROW_NUMBER() OVER (PARTITION BY product_id, price_type 
                             ORDER BY moment DESC) as rn
    FROM products 
    LEFT JOIN prices
      ON products.id = prices.product_id 
    WHERE moment < now() 
  )
SELECT id, title, 
       MAX(CASE WHEN price_type = 'retail'
                THEN moment
           END) as retail_moment,
       MAX(CASE WHEN price_type = 'retail'
                THEN value
           END) as retail_price,
       MAX(CASE WHEN price_type = 'purchase'
                THEN moment
           END) as purchase_moment,
       MAX(CASE WHEN price_type = 'purchase'
                THEN value
           END) as purchase_price
FROM last_prices     
WHERE rn = 1
GROUP BY id, title
ORDER BY id

【讨论】:

  • 选择仍然显示 retail_prices.xxxx 并且您不再拥有该表/别名
  • 对此没有异议。但是 select....我认为你遗漏了一个连接或其他东西。
  • @xQbert 已修复。谢谢
  • 现在我会担心,如果购买和零售有不同的“时刻”......别介意你也通过价格类型分区来处理它......看起来它应该可以工作:P
【解决方案2】:

为了让事情井井有条,我会使用 CTE 生成两个价格数据子集,一个用于购买,一个用于零售,并按升序分配一个行号,最小的数字表示最近的时刻小于当前时间图。然后当我们加入这些 ctes 时,我们只返回分配的最低数字。

With Pur_prices as (SELECT P.*, row_Number() over (partition by product_ID order by moment desc) RN
                    FROM prices P 
                    WHERE price_Type = 'purchase'
                    and p.moment < current_timestamp)

, Retail_prices as (SELECT P.*, row_Number() over (partition by product_ID order by moment desc) RN
                    FROM prices P 
                    WHERE price_Type = 'retail'
                    and p.moment < current_timestamp)

SELECT 
      p.id, 
      p.title_translations AS title,
      rp.moment AS ret_moment,
      rp.value AS retail,
      pp.moment AS Pur_moment,
      pp.value AS purchase
FROM products p
LEFT JOIN pur_prices pp
  ON p.id=pp.product_id 
 AND pp.RN = 1  --Only show the most recent price less than current time
LEFT JOIN retail_prices rp
  ON p.id=rp.product_id 
 AND RP.RN = 1 --Only show the most recent price less than current time
ORDER BY p.id;

最终结果应该是所有产品,无论它们有零售价还是购买价;但如果他们确实显示了之前最近时刻的零售/购买价格。我唯一担心的是这意味着所有定价都有一个开始的时刻(不允许空值!)

【讨论】:

    【解决方案3】:

    您可能希望它按时刻降序排列。 改变 ORDER BY products.id;ORDER BY product.id ASC, moment DESC;

    【讨论】:

    • 涉及的内容不多,因为涉及到一个类型字段。他们希望对同一行结果进行报复/购买。
    猜你喜欢
    • 2010-09-14
    • 1970-01-01
    • 2016-09-29
    • 1970-01-01
    • 2014-04-19
    • 1970-01-01
    • 2016-09-19
    • 2010-10-04
    • 1970-01-01
    相关资源
    最近更新 更多