Sql 行上的联接,联接查询中的行根据该行具有所有行的最大/最小值
如果我要在返回时加入的查询:Sql 行上的联接,联接查询中的行根据该行具有所有行的最大/最小值,sql,sql-server,date,join,max,Sql,Sql Server,Date,Join,Max,如果我要在返回时加入的查询: 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 RIn
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
我需要把这个退回:
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
我需要它返回第二个表/查询信息,其中它的日期大于第一个表中任何相应的应用程序日期,但不大于第一个表中任何后续日期
因此,对于上面的第一条记录,它是空的,因为回复表中没有日期在2016年6月1日之后的回复信息,因此没有回复,但第二条记录的回复日期为2016年5月30日,因为这是该申请的最大回复日期。更重要的是,第5条记录的回复日期为2016年2月1日,该日期大于联系日期,但不是申请2的最大回复日期(2016年6月10日),但由于申请2的另一个联系日期介于这两个日期之间,因此需要显示下一个联系日期之前的最大日期
这就是让我的大脑受伤的逻辑
我已经加入了关于回复日期大于联系日期的第二个查询,但这会使所有日期的行都变大
我需要在记录中加入一个日期大于基表日期的数据,但其中最大的数据不大于下一个最大的基表日期 经过大约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)
若记录依赖于上面记录中的一些数据,那个么最好实现一些存储过程逻辑。大概是这样的:
/*
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
希望这有帮助。
附言:这个查询写得很快。很抱歉,它的逻辑性很差。我没有要测试的SQL Server实例。让我知道这离Tim解决方案的扩展有多近
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 ) )
我处理这类问题的方法通常是将它们分成几个小步骤,每个步骤我都可以作为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;
第二个表是否有代理键?这是一个理论练习还是一个现实问题?很有可能使联接按您指定的方式工作。但我怀疑这是处理你掌握的信息的正确方法。如果这是一个真实世界的问题,最好重新组织信息或查询。这是真实世界,最糟糕的部分是我提到的两个表实际上是同一个表,我们数据库的这部分是一个笑话。有一列说明了它的联系方式或回复方式,因此,为了说明这一点,可以用两个表来说明我们的数据库非常庞大,我们生成了它的关系图,它只渲染了1/20,这是抽象艺术。所以我们不能改变桌子本身,太乱了。如果我能在和x,y,z阶段的连接中做一个if,我认为这是可行的。这是一个典型的间隙和孤岛问题。我今天没有勇气解决这个问题,但是,下面是我对类似问题的一个答案。我不确定您给我们的连接条件是否足以确保第一个表中的给定记录始终与第二个表中的记录一一对应。嗯,这带来了正确的行数,但只有表1中的信息,表2中的任何信息都不能带来,只有NULL,执行表2。*仅对所有字段返回NULL。但是你在15分钟内比我一周的时间都要长。你需要戈登·林诺夫来回答这样的问题,或者可能是我记不起名字的那匹马。这个解决方案已经接近目标了。至少在子查询中需要一个额外的join子句:从table1中选择MINtable1.ContactDate,其中table1.ContactDate>t1.ContactDate和table1.IDApplication=t1.IDApplication。它还需要进一步的子查询。目前,如果您有联系人、回复、回复、回复联系人的时间序列,那么主查询将为第一个联系人生成三行,每个回复一行。应该有一个子查询来限制t2,在下一个联系日期之前会有后续回复。第二个子查询是具有挑战性的部分,因为我不容易看到它。太棒了!工作起来就像一种享受,现在需要花一段时间来真正理解它:]注意,由于我正在处理的数据的大小,我将它改为使用临时表而不是CTE,因为结果有数千个,这是针对一个每天可能被查询多次的报告。