let
Q1是计划数量,q2是交付数量。每当表2(聚合之后)满足计划数量时,我希望表2(C)中的日期与表1(A)中的日期相关联。输出如下:
Table 1:
A | B | Q1
1 1/1 20
2 1/1 10
1 1/7 30
2 1/21 30
1 1/10 15
2 1/30 5
Table 2:
A | C | Q2
1 1/10 10
1 1/12 10
1 1/17 40
2 1/14 10
2 1/23 25
2 1/25 5
因此,关键标准是两个表匹配的数量。
答案 0 :(得分:0)
此查询需要SQL 2012或更高版本。如果使用较低版本的SQL,则需要使用另一个自联接更改sum() over (... order by)
。想法是计算两个表的运行总计并在连接条件中使用它。当第二个表中的总计运行总数等于或高于第一个表中的运行总计时,您需要找到最小日期。
declare @t1 as table (a int, b varchar(10), q1 int)
declare @t2 as table (a int, c varchar(10), q2 int)
insert into @t1
values (1, '1/1', 20), (2, '1/1', 10)
, (1, '1/7', 30), (2, '1/21', 30)
, (1, '1/10', 15), (2, '1/30', 5)
insert into @t2
values (1, '1/10', 10), (1, '1/12', 10)
, (1, '1/17', 45), (2, '1/14', 10)
, (2, '1/23', 25), (2, '1/25', 5)
;with cte1 as (
select
*, ss = sum(q1) over (partition by a order by rn)
from (
select
*, rn = row_number() over (partition by a order by cast(substring(b, 3, len(b)) as int))
from
@t1
) t
)
, cte2 as (
select
*, ss = sum(q2) over (partition by a order by rn)
from (
select
*, rn = row_number() over (partition by a order by cast(substring(c, 3, len(c)) as int))
from
@t2
) t
)
select
t1.a, t1.b, c = min(t2.c), t1.q1
, q2 = case when min(t2.ss) is null then 0 else t1.q1 end
from
cte1 t1
left join cte2 t2 on t1.a = t2.a and t1.ss <= t2.ss
group by t1.a, t1.b, t1.q1, t1.rn, t1.ss
order by t1.a, t1.rn
输出:
a b c q1 q2
--------------------------
1 1/1 1/12 20 20
1 1/7 1/17 30 30
1 1/10 1/17 15 15
2 1/1 1/14 10 10
2 1/21 1/25 30 30
2 1/30 NULL 5 0
编辑:
仅在最后select
更新。此外,您可能希望在rows unbounded preceding
order by
之后添加sum() over (...)
以获得更好的效果
select
t1.a, t1.b, c = min(t2.c), t1.q1
, q2 = t1.q1 - case when min(t2.ss) is null then t1.ss - t3.ss else 0 end
from
cte1 t1
left join cte2 t2 on t1.a = t2.a and t1.ss <= t2.ss
left join (select a, ss = sum(q2) from @t2 group by a) t3 on t1.a = t3.a
group by t1.a, t1.b, t1.q1, t1.rn, t1.ss, t3.ss
order by t1.a, t1.rn
答案 1 :(得分:0)
简单的任务,您需要在预定数量上“传播”交付的数量。
为了实现这一目标,您必须在单个数量的行中转换计划和交货,然后在其交货行上“匹配”每个计划行。
它运作得很好。
我认为送货可以涵盖具有相同A
和C>=B
;with
N as (
select ROW_NUMBER() over (order by o.object_id) n from sys.objects o
),
T1 as (
select *
from (values
(1, '1/01', 20),
(1, '1/07', 30),
(1, '1/10', 15),
(2, '1/01', 10),
(2, '1/21', 30),
(2, '1/30', 5)
) x (A, B, Q1)
),
T2 as (
select *
from (values
(1, '1/10', 10),
(1, '1/12', 10),
(1, '1/17', 40),
(2, '1/14', 10),
(2, '1/23', 25),
(2, '1/25', 5)
) x (A, C, Q2)
),
t1b as (
select *, ROW_NUMBER() over (partition by a order by b, n) x
from t1
join N on t1.Q1>=n.n
),
t2b as (
select *, ROW_NUMBER() over (partition by a order by c, n) x
from t2
join N on t2.Q2>=n.n
),
tx as (
select t1b.A, B, C, Q1, t1b.n Q2, ROW_NUMBER() over (partition by t1b.A, t1b.B order by t1b.n desc) x, q2 q2x
from t1b
inner join t2b on t1b.A = t2b.a and t1b.B <= t2b.C and t1b.x = t2b.x
)
select t1.A, t1.B, C, t1.Q1, isnull(Q2, 0) Q2
from t1
left join tx on t1.A = tx.A and t1.B = tx.B
where isnull(x,1)=1
order by A, B, C
编辑2018-04-04
这只是基本概念,您应该在系统和数据上进行调整。
表N
仅用于pourposes,你需要在数据库中使用性能更高的计数表,我使用它(将其调整到你的数量):
CREATE FUNCTION [dbo].[FN_NUMBERS](
@MAX INT
)
RETURNS @N TABLE (N INT NOT NULL PRIMARY KEY)
BEGIN
WITH
Pass0 as (select '1' as C union all select '1'), --2 rows
Pass1 as (select '1' as C from Pass0 as A, Pass0 as B),--4 rows
Pass2 as (select '1' as C from Pass1 as A, Pass1 as B),--16 rows
Pass3 as (select '1' as C from Pass2 as A, Pass2 as B),--256 rows
Pass4 as (select TOP (@MAX) '1' as C from Pass3 as A, Pass3 as B) --65536 rows
,Tally as (select TOP (@MAX) '1' as C from Pass4 as A, Pass2 as B, Pass1 as C) --4194304 rows
--,Tally as (select TOP (@MAX) '1' as C from Pass4 as A, Pass3 as B) --16777216 rows
--,Tally as (select TOP (@MAX) '1' as C from Pass4 as A, Pass4 as B) --4294836225 rows
INSERT INTO @N
SELECT TOP (@MAX) ROW_NUMBER() OVER(ORDER BY C) AS N
FROM Tally
RETURN
END
使用计数表,您可以将JOIN
和t1b
中的t2b
切换为CROSS APPLY
以获得类似内容:
;with
t1b as (
select *, ROW_NUMBER() over (partition by a order by b, n) x
from Schedules
cross apply FN_NUMBERS(Q1) n
),
t2b as (
select *, ROW_NUMBER() over (partition by a order by c, n) x
from Deliveries
cross apply FN_NUMBERS(Q2) n
),
tx as (
select t1b.A, B, C, Q1, t1b.n Q2, ROW_NUMBER() over (partition by t1b.A, t1b.B order by t1b.n desc) x, q2 q2x
from t1b
inner join t2b on t1b.A = t2b.a and t1b.B <= t2b.C and t1b.x = t2b.x
)
select t1.A, t1.B, C, t1.Q1, isnull(Q2, 0) Q2
from t1
left join tx on t1.A = tx.A and t1.B = tx.B
where isnull(x,1)=1
order by A, B, C
如果您遇到性能问题(通常是关于MemoryGrant),您可以构建一个过程并循环计划并为每个计划执行此查询。 使用较小的数据运行此查询6次比仅对整个数据集执行1次可能更高效。
检查列上是否有正确的索引..
但优化是一个不同的问题。
度过愉快的一天