我对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实际上是一个时间戳,提供“今天”和“昨天”这两个词只是为了快速传达这个概念)。
我的目标是获取一个简单的结果,其中包含与最新最低价格相关的库存商品,包括对提供所述价格的供应商 ID 的引用。
但是,我发现绊脚石似乎是要处理的语句(或多个语句)的需求数量:
看似简单,但我发现这个问题异常困难。
请注意,我使用的是 Postgres,因此它提供的所有功能都可以使用(即:窗口函数)。
在 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;
详细说明:
选择每个 GROUP BY 组中的第一行?
试试这个
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
也可以使用窗口函数来执行此操作,它将在 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 小提琴演示(谢谢 Erwin)
我相信下面的查询可以使用 Windows 函数进行工作,并且比使用 unique 更快。
select rank() over (partition by p.item_id order by created_at desc, price) as rank, p.item_id,p.vendor_id,p.price,I.title
from prices as p
inner join Items as I
on I.ID = P.Item_ID
where rank = 1