具有交叉应用语法的先前记录

Posted

技术标签:

【中文标题】具有交叉应用语法的先前记录【英文标题】:Previous Record With Cross Apply Syntax 【发布时间】:2019-07-24 15:46:40 【问题描述】:

我有一个名为 ArchiveActivityDetails 的表格,其中显示了客户维修订单的历史记录。 1 个维修订单将进行多次访问 (ActivityID),并根据计划访问的可用人员分配一名技术员。 系统会自动分配作业所需的时间,但有时作业需要更长的时间,因此我们手动修改作业。 我从客户那里得到的初步查询是提取手动修改的作业(即:PlannedDuration >=60 分钟的作业)并显示链接到该手动修改的作业的技术人员。 此报告运行良好。

我最近从客户那里得到的请求是现在添加一个列,显示谁是之前的技术人员,该列与维修订单相关联。

我的同事建议我做一个Cross Apply 回到ArchiveActivityDetails 表,然后显示“以前的技术”,但我之前没有使用过Cross Apply,我在语法上苦苦挣扎,无法得到我的结果想。在我的Cross Apply 中,我使用LAG 来计算“PrevTech”,但是当将其拉入我的主要报告时,我得到NULL。所以我假设我没有正确执行Cross Apply

DECLARE @DateFrom as DATE = '2019-05-20'
DECLARE @DATETO AS DATE = '2019-07-23'
----------------------------------------------------------------------------------
SELECT 
        AAD.Date
        ,ASM.ASM
        ,A.ASM as PrevASM
        ,ASM.KDGID2
        ,R.ResourceName
        ,R.ID_ResourceID
        ,A.ServiceOrderNumber
        ,CONCAT(EN.TECHVORNAME, ' ' , EN.TECHNACHNAME) as TechName
        ,A.PrevTech
        ,EN.TechnicianID
        ,AAD.ID_ActivityID
        ,SO.ServiceOrderNumber
        ,AAD.VisitNumber
        ,AAD.PlannedDuration
        ,AAD.ActualDuration
        ,AAD.PlannedDuration-AAD.ActualDuration as DIFF
        ,DR.Original_Duration
FROM
[Easy].[ASMTrans] AS ASM
INNER JOIN 
[FS_OTBE].[EngPayrollNumbers] AS EN
    ON ASM.KDGID2 = EN.KDGID2
INNER JOIN 
[OFSA].[ResourceID] AS R 
    ON EN.TechnicianID = Try_cast(R.ResourceName as int)
INNER JOIN
[OFSDA].[ArchiveActivityDetails] as [AAD]
    ON R.[ID_ResourceID] = AAD.ID_ResourceID
INNER JOIN
[OFSA].[ServiceOrderNumber] SO
    ON SO.ID_ServiceOrderNumber = AAD.ID_ServiceOrderNumber
LEFT JOIN
[OFSE].[DurationRevision] DR 
    on DR.ID_ActivityID = AAD.ID_ActivityID
CROSS APPLY
    (
        SELECT   
             AD.Date
            ,AD.ID_CountryCode
            ,AD.ID_Status
            ,Activity_TypeID
            ,AD.ID_ActivityID
            ,AD.ID_ResourceID
            ,SO.ServiceOrderNumber
            ,ASM.ASM
            ,LAG(EN.TECHVORNAME+ ' '+EN.TECHNACHNAME) OVER (ORDER BY SO.ServiceOrderNumber,AD.ID_ActivityID) as PrevTech
            ,AD.VisitNumber
            ,AD.ID_ServiceOrderNumber
            ,AD.PlannedDuration
            ,AD.ActualDuration
            ,ROW_NUMBER() OVER (PARTITION BY AD.ID_ServiceOrderNumber Order by AD.ID_ActivityID,AD.Date) as ROWNUM
        FROM
            [Easy].[ASMTrans] AS ASM
        INNER JOIN 
            [FS_OTBE].[EngPayrollNumbers] AS EN
                ON ASM.KDGID2 = EN.KDGID2
        INNER JOIN 
            [OFSA].[ResourceID] AS R 
                ON EN.TechnicianID = Try_cast(R.ResourceName as int)
        INNER JOIN
            [OFSDA].[ArchiveActivityDetails] as [AD]
                ON R.[ID_ResourceID] = AD.ID_ResourceID
        INNER JOIN
            [OFSA].[ServiceOrderNumber] SO
                ON SO.ID_ServiceOrderNumber = AD.ID_ServiceOrderNumber
        WHERE 
            AAD.ID_ActivityID = AD.ID_ActivityID 
            AND 
            AD.ID_CountryCode = AAD.ID_CountryCode
            AND AD.ID_Status = AAD.ID_Status
            AND AD.ID_ResourceID = AAD.ID_ResourceID
            AND AD.Activity_TypeID = AAD.Activity_TypeID
            AND AD.ID_ServiceOrderNumber  = AAD.ID_ServiceOrderNumber
            AND AD.Date  >= '2019-05-01'

     ) as A
WHERE 
ASM.KDGID2 
    IN (50008323,50008326,50008329,50008332,50008335,50008338,50008341,50008344,50008347,50008350,50008353,50008356,50008359,50008362,50008365)
    AND AAD.ID_Status = 1
    AND AAD.ID_CountryCode = 7
    AND AAD.Activity_TypeID=91
    AND 
(
    AAD.[Date] BETWEEN IIF(@DateFrom < '20190520','20190520',@DateFrom)  AND IIF(@DateTo < '20190520','20190520',@DateTo))
    AND AAD.ActualDuration > 11
    AND
(
    (DR.Original_Duration >= 60)
    OR 
    (DR.ID_ActivityID IS NULL AND AAD.PlannedDuration >= 60))

我希望看到以前的技术经理和以前的区域销售经理来处理手动修改的工作。

业务原因:经理想要查看最初请求手动修改工作的人。请求的时间被高估了,这是在浪费时间。为了更好地计划,他们需要查看谁要求在工作中获得额外的时间,并尝试减少时间。 我将附上ArchiveActivityDetail 表,其中显示了维修订单的历史记录以及预期结果。

【问题讨论】:

【参考方案1】:

您在交叉应用中的查询结果将在您的查询中显示为一个表,因此您可以使用top(1) 并按降序排列以获得您想要的第一行排序(它看起来像 ActivityId?也许是 VisitNumber?) .

简化问题的根源,假设您只有一张带有ServiceOrderNumberID_ActivityASMTECH 的表。要获取活动 2414073 的前一行,您可以这样做:

select  top(1) ASM, TECH
from    OFSDA.ArchiveActivityDetails as AD
where   ID_ServiceOrderNumber = 2370634229 -- same ServiceOrderNumber
    and ID_Activity < 2414073 -- previous activities
order by ID_Activity desc -- highest activity less than 2414073

您可能想要使用outer apply,而不是cross apply。这是相同的,但您将在第一个活动的主查询中获得一行,它只会在您的应用中包含空值。如果您想从结果中省略第一行,因为它没有前一行,请继续使用交叉应用。

您可以将上述查询放入outer apply() 的括号中并添加别名(Previous)。在主查询中链接到当前行的值,使用top(1) 仅获取第一行,并按 ID_Activity 降序排列以获取 ID_Activity 最高的行。

select  ASM, TECH,
        PreviousASM, PreviousTECH
from    OFSDA.ArchiveActivityDetails as AD
outer apply (
    select  top(1) ADInner.ASM as PreviousASM, ADInner.TECH as PreviousTECH
    from    OFSDA.ArchiveActivityDetails as ADInner
    where   ADInner.ID_ServiceOrderNumber = AD.ID_ServiceOrderNumber
        and ADInner.ID_Activity < AD.ID_Activity
    order by ADInnerID_Activity desc
) Previous
where   ID_ServiceOrderNumber = 2370634229

【讨论】:

嗨@Jason,我在查询中尝试了外部应用,效果很好。非常感谢。我现在只是尝试优化查询以稍微加快它的速度。这还不错,但看看我能做些什么来让它稍微快一点。任何提示都会很棒。

以上是关于具有交叉应用语法的先前记录的主要内容,如果未能解决你的问题,请参考以下文章

如何根据先前的字段是不是被占用来更新记录? [关闭]

在bigquery中交叉应用MSSQL运算符等效

尝试将记录添加到具有先前创建的记录的表时出错

MySql的回顾五:多表查询下(内联/左外/右外/自连接/交叉)-1999语法

SQL - 从源表中提取某些记录,但也交叉引用该表

连接(交叉连接内连接外连接自连接)