【问题标题】:Select only rows with last modified timestamp, without duplicate rows with same ID and older timestamp仅选择具有最后修改时间戳的行,而不选择具有相同 ID 和旧时间戳的重复行
【发布时间】:2018-07-19 05:18:08
【问题描述】:

我找不到我的问题的解决方案。我有两张表 Order 和 OrderDetail。

订单表 (简单版)

| ID | modified  |
| 1  | 7.1.2018. |
| 2  | 10.1.2018.|
| 3  | 15.1.2018.|
| 4  | 20.1.2018.|
| 5  | 25.1.2018.|

订单详情(简单版)

| order_id | detail_id | base_price | buy_price | sell_price|
| 1        | 1         | 99.00      | 111.00    | 122.00    |
| 1        | 2         | 82.00      | 95.00     | 117.00    | 
| 1        | 3         | 82.00      | 95.00     | 117.00    |
| 2        | 4         | 95.00      | 108.00    | 119.00    | 
| 2        | 5         | 86.00      | 94.00     | 115.00    | 
| 2        | 1         | 82.00      | 95.00     | 117.00    |
| 3        | 1         | 92.00      | 106.00    | 116.00    | 
| 3        | 4         | 90.00      | 100.00    | 120.00    | 
| 3        | 5         | 82.00      | 95.00     | 117.00    |
| 4        | 2         | 92.00      | 106.00    | 116.00    | 
| 4        | 3         | 90.00      | 100.00    | 120.00    | 
| 4        | 1         | 82.00      | 95.00     | 117.00    |
| 5        | 1         | 92.00      | 106.00    | 116.00    | 
| 5        | 5         | 90.00      | 100.00    | 120.00    | 
| 5        | 3         | 82.00      | 95.00     | 117.00    |

如何从 OrderDetails 表中获取与 Order 表中最后修改的时间戳相关联的行?

结果应该是:

| order_id | detail_id | base_price | buy_price | sell_price | modified  |
| 5        | 1         | 92.00      | 106.00    | 116.00     | 25.1.2018.|
| 4        | 2         | 92.00      | 106.00    | 116.00     | 20.1.2018.|
| 5        | 3         | 82.00      | 95.00     | 117.00     | 25.1.2018.|
| 3        | 4         | 90.00      | 100.00    | 120.00     | 15.1.2018.|
| 5        | 5         | 90.00      | 100.00    | 120.00     | 25.1.2018.|

我知道连接表,并从具有所需列的联合表中获取所有行,但我不知道如何从每个 order_id, detail_id 对中仅过滤具有最新时间戳的行。请,任何帮助将不胜感激。

编辑

Firebird 数据库需要查询。

编辑 2。

第一个样本数据在某种程度上具有误导性。请再次查看扩展表和理想结果。 我需要所有不同的行(基于“details_id”)及其最后修改的数据。如何用旧时间戳排除每个“detail_id”的“重复”行,只保留最新时间戳的“detail_id”行???

【问题讨论】:

  • 您不想要 Order_ID 1 和 3,因为它们的时间戳比 Order_ID 2 早?
  • @AmithKumar 是的,理解他真正想要的并不容易。阅读他的“编辑 2”部分,就像您从未阅读过问题的先前部分一样。然后它就变得有意义了。
  • 更有效的解决方案是使用带有order by detail_id, modified desc 的一次性查询并使用Firebird 3 中引入的窗口功能。但我没有心情再写一个答案。这是使用 FB3 W.Fs 对类似问题的答案。 - stackoverflow.com/a/48325694/976391
  • 您使用的是哪个版本的 Firebird?

标签: sql firebird


【解决方案1】:
with x as (select o.modified, od.* 
           from orderDetails od, orders o
           where o.id=od.order_id)
 , mx as (select max(modified) as modified, detail_id
          from x group by detail_id)
Select x.* from x, mx
Where x.detail_id = mx.detail_id and x.modified=mx.modified

这里我们使用公用表表达式,所以我们只连接两个表一次。 至少我们在编写查询时只做了一次——因此我们犯错字或复制粘贴错误的机会就更少了。 我们还提示 SQL 服务器只执行一次连接,然后重用它,但它是否遵循此提示 - 取决于其内部实现。

CTE 的另一个好处:它可以帮助您逐步构建查询,从简单到复杂。在 https://en.wikipedia.org/wiki/REPL
阅读有关 Read–eval–print 循环的信息 我稍后会添加更多。

您可以在 Google 中找到许多关于 CTE 的文章。 Firebird 的实现记录在这里:https://www.firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-dml-select.html#fblangref25-dml-select-cte

由于我只使用了非常基本的 SQL,我相信它几乎可以在任何实用的 SQL 服务器中工作,包括 Firebird。

这里是查询结果和输出数据:SQL Fiddle

PostgreSQL 9.6 架构设置

create table orders
 (id integer primary key,
  modified timestamp);
create index o_m on orders(modified);  

create table OrderDetails(
  order_id integer references orders(id),
  detail_id integer not null,
  base_price float,
  buy_price float,
  sell_price float );
create index od_do on OrderDetails(detail_id, order_id);

Insert into orders values
( 1, '2018-1-07'),
( 2, '2018-1-10'),
( 3, '2018-1-15'),
( 4, '2018-1-20'),
( 5, '2018-1-25');

Insert into OrderDetails values
(   1   ,   1   ,   99.00   ,   111.00  ,   122.00  ),
(   1   ,   2   ,   82.00   ,   95.00   ,   117.00  ),
(   1   ,   3   ,   82.00   ,   95.00   ,   117.00  ),
(   2   ,   4   ,   95.00   ,   108.00  ,   119.00  ),
(   2   ,   5   ,   86.00   ,   94.00   ,   115.00  ),
(   2   ,   1   ,   82.00   ,   95.00   ,   117.00  ),
(   3   ,   1   ,   92.00   ,   106.00  ,   116.00  ),
(   3   ,   4   ,   90.00   ,   100.00  ,   120.00  ),
(   3   ,   5   ,   82.00   ,   95.00   ,   117.00  ),
(   4   ,   2   ,   92.00   ,   106.00  ,   116.00  ),
(   4   ,   3   ,   90.00   ,   100.00  ,   120.00  ),
(   4   ,   1   ,   82.00   ,   95.00   ,   117.00  ),
(   5   ,   1   ,   92.00   ,   106.00  ,   116.00  ),
(   5   ,   5   ,   90.00   ,   100.00  ,   120.00  ),
(   5   ,   3   ,   82.00   ,   95.00   ,   117.00  );

查询 1

with x as (select o.modified, od.* 
           from orderDetails od, orders o
           where o.id=od.order_id)
 , mx as (select max(modified) as modified, detail_id
          from x group by detail_id)
Select x.* from x, mx
Where x.detail_id = mx.detail_id and x.modified=mx.modified
Order by detail_id

Results

|             modified | order_id | detail_id | base_price | buy_price | sell_price |
|----------------------|----------|-----------|------------|-----------|------------|
| 2018-01-25T00:00:00Z |        5 |         1 |         92 |       106 |        116 |
| 2018-01-20T00:00:00Z |        4 |         2 |         92 |       106 |        116 |
| 2018-01-25T00:00:00Z |        5 |         3 |         82 |        95 |        117 |
| 2018-01-15T00:00:00Z |        3 |         4 |         90 |       100 |        120 |
| 2018-01-25T00:00:00Z |        5 |         5 |         90 |       100 |        120 |

请注意,如果您有两个或多个具有相同时间戳的订单,则会有不同的输出!似乎您甚至没有想过这种可能性——但既然有可能,它最终会发生。

现在,回到 CTE 和 REPL

随着您逐步构建查询,从第一个模糊的想法到特定的行,最好检查一下输出数据是否正是您所期望的。 “大象还是小块吃好”。

在这里,我将向您展示如何逐步构建查询。 如果您在上面链接的 SQL Fiddle 中重复这些步骤,将会很有用。

首先我创建并填充了表格。

然后我发出第一个查询只是为了检查我是否正确填充了它们。

1:select * from orders - 在 SQL fiddle(或 IBExpert、FlameRobin 等)中尝试这个和进一步的查询

2:select * from orderDetails

3:然后我发出了连接查询来检查我的跨表查询是否真的给出了有意义的输出。确实如此。

select o.modified, od.* 
from orderDetails od, orders o
where o.id=od.order_id

4:然后我想知道,我可以从该查询中获取详细信息的最后一个时间戳吗?为了检查它,我做了以下事情:1)保存了我之前做过并测试过的上述查询,2)在它上面写了一个辅助查询。它确实提取了最后的更改日期。编写和测试。

with x as (select o.modified, od.* 
           from orderDetails od, orders o
           where o.id=od.order_id)
Select max(modified) as modified, detail_id
  from x group by detail_id

5:最后一步也是保存测试二级查询,并在它们之上编写最终的三级查询,给出最终过滤的数据


更有效的解决方案是使用一次性连接查询(我在上面的步骤 3. 中介绍并保存为x)添加order by detail_id, modified desc,然后使用Firebird 3 中引入的窗口函数

这是使用该方法的类似问题的答案 - Firebird select from table distinct one field

不过,Firebird 2.x 中不提供窗口函数。

【讨论】:

【解决方案2】:

这解决了问题的前两个版本。

对于每个详细记录,您需要最近的order 记录。当你的数据被布局时,这相当于最大的order_id。使用它比使用日期更简单:

select od.*
from orderdetail od
where od.order_id = (select max(od2.order_id)
                     from orderdetail od2
                     where od2.detail_id = od.detail_id
                    );

【讨论】:

  • 查看他的“编辑 2” - 他想要“按 detail_id 分组”,而不是选择最后一个订单。尽管他在“编辑 2”之前所说的一切确实是“我想要最后一个订单,而不是其他订单”。
  • 我想知道是否使用 Vinit 的答案想法(选择第一个 1 .... order by modified desc)您可以扩展您的答案以检查最新的修改而不是 maximim id(而您的假设可能会如果订单进入数据库后没有修改,则保留,我们实际上不确定)
【解决方案3】:

你可以试试这个查询。根据修改后的日期从订单表中获取最上面的行,然后将该行与 orderdetails 表进行内部连接。

SELECT od.*, o.modified 
FROM OrderDetails od
Inner join (Select top 1 * -- get topmost row
           from [Order] 
           order by modified desc ) O on o.id = od.order_id

【讨论】:

  • 这将只返回所有表中的一行。我需要具有“order_id,details_Id”密钥对的唯一行。
  • 它将根据您共享的数据返回 2 行。子查询O 将返回 id 为 '2' 的一行,并且我们使用带有订单详细信息的内部连接,其中 order_id '2' 有 2 行,因此您应该得到 2 个唯一行。如果它仍然不起作用,请告诉我
  • 这是一些示例数据。每个表都有数千行。您的子查询只会从 Order 表中返回 1(前 1)行。
  • Vinit,我已经尝试过,您的解决方案,但结果在所有情况下都只有一行。无论如何感谢您的帮助。
  • 您能否分享您的查询版本,我可能能够理解您想要实现的目标并修复查询
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-06
  • 2013-01-24
  • 1970-01-01
  • 2021-10-31
相关资源
最近更新 更多