在 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_contract
和 dt_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 中引入的一项功能。如果您使用的是旧版本,则最好使用迭代解决方案.
干杯,
伊兹克
我需要一些关于 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_contract
和 dt_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 中引入的一项功能。如果您使用的是旧版本,则最好使用迭代解决方案.
干杯, 伊兹克