使用集合的第一条记录进行联接的快速SQL查询
假设有以下表格 表A使用集合的第一条记录进行联接的快速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
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上的索引没有效果。