根据日期和数量加入2个表

时间:2018-03-12 19:02:16

标签: sql-server join

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

因此,关键标准是两个表匹配的数量。

2 个答案:

答案 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)

简单的任务,您需要在预定数量上“传播”交付的数量。

为了实现这一目标,您必须在单个数量的行中转换计划和交货,然后在其交货行上“匹配”每个计划行。

它运作得很好。

我认为送货可以涵盖具有相同AC>=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

使用计数表,您可以将JOINt1b中的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次可能更高效。

检查列上是否有正确的索引..

但优化是一个不同的问题。

度过愉快的一天