MS SQL:如何在多个用户的另一个日期(付款日期)之前获取最新日期(billdate)?

问题描述:

示例表如下:MS SQL:如何在多个用户的另一个日期(付款日期)之前获取最新日期(billdate)?

表1

userid | billdate  
user1 | 2017-06-01 
user1 | 2017-05-01 
user1 | 2017-04-01 
user2 | 2017-06-01 
user2 | 2017-05-01 

表2

userid | paymentdate 
user1 | 2017-06-01 
user1 | 2017-06-02 
user1 | 2017-05-02 
user2 | 2017-06-30 
user2 | 2017-05-30 

希望的输出提前

userid | billdate  | paymentdate 
user1 | 2017-05-01 | 2017-06-01 
user1 | 2017-06-01 | 2017-06-02 
user1 | 2017-05-01 | 2017-05-02 
user2 | 2017-06-01 | 2017-06-30 
user2 | 2017-05-01 | 2017-05-30 

由于

增加了一个场景:如果同一月份有多个付款,该怎么办?并且对于每个付款日期,先前的帐单日期应该在期望的输出中

+0

如果'user1'在'表2'中有三条记录怎么办? –

+0

我认为您应该将您的付款与帐单关联起来。如果我没有收到账单,我永远不会付钱给你,因此链接应该始终存在。所以基本上你必须在付款表中添加一列“bill_ID”。如果一笔交易支付多笔账单,这一笔将会有麻烦。您可能需要在前端应用程序 – Luke

+0

上处理该问题。但假设user1只有这些记录。 –

如果你只想返回那里是一个BillPayment记录的记录,那么我们首先需要作出一些假设 - 即不会有任何两个Bill付款之间有两个Payment记录,或在同一天支付两笔Bill付款。

假设这一切,你可以使用cross apply获得最新Payment记录每个Bill记录,不返回任何东西在没有Payment记录可供选择:

declare @Bills table(userid nvarchar(10),billdate date); 
insert into @Bills values ('user1','2017-06-01'),('user1','2017-05-01'),('user1','2017-04-01'),('user2','2017-06-01'),('user2','2017-05-01'); 

declare @Payments table(userid nvarchar(10),paymentdate date); 
insert into @Payments values ('user1','2017-06-01'),('user1','2017-06-02'),('user1','2017-05-02'),('user2','2017-06-30'),('user2','2017-05-30'); 

select p.userid 
     ,ba.billdate 
     ,p.paymentdate 
from @Payments p 
    cross apply(select top 1 billdate 
       from @Bills b 
       where p.userid = b.userid 
        and b.billdate < p.paymentdate 
       order by b.billdate desc 
       ) ba 
order by p.userid 
     ,ba.billdate desc 
     ,p.paymentdate desc; 

输出:

+--------+------------+-------------+ 
| userid | billdate | paymentdate | 
+--------+------------+-------------+ 
| user1 | 2017-06-01 | 2017-06-02 | 
| user1 | 2017-05-01 | 2017-06-01 | 
| user1 | 2017-05-01 | 2017-05-02 | 
| user2 | 2017-06-01 | 2017-06-30 | 
| user2 | 2017-05-01 | 2017-05-30 | 
+--------+------------+-------------+ 
+0

感谢您的详细回复。如果在我的现实生活场景中存在多笔付款,那么该怎么办。 :) –

+0

@HopetoChristianChophi正在更新我的答案,就像你正在写评论:) – iamdave

+2

似乎工作..只是确认与我的活的数据库...一些活的情况可能已经在示例场景中被忽略...将更新如果我能找到它,或者在确认后将其标记为答案。请给我一些时间。再次感谢 –

您似乎想要加入表格,但没有密钥。 row_number()可以提供关键:

select t1.*, t2.paymentdate 
from (select t1.*, 
      row_number() over (partition by userid order by billdate) as seqnum 
     from t1 
    ) t1 join 
    (select t2.*, 
      row_number() over (partition by userid order by paymentdate) as seqnum 
     from t2 
    ) t2 
    on t1.userid = t2.userid and t1.seqnum = t2.seqnum; 

你需要从Table01所有行,然后得到的只有一行从TABLE02升序排列。 Table02行通过用户标识和日期连接(billdate < = paymentdate)。顶部(1)的外部应用将会诀窍。

drop table if exists dbo.Table01; 
drop table if exists dbo.Table02; 

create table dbo.Table01 (
    userid varchar(100) 
    , billdate date 
); 

create table dbo.Table02 (
    userid varchar(100) 
    , paymentdate date 
); 

insert into dbo.Table01 (userid, billdate) 
values ('user1', '2017-06-01'), ('user1', '2017-05-01') 
    , ('user2', '2017-06-01'), ('user2', '2017-05-01'); 

insert into dbo.Table02 (userid, paymentdate) 
values ('user1', '2017-06-02'), ('user1', '2017-05-02') 
    , ('user2', '2017-06-30'), ('user2', '2017-05-30'); 


select 
* 
from dbo.Table01 t1 
    outer apply (
      select 
       top (1) 
       t2.paymentdate 
      from dbo.Table02 t2 
      where t1.userid = t2.userid 
       and t1.billdate <= t2.paymentdate 
      order by t2.paymentdate asc 
    ) tt 
+2

虽然这个脚本看起来像它的实际工作,但代码只有答案一般不赞成。你需要解释你在做什么,为什么。 – iamdave

+2

确实,只需将代码写入答案将使*看起来像它最初不想要的编码服务。工作代码很好,但理解它是不可或缺的。 – Luke

+0

@iamdave当然,我编辑了答案。 –

有两种可能的解决方案:

首先

合并表为一体。这将使哪些日期属于哪个账单和哪个用户更清楚。因此,所需的输出是您正在使用的表格。如果用户已被记帐但尚未付款。付款日期是NULL

添加主键到table 1和表2中使用它作为外键,像这样:

bill_id | userid |s billdate 1 | user1 |s 2017-06-01 2 | user1 |s 2017-05-01 3 | user2 |s 2017-06-01 4 | user2 |s 2017-05-01

表2

bill_id | userid |s paymentdate 1 | user1 |s 2017-06-02 2 | user1 |s 2017-05-02 3 | user2 |s 2017-06-30 4 | user2 |s 2017-05-30

现在你可以轻松获得连接:

SELECT table1.*, table2.paymentdate FROM table1 LEFT JOIN table2 ON table1.bill_id = table2.bill_id;

+0

由于一个账单可能发生多次交易,第一种解决方案在实践中将无法正常工作,第二种解决方案几乎是我的评论的复制粘贴,并且由于OP需要提取数据而不是建立新数据,因此没有解决方案。 – Luke

+0

如果意图是多次付款,则无法绕过外键表。并抱歉复制你。没有看到您的评论。 – mamazu

+1

只需多次付款,您只需要FK栏。您可以将多个付款行链接到一个帐单行。您只需要一张额外的表格即可为多次付款提供多次付款,这在reallife中是一种可能的情况。 – Luke