【问题标题】:Selecting the most recent, lowest price from multiple vendors for an inventory item从多个供应商中为库存项目选择最新、最低的价格
【发布时间】:2013-08-23 19:37:21
【问题描述】:

我对 SQL 相当精通,但是这个问题让我自己困惑了很长时间。在最基本的意义上,只有两个表:

Items
+----+--------+
| id | title  |
+----+--------+
|  1 | socks  |
|  2 | banana |
|  3 | watch  |
|  4 | box    |
|  5 | shoe   |
+----+--------+

...和价格表:

Prices
+---------+-----------+-------+------------+
| item_id | vendor_id | price | created_at |
+---------+-----------+-------+------------+
|       1 |         1 | 5.99  | Today      |
|       1 |         2 | 4.99  | Today      |
|       2 |         1 | 6.99  | Today      |
|       2 |         2 | 6.99  | Today      |
|       1 |         1 | 3.99  | Yesterday  |
|       1 |         1 | 4.99  | Yesterday  |
|       2 |         1 | 6.99  | Yesterday  |
|       2 |         2 | 6.99  | Yesterday  |
+---------+-----------+-------+------------+

(请注意:created_at 实际上是一个时间戳,提供“今天”和“昨天”这两个词只是为了快速传达这个概念。

我的目标是返回一个简单的结果,其中包含与最新、最低价格相关的库存项目,包括对提供所述价格的 vendor_id 的引用。

但是,我发现绊脚石似乎是要处理的语句(或语句)的要求数量之多:

  • 每件商品都有多个供应商,因此我们需要确定每件商品的所有供应商之间哪个价格最低
  • 商品的新价格会定期附加,因此我们只想考虑每个供应商的每件商品的最新价格
  • 我们希望将所有这些汇总到一个结果中,每行一个项目,其中包括项目、价格和供应商

看起来很简单,但我发现这个问题非常困难。

请注意,我使用的是 Postgres,因此它提供的所有功能都可以使用(即:窗口函数)。

【问题讨论】:

  • “最近的最低价”是什么意思?你想要最低的价格吗?还是最近的?还是两者的某种组合?
  • @GordonLinoff:进一步的解释进一步阐明了这一点。

标签: mysql sql postgresql greatest-n-per-group distinct-on


【解决方案1】:

在 Postgres 中使用 DISTINCT ON 更简单:

每个供应商每件商品的当前价格

SELECT DISTINCT ON (p.item_id, p.vendor_id)
       i.title, p.price, p.vendor_id
FROM   prices p
JOIN   items  i ON i.id = p.item_id
ORDER  BY p.item_id, p.vendor_id, p.created_at DESC;

每个项目的最佳供应商

SELECT DISTINCT ON (item_id) 
       i.title, p.price, p.vendor_id -- add more columns as you need
FROM (
   SELECT DISTINCT ON (item_id, vendor_id)
          item_id, price, vendor_id -- add more columns as you need
   FROM   prices p
   ORDER  BY item_id, vendor_id, created_at DESC
   ) p
JOIN   items i ON i.id = p.item_id
ORDER  BY item_id, price;

->SQLfiddle demo

详解:
Select first row in each GROUP BY group?

【讨论】:

  • 这显示了每个供应商的当前价格。我相信 OP 也想知道每个项目的最佳供应商。这可以通过将答案转换为子查询然后执行SELECT DISTINCT ON(item_id) * FROM the_subquery ORDER BY price 来完成,是吗?
  • OMG SQLFiddle 是一个东西!无论如何,安德鲁是对的,我不想要袜子的多个结果,每个项目只有一个结果。
  • @AndrewLazarus:是的,第二步不见了。补充说。不过,ORDER BY 需要匹配 DISTINCT ON。所以我们需要ORDER BY item_id, price
  • @ErwinBrandstetter:甚至荷​​马点头。
【解决方案2】:

试试这个

CREATE TABLE #Prices ( Iid INT, Vid INT, Price Money, Created DateTime)
INSERT INTO #Prices 
SELECT 1, 1, 5.99 ,GETDATE()    UNION
SELECT 1, 2, 4.99 ,GETDATE()    UNION
SELECT 2, 1, 6.99 ,GETDATE()    UNION
SELECT 2, 2, 6.99 ,GETDATE()    UNION
SELECT 1, 1, 3.99 ,GETDATE()-1  UNION
SELECT 1, 2, 4.99 ,GETDATE()-1  UNION
SELECT 2, 1, 6.99 ,GETDATE()-1  UNION
SELECT 2, 2, 6.99 ,GETDATE()-1 

WITH CTE AS
(
    SELECT 
        MyPriority = ROW_NUMBER() OVER ( partition by Iid, Vid ORDER BY Created DESC, Price ASC) 
    ,   Iid
    ,   Vid
    ,   price
    ,   Created
    FROM #Prices 
)

SELECT * FROM CTE WHERE MyPriority = 1

【讨论】:

  • 非常接近 - 但是,这会导致为每个 vendor_id 返回一个 item_id。将其归结为单个 item_id 的最佳方法是什么?
【解决方案3】:

也可以使用窗口函数执行此操作,它适用于 SQL Server 版本 > 2005:

with cte1 as (
    select
        *,
        row_number() over(partition by vendor_id, item_id order by created_at desc) as row_num
    from prices
), cte2 as (
    select
        *,
        row_number() over(partition by item_id order by price asc) as row_num2
    from cte1
    where row_num = 1
)
select i.title, c.price, c.vendor_id
from cte2 as c
    inner join items as i on i.id = c.item_id
where c.row_num2 = 1;

sql fiddle demo(感谢欧文)

【讨论】:

    猜你喜欢
    • 2016-05-28
    • 1970-01-01
    • 2020-08-03
    • 1970-01-01
    • 2013-01-14
    • 2016-04-25
    • 2018-12-07
    • 2021-05-03
    • 2018-03-16
    相关资源
    最近更新 更多