Select 只有具有最后修改时间戳的行,没有具有相同 ID 和旧时间戳的重复行

Select only rows with last modified timestamp, without duplicate rows with same ID and older timestamp

我找不到解决问题的办法。我有两个 tables Order 和 OrderDetail。

订单table(简易版)

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

OrderDetails(简单版)

| 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 table 获取与订单 table 的最后修改时间戳相关的行?

结果应该是:

| 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.|

我知道加入 tables,并从联合 tables 中获取所有具有所需列的行,但我不知道如何仅过滤那些具有最新时间戳的行order_id、detail_id对。拜托,我们将不胜感激。

编辑

Firebird 数据库需要查询。

编辑 2.

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

您可以试试这个查询。根据修改日期从订单 table 获取最顶层行,然后使用订单详细信息 table.

内部连接该行
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 记录。根据您的数据布局,这相当于最大的 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
                    );
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

这里我们使用了Common Table表达式,所以我们只连接了两个表一次。 至少我们在编写查询时只做了一次——因此我们出现打字错误或复制粘贴错误的机会更少。 我们还提示 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

当您逐步构建查询时,从第一个模糊的想法到特定的行,最好检查输出数据是否完全符合您的期望。 "Big elephant it better to be eaten by small pieces".

在这里,我将向您展示如何逐步构建查询。 如果您在上面链接的 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:最后一步也是保存测试二级查询,并在它们之上编写最终的三级查询,给出最终过滤的数据


更有效的解决方案可以使用 one-运行 连接查询(我在上面 步骤 3. 中介绍并另存为 x 的那个) 添加 order by detail_id, modified desc,然后使用 Firebird 3 中引入的 Window 函数

这是使用该方法对类似问题的回答 -

Window 功能在 Firebird 2.x 中不可用。