Mysql 为库存项目从多个供应商中选择最近的最低价格

Mysql 为库存项目从多个供应商中选择最近的最低价格,mysql,sql,postgresql,greatest-n-per-group,distinct-on,Mysql,Sql,Postgresql,Greatest N Per Group,Distinct On,我相当精通SQL,但是这个问题已经困扰了我好一阵子了。从最基本的意义上讲,只有两个表: Items +----+--------+ | id | title | +----+--------+ | 1 | socks | | 2 | banana | | 3 | watch | | 4 | box | | 5 | shoe | +----+--------+ …以及价格表: Prices +---------+-----------+-------+---------

我相当精通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  |
+---------+-----------+-------+------------+
请注意:创建时间实际上是一个时间戳,提供“今天”和“昨天”只是为了快速传达概念

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

然而,我发现阻碍因素似乎是一个或多个语句需要处理的需求数量:

每个项目都有多个供应商,因此我们需要确定每个项目的所有供应商之间的价格最低 项目的新价格定期被追加,因此我们只考虑每个供应商的每个项目的最新价格。 我们希望将所有这些汇总到一个结果中,每行一个项目,包括项目、价格和供应商 这看起来很简单,但我发现这个问题异常困难

请注意,我使用的是Postgres,因此它提供的所有功能都可以使用,例如:窗口功能。

试试这个

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
试试这个

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
在Postgres中使用DISTINCT ON更简单:

每个供应商的每项当前价格 每个项目的最佳供应商 详细说明:

在Postgres中使用DISTINCT ON更简单:

每个供应商的每项当前价格 每个项目的最佳供应商 详细说明:

也可以使用窗口函数执行此操作,它将在SQL Server version>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;

感谢Erwin,也可以使用窗口函数执行此操作,它将在SQL Server version>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;

感谢Erwin

非常接近-但是,这会导致为每个供应商id返回一个项目id。将其归结为单个项目id的最佳方法是什么?非常接近-但是,这将导致为每个供应商返回一个商品标识。将其还原为单个商品标识的最佳方式是什么?您认为最近的最低价格是什么意思?你想要最低的价格吗?还是最近的?或者两者的某种结合?@GordonLinoff:进一步的解释澄清了这一点。你说的最近的最低价格是什么意思?你想要最低的价格吗?还是最近的?还是两者的某种结合?@GordonLinoff:进一步解释清楚了这一点。这显示了每个供应商的当前价格。我相信OP还想知道每个项目的最佳供应商。这可以通过将答案转换成子查询,然后按价格从子查询顺序中选择不同的ONitem\u id*来实现,是吗?OMG SQLFIDLE是一个东西!不管怎样,安德鲁是对的,我不想要袜子的多个结果,每个项目只有一个结果。@AndrewLazarus:对,第二步不见了。他补充道。不过,ORDER BY需要与DISTINCT ON匹配。所以我们需要按商品编号、价格订购。@ErwinBrandstetter:甚至荷马点头。这显示了每个供应商的当前价格。我相信OP还想知道每个项目的最佳供应商。这可以通过将答案转换成子查询,然后按价格从子查询顺序中选择不同的ONitem\u id*来实现,是吗?OMG SQLFIDLE是一个东西!不管怎样,安德鲁是对的,我不想要袜子的多个结果,每个项目只有一个结果。@AndrewLazarus:对,第二步不见了。他补充道。不过,ORDER BY需要与DISTINCT ON匹配。所以我们需要按商品编号、价格订购。@ErwinBrandstetter:连荷马也点头。