SQL Server UPDATE,WHERE跨越2个表

时间:2011-03-16 08:32:41

标签: sql-server stored-procedures sql-server-2008-r2

我有一个SQL Server数据库,我需要手动执行更新查询。没有使用任何编程语言的解决方案。(可以使用存储过程)

我在查询中有4个表受影响(/已使用)。

  • [指令]
  • [StatusHistoryForOrder]
  • [StatusHistory] ​​
  • [以下状态]

我需要更新字段[Orders]。[OrderStatusID],它是[状态]的外键。 (所以实际上改变了订单的状态。表[StatusHistoryForOrder]是[StatusHistory]的链接表,只包含2个列。

  • [StatusHistoryForOrder]。[单编号]
  • [StatusHistoryForOrder]。[OrderStatusHistoryid]

不要说这在逻辑上不是因为我已经知道了。设计数据库的公司是一个完全迟钝的公司,但数据库现在太大而无法直接设置,没有时间或金钱去做。

[StatusHistory]表有多列:

  • [StatusHistory]。[OrderStatusHistoryId]
  • [StatusHistory]。[OrderStatusId]
  • [StatusHistory]。[日期]
  • [StatusHistory]。[信息]

[StatusHistory]。[OrderStatusId]也是[状态]的外键。

在更新查询中,我需要将订单状态更新为状态16.但仅限于现在状态为1且超过60天的行。我知道我可以使用函数

来检查日期
DATEDIFF(DD,[StatusHistory].[Date],GETDATE()) > 60

但是如果日期字段不在订单中,如何实现此查询。要设置新的[StatusHistory],必须为该表创建一个新行,并且[StatusHistoryForOrder]表也需要一个新行,并且需要在[Orders]表行中设置该行的ID。

有谁知道怎么做?我对SQL Server(或SQL)相当新,我绝对不知道从哪里开始。

结论:

我需要一个存储过程,首先检查[Orders]中的每一行,如果[StatusHistory]。[Date](使用外键的订单链接)该订单的年龄大于60.如果它更旧那么必须插入一个新的StatusHistory行,其中包含当前日期和状态16.然后在[StatusHistoryForOrder]中必须插入一个新行,并在[StatusHistoryForOrder]中设置statusHistory的新ID。[OrderStatusHistoryid]并在[PageStatusHistoryid]中设置订单ID StatusHistoryForOrder] [单编号]。最后但并非最不重要的是:[Orders]。[OrderStatusID]也需要设置为16。


选择查询以选择订单的日期和状态:

SELECT     TOP (100) PERCENT 
    dbo.Orders.OrderID, 
    dbo.Statuses.Description AS Status, 
    dbo.StatusHistory.Date
FROM         
    dbo.Orders 
INNER JOIN
    dbo.Statuses 
ON 
    dbo.Orders.OrderStatusID = dbo.Statuses.StatusId 
INNER JOIN
    dbo.StatusHistoryForOrder 
ON 
    dbo.Orders.OrderID = dbo.StatusHistoryForOrder.OrderId 
INNER JOIN
    dbo.StatusHistory 
ON 
    dbo.StatusHistoryForOrder.OrderStatusHistoryid = dbo.StatusHistory.OrderStatusHistoryId
WHERE     
    (dbo.Statuses.StatusId = 1) 
AND 
    (DATEDIFF(DD, dbo.StatusHistory.Date, GETDATE()) > 60)

更新 对于@marc_s:

Column info


任何人都可以帮助我吗?

2 个答案:

答案 0 :(得分:1)

尝试使用此CTE(公用表表达式)查找所有这些订单 - 它是否有效,结果是否合理? (这还没有更新任何东西 - 现在只是选择):

USE (your database name here)
GO

DECLARE @OrdersToUpdate TABLE (OrderID INT, StatusHistoryID INT, StatusDate DATETIME)

;WITH RelevantOrders AS
(
    SELECT 
       o.OrderId, sh.Date
    FROM dbo.Orders o
    INNER JOIN dbo.StatusHistoryForOrder ho ON ho.OrderId = o.OrderId
    INNER JOIN dbo.StatusHistory sh ON ho.OrderStatusHistoryid = sh.OrderStatusHistoryid 
    WHERE
       sh.Date <= DATEADD(D, -60, GETDATE())   -- older than 60 days back from today
       AND o.OrderStatusID = 1                 -- status = 1
)
INSERT INTO @OrdersToUpdate(OrderID, StatusDate)
   SELECT OrderID, [Date] 
   FROM RelevantOrders

BEGIN TRANSACTION
BEGIN TRY
    DECLARE @OrderIDToInsert INT,         -- OrderID to process
            @InsertedStatusHistoryID INT  -- new ID of the inserted row in StatusHistory

    -- grab the first OrderID that needs to be processed
    SELECT TOP 1 @OrderIDToInsert = OrderID
    FROM @OrdersToUpdate
    WHERE StatusHistoryID IS NULL
    ORDER BY OrderID

    -- as long as there are still more OrderID to be processed ....
    WHILE @OrderIDToInsert IS NOT NULL
    BEGIN
       PRINT 'Now inserting new StatusHistory entry for OrderID = ' + CAST(@OrderIDToInsert AS VARCHAR(10))

       INSERT INTO dbo.StatusHistory(OrderStatusID, [Date], [Message])
       VALUES(16, GETDATE(), 'Bulk Insert/Update operation')   -- enter here whatever you want to store 

       SELECT @InsertedStatusHistoryID = SCOPE_IDENTITY();   -- grab newly inserted ID 

       PRINT 'New StatusHistory entry inserted with ID = ' + CAST(@InsertedStatusHistoryID AS VARCHAR(10))

       UPDATE @OrdersToUpdate
       SET StatusHistoryID = @InsertedStatusHistoryID
       WHERE OrderID = @OrderIDToInsert

       -- safety - reset @OrderIDToInsert to NULL so that we'll know when we're done
       SET @OrderIDToInsert = NULL

       -- read next OrderID to be processed
       SELECT TOP 1 @OrderIDToInsert = OrderID
       FROM @OrdersToUpdate
       WHERE StatusHistoryID IS NULL
       ORDER BY OrderID
    END 

    -- insert into the StatusHistoryForOrder table
    INSERT INTO dbo.StatusHistoryForOrder(OrderID, OrderStatusHistoryID)
        SELECT OrderID, StatusHistoryID
        FROM @OrdersToUpdate

    -- update your Orders to status ID = 16
    UPDATE dbo.Orders
    SET OrderStatusID = 16 
    FROM @OrdersToUpdate upd
    WHERE dbo.Orders.OrderID = upd.OrderID

    COMMIT TRANSACTION
END TRY
BEGIN CATCH
   SELECT 
       ERROR_NUMBER() AS ErrorNumber,
       ERROR_SEVERITY() AS ErrorSeverity,
       ERROR_STATE() AS ErrorState,
       ERROR_PROCEDURE() AS ErrorProcedure,
       ERROR_LINE() AS ErrorLine,
       ERROR_MESSAGE() AS ErrorMessage

   ROLLBACK TRANSACTION
END CATCH

这个CTE基本上将你的Orders表连接到StatusHistory表(通过中间链接表)并选择你感兴趣的值(希望!)。

答案 1 :(得分:0)

这个特殊问题似乎只能通过设置操作来解决。

DECLARE @Orders TABLE (ID int, rownum int IDENTITY);
DECLARE @StatusHistory TABLE (ID int, rownum int IDENTITY);

/* get the list of orders with expired statuses */
INSERT INTO @Orders (ID)
SELECT o.OrderID
FROM Orders o
  INNER JOIN StatusHistoryForOrder shfo ON o.OrderID = shfo.OrderId
  INNER JOIN StatusHistory sh ON shfo.OrderStatusHistoryid = sh.OrderStatusHistoryId
GROUP BY o.OrderID
HAVING DATEDIFF(DD, MAX(sh.Date), GETDATE()) > 60

/* add so many new rows to StatusHistory and remember the new IDs */
INSERT INTO StatusHistory (OrderStatusId, Date, Message)
OUTPUT inserted.OrderStatusHistoryId INTO @StatusHistory (ID)
SELECT
  16,
  GETDATE(),
  'Auto-inserted as the previous status has expired'
FROM @Orders

/* join the two temp lists together and add rows to StatusHistoryForOrder */
INSERT INTO StatusHistoryForOrder (OrderId, OrderStatusHistoryid)
SELECT o.ID, sh.ID
FROM @Orders o
  INNER JOIN @StatusHistory sh ON o.rownum = sh.rownum

/* finally update the statuses in Orders */
UPDATE Orders
SET OrderStatusID = 16
FROM @Orders o
WHERE Orders.OrderID = o.ID

当然,这应该是单个交易的主体。