If the query I want to join on returns:
如果我想在返回时加入查询:
IDApplication ContactDate CInfo
1 01/06/2016 pie
1 10/01/2016 cake
1 03/02/2015 banana
2 03/06/2016 cake
2 23/12/2015 apple
IDApplication ReplyDate RInfo
1 30/05/2016 circle
1 03/05/2016 square
1 04/02/2015 triangle
1 14/01/2016 pentagon
2 04/06/2016 square
2 01/02/2016 pentagon
2 10/06/2016 circle
I need this to be returned:
我需要归还这个:
IDApplication ContactDate CInfo ReplyDate RInfo
1 01/06/2016 pie NULL NULL
1 10/01/2016 cake 30/05/2016 circle
1 03/02/2015 banana 04/02/2015 triangle
2 03/06/2016 cake 10/06/2016 square
2 23/12/2015 apple 01/02/2016 pentagon
I need it to return the second table/query info where its date is larger than any corresponding application date in the first table, but which isn't larger than any following date in the first table.
我需要它返回第二个表/查询信息,其中它的日期大于第一个表中任何相应的应用程序日期,但不大于第一个表中的任何后续日期。
So for the first record above, it is NULL because there is no reply info with a date after 01/06/2016 in the reply table (so no reply has been made), but the second record has the reply date of 30/05/2016 as that is the largest reply date for that application. More importantly for the 5th record the reply date is 01/02/2016, this date is larger than the contact date but not THE largest reply date for application 2 which is 10/06/2016, but as another contact date for application 2 is in between these 2 it needs to show the largest date before the following contact date.
所以上面第一个记录是NULL,因为回复表中没有回复信息,日期是2016年06月01日之后(所以没有回复),但是第二个记录的回复日期是2016年05月30日,这是该申请最大的回复日期。更重要的是为第五记录01/02/2016回复日期,这个日期大于接触日期但不是最大的答复日期为应用2 10/06/2016,但随着应用程序的另一个联系日期2是在这两个之间需要显示以下联系日期之前最大的日期。
This is logic which is making my brain hurt.
这是使我的大脑受伤的逻辑。
I've gotten as far as joining the second query on where reply date is larger than the contact date, but that causes it to make rows appear for all dates larger.
我已经加入了第二个查询,该查询的回复日期大于联系人日期,但这会导致所有日期的行都显示得更大。
I need to join on the record with a date larger than the base tables date but of these the largest where it isn't larger than the next largest base table date.
我需要在记录中加入一个大于基表日期的日期,但是在这些最大的日期中,它不大于下一个最大的基表日期。
4 个解决方案
#1
2
My approach to problems like this is typically to break them into small steps, each of which I can implement as a CTE, so I get something that's pretty easy to read through and understand. I can always try to collapse it into fewer steps later if need be. Here's a possible solution. See the comments for an explanation of how it works.
我处理此类问题的方法通常是将它们分解成小步骤,每个步骤都可以实现为CTE,因此我得到了一些非常容易阅读和理解的东西。如果需要的话,我可以尝试把它分解成更少的步骤。这是一个可能的解决方案。有关它如何工作的解释,请参阅评论。
--------------------------------------------------------------------------------
-- Set up the sample data from the question.
--------------------------------------------------------------------------------
declare @Contact table (IDApplication int, ContactDate date, CInfo varchar(32));
declare @Reply table (IDApplication int, ReplyDate date, RInfo varchar(32));
insert @Contact values
(1, '2016-06-01',' pie'),
(1, '2016-01-10', 'cake'),
(1, '2015-02-03', 'banana'),
(2, '2016-06-03', 'cake'),
(2, '2015-12-23', 'apple');
insert @Reply values
(1, '2016-05-30', 'circle'),
(1, '2016-05-03', 'square'),
(1, '2015-02-04', 'triangle'),
(1, '2016-01-14', 'pentagon'),
(2, '2016-06-04', 'square'),
(2, '2016-02-01', 'pentagon'),
(2, '2016-06-10', 'circle');
--------------------------------------------------------------------------------
-- Step 1: Sequence each group of contacts by contact date.
--------------------------------------------------------------------------------
with OrderedContactCTE as
(
select
*,
[Sequence] = row_number() over (partition by IDApplication order by ContactDate)
from
@Contact
),
--------------------------------------------------------------------------------
-- Step 2: Match each contact with the subsequent contact (where one exists)
-- having the same IDApplication value. The date of the subsequent
-- contact will act as the upper bound on reply dates that are valid for
-- the original contact. Assign each contact a unique identifier that
-- we'll use in the following step.
--------------------------------------------------------------------------------
PairedContactCTE as
(
select
UniqueID = row_number() over (order by Contact.IDApplication, Contact.[Sequence]),
Contact.IDApplication,
Contact.ContactDate,
Contact.CInfo,
NextContactDate = NextContact.ContactDate
from
OrderedContactCTE Contact
left join OrderedContactCTE NextContact on
Contact.IDApplication = NextContact.IDApplication and
Contact.[Sequence] = NextContact.[Sequence] - 1
),
--------------------------------------------------------------------------------
-- Step 3: Match every contact with all replies that are strictly after the
-- original contact date and, where applicable, strictly before the
-- subsequent contact date. For each unique contact, sequence the
-- replies in reverse order by reply date.
--------------------------------------------------------------------------------
OrderedResponseCTE as
(
select
Contact.*,
Reply.ReplyDate,
Reply.RInfo,
[Sequence] = row_number() over (partition by Contact.UniqueID order by Reply.ReplyDate desc)
from
PairedContactCTE Contact
left join @Reply Reply on
Contact.IDApplication = Reply.IDApplication and
Contact.ContactDate < Reply.ReplyDate and
(
Contact.NextContactDate is null or
Contact.NextContactDate > Reply.ReplyDate
)
)
--------------------------------------------------------------------------------
-- Step 4: Finally, select each contact and the date/info of the latest reply
-- which is an eligible match for that contact.
--------------------------------------------------------------------------------
select
IDApplication,
ContactDate,
CInfo,
ReplyDate,
RInfo
from
OrderedResponseCTE
where
[Sequence] = 1;
#2
1
After about 15 minutes of psychological torment I was able to churn out this query. The only caviat is that I am not sure that the join condition will only match a single record in each case. I suspect there is another join condition which you did not explicitly mention.
在经历了15分钟的心理折磨之后,我终于明白了这个问题。唯一的缺点是,我不确定联接条件是否只匹配每个案例中的单个记录。我怀疑还有另一个加入条件你没有明确提到。
SELECT t1.IDApplication, t1.ContactDate, t1.CInfo,
t2.ReplyDate, t2.RInfo
FROM table1 t1
LEFT JOIN
table2 t2
ON t1.IDApplication = t2.IDApplication AND
t2.ReplyDate > t1.ContactDate AND
t2.ReplyDate < (SELECT MIN(t.ContactDate)
FROM table1 t
WHERE t.ContactDate > t1.ContactDate AND
t.IDApplication = t1.IDApplication)
#3
1
I don't have a SQL Server instance to test. Let me know how close this gets (extension of Tim's solution)
我没有要测试的SQL Server实例。让我知道这有多接近(蒂姆解的延伸)
SELECT c1.IDApplication, c1.ContactDate, c1.CInfo, r1.ReplyDate, r1.RInfo
FROM contact_table c1
LEFT JOIN
reply_table r1
ON c1.IDApplication = r1.IDApplication AND
r1.ReplyDate > c1.ContactDate AND
r1.ReplyDate < ( SELECT isnull(MIN(c2.ContactDate),'31-DEC-9999')
FROM contact_table c2
WHERE c2.ContactDate > c1.ContactDate AND
c2.IDApplication = c1.IDApplication ) AND
NOT EXISTS ( SELECT null
FROM reply_table r2
WHERE r2.IDApplication = r1.IDApplication AND
r2.ReplyDate > r1.ReplyDate AND
r2.ReplyDate < ( SELECT isnull(MIN(c2.ContactDate),'31-DEC-9999')
FROM contact_table c2
WHERE c2.ContactDate > c1.ContactDate AND
c2.IDApplication = c1.IDApplication ) )
#4
0
If you record rely on some data from record above its better to implement some stored procedure logic. Something like this:
如果您记录依赖于上面记录的一些数据,那么最好实现一些存储过程逻辑。是这样的:
/*
CREATE TABLE App (Id INT, ContractDate DATETIME, CInfo VARCHAR(100))
CREATE TABLE Reply (Id INT, ReplyDate DATETIME, RInfo VARCHAR(100))
INSERT App SELECT 1, '06/01/2016',' pie'
INSERT App SELECT 1, '01/10/2016', 'cake'
INSERT App SELECT 1, '02/03/2015', 'banana'
INSERT App SELECT 2, '06/03/2016', 'cake'
INSERT App SELECT 2, '12/23/2015', 'apple'
INSERT Reply SELECT 1, '05/30/2016', 'circle'
INSERT Reply SELECT 1, '05/03/2016', 'square'
INSERT Reply SELECT 1, '02/04/2015', 'triangle'
INSERT Reply SELECT 1, '01/14/2016', 'pentagon'
INSERT Reply SELECT 2, '06/04/2016', 'square'
INSERT Reply SELECT 2, '02/01/2016', 'pentagon'
INSERT Reply SELECT 2, '06/10/2016', 'circle'
*/
--SELECT * FROM App
DECLARE @AppReply TABLE (Id INT, ContractDate DATETIME, CInfo VARCHAR(100), ReplyDate DATETIME, RInfo VARCHAR(100))
DECLARE
@Id INT,
@PrevId INT,
@ContractDate DATETIME,
@PrevContractDate DATETIME,
@CInfo VARCHAR(100)
DECLARE appcursor CURSOR FAST_FORWARD FOR
SELECT Id, ContractDate, CInfo FROM App
OPEN appcursor
FETCH NEXT FROM appcursor
INTO @Id, @ContractDate, @CInfo
WHILE @@FETCH_STATUS = 0
BEGIN
IF(@Id != @PrevId)
SET @PrevContractDate = NULL
INSERT @AppReply (Id, ContractDate, CInfo)
SELECT TOP 1 @Id, @ContractDate, @CInfo
UPDATE @AppReply SET ReplyDate = R.ReplyDate, RInfo = R.RInfo
FROM @AppReply AR
LEFT JOIN Reply R ON R.Id = AR.Id
AND R.ReplyDate > AR.ContractDate AND R.ReplyDate < ISNULL(@PrevContractDate, DATEADD(DD, 1, R.ReplyDate))
WHERE AR.Id = @Id AND AR.CInfo = @CInfo
SET @PrevContractDate = @ContractDate
SET @PrevId = @Id
FETCH NEXT FROM appcursor
INTO @Id, @ContractDate, @CInfo
END
CLOSE appcursor;
DEALLOCATE appcursor;
SELECT * FROM @AppReply
Hope this helps. P.S.: this query was written fast. Sorry for poor logic in it.
希望这个有帮助。注::这个查询写得很快。对不起,里面逻辑很差。
#1
2
My approach to problems like this is typically to break them into small steps, each of which I can implement as a CTE, so I get something that's pretty easy to read through and understand. I can always try to collapse it into fewer steps later if need be. Here's a possible solution. See the comments for an explanation of how it works.
我处理此类问题的方法通常是将它们分解成小步骤,每个步骤都可以实现为CTE,因此我得到了一些非常容易阅读和理解的东西。如果需要的话,我可以尝试把它分解成更少的步骤。这是一个可能的解决方案。有关它如何工作的解释,请参阅评论。
--------------------------------------------------------------------------------
-- Set up the sample data from the question.
--------------------------------------------------------------------------------
declare @Contact table (IDApplication int, ContactDate date, CInfo varchar(32));
declare @Reply table (IDApplication int, ReplyDate date, RInfo varchar(32));
insert @Contact values
(1, '2016-06-01',' pie'),
(1, '2016-01-10', 'cake'),
(1, '2015-02-03', 'banana'),
(2, '2016-06-03', 'cake'),
(2, '2015-12-23', 'apple');
insert @Reply values
(1, '2016-05-30', 'circle'),
(1, '2016-05-03', 'square'),
(1, '2015-02-04', 'triangle'),
(1, '2016-01-14', 'pentagon'),
(2, '2016-06-04', 'square'),
(2, '2016-02-01', 'pentagon'),
(2, '2016-06-10', 'circle');
--------------------------------------------------------------------------------
-- Step 1: Sequence each group of contacts by contact date.
--------------------------------------------------------------------------------
with OrderedContactCTE as
(
select
*,
[Sequence] = row_number() over (partition by IDApplication order by ContactDate)
from
@Contact
),
--------------------------------------------------------------------------------
-- Step 2: Match each contact with the subsequent contact (where one exists)
-- having the same IDApplication value. The date of the subsequent
-- contact will act as the upper bound on reply dates that are valid for
-- the original contact. Assign each contact a unique identifier that
-- we'll use in the following step.
--------------------------------------------------------------------------------
PairedContactCTE as
(
select
UniqueID = row_number() over (order by Contact.IDApplication, Contact.[Sequence]),
Contact.IDApplication,
Contact.ContactDate,
Contact.CInfo,
NextContactDate = NextContact.ContactDate
from
OrderedContactCTE Contact
left join OrderedContactCTE NextContact on
Contact.IDApplication = NextContact.IDApplication and
Contact.[Sequence] = NextContact.[Sequence] - 1
),
--------------------------------------------------------------------------------
-- Step 3: Match every contact with all replies that are strictly after the
-- original contact date and, where applicable, strictly before the
-- subsequent contact date. For each unique contact, sequence the
-- replies in reverse order by reply date.
--------------------------------------------------------------------------------
OrderedResponseCTE as
(
select
Contact.*,
Reply.ReplyDate,
Reply.RInfo,
[Sequence] = row_number() over (partition by Contact.UniqueID order by Reply.ReplyDate desc)
from
PairedContactCTE Contact
left join @Reply Reply on
Contact.IDApplication = Reply.IDApplication and
Contact.ContactDate < Reply.ReplyDate and
(
Contact.NextContactDate is null or
Contact.NextContactDate > Reply.ReplyDate
)
)
--------------------------------------------------------------------------------
-- Step 4: Finally, select each contact and the date/info of the latest reply
-- which is an eligible match for that contact.
--------------------------------------------------------------------------------
select
IDApplication,
ContactDate,
CInfo,
ReplyDate,
RInfo
from
OrderedResponseCTE
where
[Sequence] = 1;
#2
1
After about 15 minutes of psychological torment I was able to churn out this query. The only caviat is that I am not sure that the join condition will only match a single record in each case. I suspect there is another join condition which you did not explicitly mention.
在经历了15分钟的心理折磨之后,我终于明白了这个问题。唯一的缺点是,我不确定联接条件是否只匹配每个案例中的单个记录。我怀疑还有另一个加入条件你没有明确提到。
SELECT t1.IDApplication, t1.ContactDate, t1.CInfo,
t2.ReplyDate, t2.RInfo
FROM table1 t1
LEFT JOIN
table2 t2
ON t1.IDApplication = t2.IDApplication AND
t2.ReplyDate > t1.ContactDate AND
t2.ReplyDate < (SELECT MIN(t.ContactDate)
FROM table1 t
WHERE t.ContactDate > t1.ContactDate AND
t.IDApplication = t1.IDApplication)
#3
1
I don't have a SQL Server instance to test. Let me know how close this gets (extension of Tim's solution)
我没有要测试的SQL Server实例。让我知道这有多接近(蒂姆解的延伸)
SELECT c1.IDApplication, c1.ContactDate, c1.CInfo, r1.ReplyDate, r1.RInfo
FROM contact_table c1
LEFT JOIN
reply_table r1
ON c1.IDApplication = r1.IDApplication AND
r1.ReplyDate > c1.ContactDate AND
r1.ReplyDate < ( SELECT isnull(MIN(c2.ContactDate),'31-DEC-9999')
FROM contact_table c2
WHERE c2.ContactDate > c1.ContactDate AND
c2.IDApplication = c1.IDApplication ) AND
NOT EXISTS ( SELECT null
FROM reply_table r2
WHERE r2.IDApplication = r1.IDApplication AND
r2.ReplyDate > r1.ReplyDate AND
r2.ReplyDate < ( SELECT isnull(MIN(c2.ContactDate),'31-DEC-9999')
FROM contact_table c2
WHERE c2.ContactDate > c1.ContactDate AND
c2.IDApplication = c1.IDApplication ) )
#4
0
If you record rely on some data from record above its better to implement some stored procedure logic. Something like this:
如果您记录依赖于上面记录的一些数据,那么最好实现一些存储过程逻辑。是这样的:
/*
CREATE TABLE App (Id INT, ContractDate DATETIME, CInfo VARCHAR(100))
CREATE TABLE Reply (Id INT, ReplyDate DATETIME, RInfo VARCHAR(100))
INSERT App SELECT 1, '06/01/2016',' pie'
INSERT App SELECT 1, '01/10/2016', 'cake'
INSERT App SELECT 1, '02/03/2015', 'banana'
INSERT App SELECT 2, '06/03/2016', 'cake'
INSERT App SELECT 2, '12/23/2015', 'apple'
INSERT Reply SELECT 1, '05/30/2016', 'circle'
INSERT Reply SELECT 1, '05/03/2016', 'square'
INSERT Reply SELECT 1, '02/04/2015', 'triangle'
INSERT Reply SELECT 1, '01/14/2016', 'pentagon'
INSERT Reply SELECT 2, '06/04/2016', 'square'
INSERT Reply SELECT 2, '02/01/2016', 'pentagon'
INSERT Reply SELECT 2, '06/10/2016', 'circle'
*/
--SELECT * FROM App
DECLARE @AppReply TABLE (Id INT, ContractDate DATETIME, CInfo VARCHAR(100), ReplyDate DATETIME, RInfo VARCHAR(100))
DECLARE
@Id INT,
@PrevId INT,
@ContractDate DATETIME,
@PrevContractDate DATETIME,
@CInfo VARCHAR(100)
DECLARE appcursor CURSOR FAST_FORWARD FOR
SELECT Id, ContractDate, CInfo FROM App
OPEN appcursor
FETCH NEXT FROM appcursor
INTO @Id, @ContractDate, @CInfo
WHILE @@FETCH_STATUS = 0
BEGIN
IF(@Id != @PrevId)
SET @PrevContractDate = NULL
INSERT @AppReply (Id, ContractDate, CInfo)
SELECT TOP 1 @Id, @ContractDate, @CInfo
UPDATE @AppReply SET ReplyDate = R.ReplyDate, RInfo = R.RInfo
FROM @AppReply AR
LEFT JOIN Reply R ON R.Id = AR.Id
AND R.ReplyDate > AR.ContractDate AND R.ReplyDate < ISNULL(@PrevContractDate, DATEADD(DD, 1, R.ReplyDate))
WHERE AR.Id = @Id AND AR.CInfo = @CInfo
SET @PrevContractDate = @ContractDate
SET @PrevId = @Id
FETCH NEXT FROM appcursor
INTO @Id, @ContractDate, @CInfo
END
CLOSE appcursor;
DEALLOCATE appcursor;
SELECT * FROM @AppReply
Hope this helps. P.S.: this query was written fast. Sorry for poor logic in it.
希望这个有帮助。注::这个查询写得很快。对不起,里面逻辑很差。