仅选择具有最后修改的时间戳记的行,而不选择具有相同ID和较早时间戳记的重复行

时间:2018-07-17 22:36:52

标签: sql firebird

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

订单表 (简单版本)

| 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表中获取与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”行?

3 个答案:

答案 0 :(得分:1)

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

对于每个详细记录,您都需要最新的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
                    );

答案 1 :(得分: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 Server仅执行一次连接,然后重用它,但是它是否遵循此提示-取决于其内部实现。

关于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小提琴(或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中引入的 Window Functions

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

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

答案 2 :(得分:0)

您可以尝试此查询。根据修改后的日期从订单表中获取最上面的行,然后将该行与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