在 T-SQL 中与逻辑作斗争

Struggling with Logic in T-SQL

我需要一些关于 T-SQL 的帮助。

我不能使用循环、游标等。这是因为我需要高性能。

如果你能帮助我,我将不胜感激。

这是我的问题:

我有一个名为 __tt_Freight_Product 的 table,它有以下列:

dt_reference_date, id_contract, qtt_terminal_loaded

还有一个名为 Product 的 table,它有以下列:

dt_reference_date, id_contract, id_fixing, qtt_fixing, qtt_terminal

它们之间没有外键,但 id_contractdt_reference_date 应该是相同的。

__tt_Freight_Product中的示例数据:

('2015-02-25', '0000006-t12', 200000)
('2015-02-26', '0000006-t12', 200000)
('2015-02-28', '0000006-t12', 100000)

该数据意味着合同“0000006-t12”上的 200,000 吨在“2015-02-25”装载到码头,依此类推

Product中的示例数据:

('2015-02-24', '0000006-t12', 1, 300000, 0)
('2015-02-25', '0000006-t12', 1, 300000, 0)
('2015-02-26', '0000006-t12', 1, 300000, 0)
('2015-02-27', '0000006-t12', 1, 300000, 0)
('2015-02-28', '0000006-t12', 1, 300000, 0)
('2015-02-29', '0000006-t12', 1, 300000, 0)
('2015-02-24', '0000006-t12', 2, 200000, 0)
('2015-02-25', '0000006-t12', 2, 200000, 0)
('2015-02-26', '0000006-t12', 2, 200000, 0)
('2015-02-27', '0000006-t12', 2, 200000, 0)
('2015-02-28', '0000006-t12', 2, 200000, 0)
('2015-02-29', '0000006-t12', 2, 200000, 0)

我需要完成的是根据 dt_reference_date/id_contract/id_fixing__tt_Freight_Product 上的加载卷拆分为 Product table。该拆分卷将在 Product table.

上更新

我们不得不按 "id_fixing".

划分音量

因此,考虑到 __tt_Freight_Product 上的示例数据,我们将更新:

dt_reference_date: '2015-02-24'

25号刚装完货物,不会再更新了

dt_reference_date: '2015-02-25'

qtt_terminal of id_fixing "1" 将更新为 200,000

qtt_terminal of id_fixing “2”不会改变

dt_reference_date: '2015-02-26'

qtt_terminal of id_fixing "1" 将更新为 300,000

qtt_terminal of id_fixing "2" 将更新为 100,000

dt_reference_date: '2015-02-27'

qtt_terminal of id_fixing "1" 将更新为 300,000,因为我们前一天有货物,27 日没有装载货物,或者考虑到我们已经有 id_fixing ' 1' "完全分配"

qtt_terminal of id_fixing "2" 将更新为100,000,因为我们前一天有货,27日没有装货

dt_reference_date: '2015-02-28'

qtt_terminal of id_fixing "1" 将更新为 300,000,因为我们前一天有货物,27 日没有装载货物,或者考虑到我们已经有 id_fixing ' 1' "完全分配"

qtt_terminal of id_fixing "2" 将更新为 200,000

dt_reference_date: '2015-02-29'

qtt_terminal of id_fixing “1” 将更新为 300,000,因为我们前一天有货物,29 日没有装载货物,或者考虑到我们已经有 id_fixing ' 1' "完全分配"

qtt_terminal of id_fixing "2" 将更新为 200,000,因为我们前一天有货物,29 日没有装载货物,或者考虑到我们已经有 id_fixing ' 2' "完全分配"

编辑

我必须获取 __tt_Freight_Product 中的值 [qtt_terminal_loaded] 和 "allocate" 它在 table 产品上的 qtt_terminal 列。但是,我必须按照它们出现的顺序(id_fixing = 1 和 2)将 __tt_Freight_Product.qtt_terminal_loaded 分配到一个或多个 "id_fixing"。

这样,在示例数据中,在 25 日,我们有 qtt_terminal_loaded 等于 200,000。因此,这 200,00 是我能够分配给多个 id_fixing 的金额。但是,id_fixing = 1,"balance" 为 300,00 "to receive" 来自 qtt_terminal_loaded(由列 "qtt_fixing" 决定)。qtt_terminal],我尝试更新的列不能超过 qtt_fixing。永远不会。那样的话,在 26 日,如果我们对之前的 qtt_terminal_loaded 求和,我们将得到 400,00。但是首先id_fixing刚好是30万,所以从那天开始,我开始分配到id_fixing=2,这样的话,那天是10万。

我说清楚了吗?我正在尽力解释。

结束编辑

包含示例输出的图像:

包含 table 个创作和示例数据的脚本:

-- CREATE sample "__tt_Freight_Product"
IF object_id('tempdb..#__tt_Freight_Product') IS NOT NULL
BEGIN
    DROP TABLE #__tt_Freight_Product;
END;

SELECT  a.dt_reference_date,
        a.id_contract,
        a.qtt_terminal_loaded
INTO    #__tt_Freight_Product
FROM    ( VALUES ( '2015-02-25', '0000006-t12', 200000),
        ( '2015-02-26', '0000006-t12', 200000),
        ( '2015-02-28', '0000006-t12', 100000) ) a ( dt_reference_date, id_contract, qtt_terminal_loaded );

-- CREATE sample "Product"
IF object_id('tempdb..#Product') IS NOT NULL
BEGIN
    DROP TABLE #Product;
END;

SELECT  a.dt_reference_date,
        a.id_contract,
        a.id_fixing,
        a.qtt_fixing,
        a.qtt_terminal
INTO    #Product
FROM    ( VALUES ( '2015-02-24', '0000006-t12', 1, 300000, 0),
        ( '2015-02-25', '0000006-t12', 1, 300000, 0),
        ( '2015-02-26', '0000006-t12', 1, 300000, 0),
        ( '2015-02-27', '0000006-t12', 1, 300000, 0),
        ( '2015-02-28', '0000006-t12', 1, 300000, 0),
        ( '2015-02-29', '0000006-t12', 1, 300000, 0),
        ( '2015-02-24', '0000006-t12', 2, 200000, 0),
        ( '2015-02-25', '0000006-t12', 2, 200000, 0),
        ( '2015-02-26', '0000006-t12', 2, 200000, 0),
        ( '2015-02-27', '0000006-t12', 2, 200000, 0),
        ( '2015-02-28', '0000006-t12', 2, 200000, 0),
        ( '2015-02-29', '0000006-t12', 2, 200000, 0) ) a ( dt_reference_date, id_contract, id_fixing, qtt_fixing, qtt_terminal );

有什么想法吗?

您的数据模型中似乎存在一些非规范化。对于相同的 id_contract 和 id_fixing 的所有出现,似乎重复了相同的 qtt_fixing 值,对吗?假设这是有保证的,这里有一个可能的解决方案:

with f as
(
  select *,
    sum(qtt_terminal_loaded) over(partition by id_contract
                                  order by dt_reference_date
                                  rows unbounded preceding) as runsum
  from #__tt_freight_product
),
p as
(
  select *,
    sum(qtt_fixing) over(partition by id_contract, dt_reference_date
                         order by id_fixing
                         rows unbounded preceding) as runsum
  from #product
)
update p
  set qtt_terminal =
    case
      when f.runsum >= p.runsum then p.qtt_fixing
      when p.runsum - p.qtt_fixing < f.runsum then f.runsum - (p.runsum - p.qtt_fixing)
      else 0
    end
from p
  outer apply (select top (1) *
               from f
               where f.id_contract = p.id_contract
                 and f.dt_reference_date <= p.dt_reference_date
               order by f.dt_reference_date desc) as f;

select *
from #product;

请注意,解决方案根本没有优化。我提供它只是为了让您可以研究逻辑。需要更多的步骤来优化它,比如将中间结果存储在临时表中并为它们建立索引。 此外,此解决方案使用带有框架的聚合 window 函数——SQL Server 2012 中引入的一项功能。如果您使用的是旧版本,则最好使用迭代解决方案.

干杯, 伊兹克