【问题标题】:How can I update a value in a table using a condition on values in another table in Sql server如何使用 Sql server 中另一个表中的值的条件更新表中的值
【发布时间】:2020-11-20 15:53:19
【问题描述】:

我的项目中有 2 个表,WorkOrders 和 WorkOrderTrackings,WorkOrderTrackings 有一个外键 WorkOrderId,它引用 WorkOrders 中的 Id(因此您知道跟踪记录属于哪个工作订单)。 WorkOrderTrackings 中的记录表示工作订单从开始到结束所经历的“步骤”。一旦我们的工程部门将工作订单发布到车间进行建设,它们就会创建。因此,任何工作订单都可以有 1 到多个工作订单跟踪。

现在,WorkOrderTrackings 有一个 StageNum 字段,该字段按从开始到结束的顺序说明步骤的步骤编号。有日期时间吗?开始日期和日期时间? CompleteDate 字段。当他们开始一个步骤时,开始日期被设置,当他们完成时,完成日期被设置。当它已经完成了 1 步并且还没有开始下一个步骤时,上一步的 completedate 有一个值,但下一步的 startdate 没有。

所以目前,要确定工单 X 所处的步骤,我们使用 linq 表达式:

var trackingsForX=db.WorkOrderTrackings.Where(wot=>wot.WorkOrderId==X.Id);
var currentTrackingForX=trackingsForX.OrderByDescending(t=>t.StageNum)
                         .Where(t=>t.StartDate.HasValue && !t.CompleteDate.HasValue)
                         .FirstOrDefault() ??
                    trackingsForX.OrderBy(t=>t.StageName)
                         .Where(t=>!t.CompleteDate.HasValue)
                         .FirstOrDefault()

这会通过在搜索从 last 到 first stagenum 的跟踪时返回找到的第一个带有 startdate 值但没有完成的跟踪记录来选择工作订单的最近跟踪步骤,或者,如果返回 null,则通过查找第一个记录从第一步到最后一步没有完成值。

我们不想每次都这样做,所以我们向 WorkOrders 添加了一个字段 CurrentTrackingStepId,它将作为工作订单最近跟踪记录的外键。随着新步骤的开始,我们将更新工作订单的 CurrentTrackingStepId 以指向该步骤。这样,我们总是可以通过查找工单来访问工单的当前步骤。但是,现在我需要创建一个我们可以运行的 sql 脚本,它将自动将所有现有工作订单的 CurrentTrackingStepId 设置为该订单的当前跟踪 ID。

我将如何编写一个 sql 脚本来完成此操作?

编辑:使用示例数据库数据更新


dbo.WorkOrderTrackings:

ID      WO-Id StageNum          StartDate                CompleteDate
551874  53781   1       2020-08-28 5:13:34 PM       2020-08-28 5:13:45 PM    
551875  53781   2       2020-08-28 7:01:35 PM       2020-08-28 7:01:35 PM    
551878  53781   5       2020-08-28 7:01:35 PM       NULL    
551879  53781   6       NULL                        NULL    
551880  53781   7       NULL                        NULL    
551881  53781   8       NULL                        NULL    
551882  53781   9       NULL                        NULL
551883  53781   10      NULL                        NULL    
551884  53781   11      NULL                        NULL
551885  53781   12      NULL                        NULL
551886  53781   13      NULL                        NULL
551887  53781   14      NULL                        NULL
551888  53781   15      NULL                        NULL
551889  53781   16      NULL                        NULL
551890  53781   17      NULL                        NULL
551891  53803   1       2020-08-28 6:33:18 PM       2020-08-28 6:33:18 PM   
551892  53803   2       2020-08-28 6:33:18 PM       2020-08-31 8:07:09 AM   
551893  53803   3       2020-08-31 8:07:17 AM       2020-08-31 8:07:24 AM   
551894  53803   4       NULL                        NULL
551895  53803   5       NULL                        NULL
551896  53803   6       NULL                        NULL
551897  53803   7       NULL                        NULL
551908  53803   18      NULL                        NULL
551909  53803   19      NULL                        NULL
551910  53803   20      NULL                        NULL
551911  53803   21      NULL                        NULL
551912  53803   22      NULL                        NULL
551913  53803   23      NULL                        NULL
551914  53803   24      NULL                        NULL
551915  53803   25      NULL                        NULL
551916  53803   26      NULL                        NULL


dbo.WorkOrders:

Id       OrderNumber
53803   139107-00
53781   139094-00

因此,如果您查看上面的数据,如果我想要订单 139107-00 的当前跟踪步骤,它应该返回 ID 为 551894 的 WorkOrderTracking 记录(因为这是最近完成的记录之后的记录)。 如果我想要订单 139094-00 的当前跟踪步骤,它应该返回 ID 为 551878 的 WorkOrderTracking 记录(因为这是它已经开始但尚未完成的步骤)。

因此,根据上面的数据,我想将 WorkOrders 中的新 CurrentTrackingStepId 字段设置为 WorkOrderTrackings 中具有 StartDate 值但没有 CompleteDate 值的订单的最新跟踪记录的 ID,或者,如果返回 null ,我想要在具有 StartDate 值和 CompleteDate 值的最后一个 StageNum 之后的下一个 StageNum 记录的 Id。这有意义吗?

我真的没有任何我已经尝试过的脚本,因为我不知道如何编写它。我意识到我需要更新 dbo.WorkOrders 来设置 CurrentTrackingStepId=...something。这将需要一个 select 语句来从正确记录的 WorkOrderTrackings 表中获取 Id。它可能需要一个 EXIST 语句来检查是否存在具有开始日期但没有完整日期的记录。

【问题讨论】:

  • 请分享你已经尝试过的sql脚本+示例数据

标签: sql sql-server asp.net-mvc entity-framework


【解决方案1】:

假设您的 StageNum 值是连续的,您可以执行以下操作:

/* Table and data mock-up */

DECLARE @WorkOrders table ( Id int, OrderNumber varchar(10), CurrentStage int );
INSERT INTO @WorkOrders ( Id, OrderNumber ) VALUES
    ( 53803, '139107-00' ), ( 53781, '139094-00' );

DECLARE @WorkOrderTrackings table (
    ID int, [WO-id] int, StageNum int, StartDate datetime, CompleteDate datetime
);
INSERT INTO @WorkOrderTrackings VALUES
( 551891, 53803, 1, '2020-08-28 6:33:18 PM', '2020-08-28 6:33:18 PM' ),   
( 551892, 53803, 2, '2020-08-28 6:33:18 PM', '2020-08-31 8:07:09 AM' ),   
( 551893, 53803, 3, '2020-08-31 8:07:17 AM', '2020-08-31 8:07:24 AM' ),
( 551894, 53803, 4, NULL, NULL ),
( 551895, 53803, 5, NULL, NULL ),
( 551896, 53803, 6, NULL, NULL ),
( 551897, 53803, 7, NULL, NULL );

/* Update the current stage of the WorkOrder based on the last completed StageNum + 1 */

UPDATE @WorkOrders
SET
    CurrentStage = wt.CurrentStageID
FROM @WorkOrders wo
OUTER APPLY (

    SELECT ID AS CurrentStageID FROM @WorkOrderTrackings wt
    WHERE
        wt.[WO-id] = wo.Id AND wt.StageNum = (
            SELECT ( MAX( StageNum ) + 1 ) FROM @WorkOrderTrackings x WHERE x.[WO-id] = wt.[WO-id]
                AND x.CompleteDate IS NOT NULL
        )

) AS wt
WHERE
    wo.[OrderNumber] = '139107-00';

/* Show the updated WorkOrders resultset */

SELECT * FROM @WorkOrders ORDER BY Id;

返回

+-------+-------------+--------------+
|  Id   | OrderNumber | CurrentStage |
+-------+-------------+--------------+
| 53781 | 139094-00   | NULL         |
| 53803 | 139107-00   | 551894       |
+-------+-------------+--------------+

更新:

我喜欢这种方法,但请注意 StageNum 不是连续的 (第一个系列中的 1,2,5,6,7)。

此示例使用 LEAD,就像 Gert 的方法一样。

/* Update the current stage of the WorkOrder based on the last stage completed */

UPDATE @WorkOrders
SET
    CurrentStage = wt.CurrentStageID
FROM @WorkOrders wo
OUTER APPLY (

    SELECT TOP 1
        LEAD( ID, 1, NULL ) OVER ( PARTITION BY [WO-id] ORDER BY ID, StageNum ) AS CurrentStageID 
    FROM @WorkOrderTrackings wt
    WHERE
        wt.[WO-id] = wo.Id AND wt.ID >= (
            SELECT MAX( ID ) FROM @WorkOrderTrackings x WHERE x.[WO-id] = wt.[WO-id]
                AND x.CompleteDate IS NOT NULL
        )
    ORDER BY
        wt.ID, wt.StageNum

) AS wt
WHERE
    wo.[OrderNumber] = '139107-00';

返回

+-------+-------------+--------------+
|  Id   | OrderNumber | CurrentStage |
+-------+-------------+--------------+
| 53781 | 139094-00   | NULL         |
| 53803 | 139107-00   | 551894       |
+-------+-------------+--------------+

【讨论】:

  • 我喜欢这种方法,但请注意 StageNum 不是连续的(第一个系列中的 1、2、5、6、7)。
  • 相应地更新了我的答案。
  • 感谢您的帮助。我使用了这个并对其进行了一些修改,因此我将此响应标记为答案。但是@GertArnold 的响应可以修改为也可以。
【解决方案2】:

SQL Server 的窗口函数 LEAD 允许在一个 SELECT 语句中访问其他行中的数据。在您的情况下,像...这样的声明

SELECT *
FROM
(
    SELECT *
    ,LEAD(ID, 1) OVER (PARTITION BY [WO-Id] ORDER BY StageNum) [NextId]
    ,LEAD(StageNum, 1) OVER (PARTITION BY [WO-Id] ORDER BY StageNum) [NextStageNum]
    ,LEAD(StartDate, 1) OVER (PARTITION BY [WO-Id] ORDER BY StageNum) [NextStartDate]
    ,LEAD(CompleteDate, 1) OVER (PARTITION BY [WO-Id] ORDER BY StageNum) [NextCompleteDate]
    FROM dbo.WorkOrderTrackings
) d
WHERE CompleteDate IS NOT NULL
AND [NextCompleteDate] IS NULL

...返回:

ID     WO-id StageNum StartDate           CompleteDate        NextId NextStageNum NextStartDate       NextCompleteDate
------ ----- -------- ------------------- ------------------- ------ ------------ ------------------- ----------------
551875 53781 2        2020-08-28 19:01:35 2020-08-28 19:01:35 551878 5            2020-08-28 19:01:35 NULL
551893 53803 3        2020-08-31 08:07:17 2020-08-31 08:07:24 551894 4            NULL                NULL

只是为了向您展示它的作用。第一个LEAD 语句基本上说,“当StageNum 订购时,返回一组相同WO-ids 中的第一个-下一个ID”。这个PARTITION BY 部分对于防止NextId 从下一组订单溢出非常重要。

对您来说,从此查询中返回 [WO-Id], NextId 并删除多余的 LEAD 语句就足够了,以获得您需要的数据:

UPDATE wo
SET CurrentStage = nextStage.NextId
FROM dbo.WorkOrders wo
INNER JOIN
(
    SELECT [WO-Id], NextId
    FROM
    (
        SELECT *
        ,LEAD(ID, 1) OVER (PARTITION BY [WO-Id] ORDER BY StageNum) [NextId]
        ,LEAD(CompleteDate, 1) OVER (PARTITION BY [WO-Id] ORDER BY StageNum) [NextCompleteDate]
        FROM dbo.WorkOrderTrackings
    ) d
    WHERE CompleteDate IS NOT NULL
    AND [NextCompleteDate] IS NULL
    AND NextId IS NOT NULL
) nextStage
ON wo.Id = nextStage.[WO-id]

【讨论】:

  • 哇,我什至从未听说过 LEAD 或 PARTITION 哈哈。 LINQ 绝对毁了我写 sql 语句。这看起来不错,但它会返回当前阶段还是下一个阶段?
  • 查看示例输出:下一个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-12
  • 1970-01-01
  • 1970-01-01
  • 2013-01-21
  • 1970-01-01
相关资源
最近更新 更多