使用集合的第一条记录进行联接的快速SQL查询

使用集合的第一条记录进行联接的快速SQL查询,sql,sql-server,tsql,Sql,Sql Server,Tsql,假设有以下表格 表A WorkId DateA ----- ------- 1 01/01/2017 表B WorkId DateB Flag User ----- ------- ---- ----- 1 01/12/2016 N u1 1 03/12/2016 N u2 1 01/01

假设有以下表格

表A

WorkId       DateA
-----        -------
1           01/01/2017
表B

WorkId       DateB        Flag    User
-----        -------      ----    -----
1           01/12/2016     N       u1
1           03/12/2016     N       u2
1           01/01/2017     Y       u2
1           02/01/2017     Y       u3
1           02/01/2017     Y       u3
1           05/01/2017     N       u4 
1           05/01/2017     N       u5 
1           06/01/2017     N       u5
1           10/01/2017     Y       u5 
1           12/01/2017     Y       u6
1           12/01/2017     N       u7
表A中的每个记录都应根据TableA.WorkId=TableB.WorkId和TableA.DateA=TableB.DateB与表B中的一个记录进行联接(此联接在TableB中始终具有标志=y)。基于这个连接,我应该得到WorkId/TableA.DateA和TableB.User(下面结果中的user1)。例如,表A中的上述记录由表B的第三行连接

然后我需要从表B中获取第一条记录,它的标志是N,并且在DateA之后有最小日期。在本例中,这是表A中的第六条记录。然后我需要将此用户(user2)和日期(DateB)添加到结果中:

结果

WorkId    DateA        DateB         User1     User2
-----     -------      ------        -----     -----
1         01/01/2017   05/01/2017    u2         u4
我使用了下面的查询

WITH c AS (
SELECT a.WorkId, a.DateA, b.User AS User1
FROM TableA a
INNER JOIN TableB b
ON a.WorkId = b.WorkId AND a.DateA = b.DateB
),

c1 AS (
SELECT c.*, b.DateB, b.User AS User2
, ROW_NUMBER() OVER (PARTITION BY b.WorkId, c.DateA ORDER BY b.DateB) AS rn
FROM c
LEFT OUTER JOIN TableB b
ON c.WorkId = b.WorkId AND b.Flag = 'N' AND b.DateB > c.DateA
)

SELECT *
FROM c1
WHERE rn = 1
我有两个索引WorkId+Data和每个表上的数据


问题是查询速度很慢,当表很大时,查询速度会变得非常慢。你知道更快的代码吗。谢谢。

这里有一种表述查询的方法:

select a.*, b2.date as date2, b.user as user1, bnext.user as user2
from tableA a join
     tableB b 
     on a.workid = b.workid and a.date = b.date outer apply
     (select top 1 b2.*
      from tableB b2
      where b2.workId = a.workid and b2.date > a.date and b2.flag = 'N'
      order by b2.date desc
     ) bnext;
对于
连接
,您需要在
表B(工作ID,日期)
上建立索引——键可以按任意顺序排列。对于子查询,您需要在
表B(工作ID、日期、标志、用户)
上建立索引。这一个查询就是您真正需要的

嗯,还有一种可能更快的方法:

select workid, date1, date as date2, user1, user as user2
from (select ab.*, min(date) over (partition by workid, grp) as date1,
             max(user1) over (partition by workid, grp) as user1,
             row_number() over (partition by workid, grp, flag) as seqnum
      from (select b.*,
                   sum(case when a.workid is not null then 1 else 0 end) over (partition by b.workid order by b.date) as grp,
                   max(case when a.workid is not null then user end) as user1
            from tableB b left join
                 tableA a
                 on a.workid = b.workid and a.date = b.date
           ) ab
     ) ab
where seqnum = 1 and flag = 'N';

这要复杂得多,它取决于A中的行与B中的匹配项不重叠。其思想是,它在B中查找匹配项,然后使用窗口函数查找标记为N的第一行。

如果按CTRL-L,查询计划器将建议索引。你有索引吗?每个表中有多少行?如果您可以共享执行计划,我可以提供帮助u@Bob . . . 每个表中都有唯一的id吗?不,这些是临时表,我没有创建任何唯一的id。非常感谢Gordon。我使用了你的第一种方法,当tableA有3米记录,tableB有50米记录时,大约需要30秒。我的方法花了大约40分钟。你提到的表B上的索引没有效果。