UPDATE源表,AFTER分组?

时间:2015-02-12 05:25:10

标签: sql sql-server

我有一张桌子(来源),上面有一个人的付款 - 名为' Item'在下面的例子中。

此表格将为每个人付款,并在一段时间内添加到其中。

然后我生成发票,基本上收取特定人的所有付款,并将它们合计为一行。

出于审核原因,必须将其存储在发票表中。

我在下面的例子中这样做。

我缺少的是,因为我不知道该怎么做,每次付款一旦分配到发票表,就需要有分配给它的发票ID,存储在Items表中。

所以,请看下面的例子:

CREATE TABLE Items
(
    ID INT NOT NULL IDENTITY(1,1),
    PersonID INT NOT NULL,
    PaymentValue DECIMAL(16,2) NOT NULL,
    AssignedToInvoiceID INT NULL
)

CREATE TABLE Invoice 
(
  ID INT NOT NULL IDENTITY(1,1),
  PersonID INT NOT NULL,
  Value DECIMAL(16,2)

)

INSERT INTO Items (PersonID, PaymentValue) VALUES (1, 100)
INSERT INTO Items (PersonID, PaymentValue) VALUES (2, 132)
INSERT INTO Items (PersonID, PaymentValue) VALUES (2, 65)
INSERT INTO Items (PersonID, PaymentValue) VALUES (1, 25)
INSERT INTO Items (PersonID, PaymentValue) VALUES (3, 69)

SELECT * FROM Items

INSERT INTO Invoice (PersonID, Value)
SELECT PersonID, SUM(PaymentValue) FROM Items
WHERE  AssignedToInvoiceID IS NULL
GROUP BY PersonID

SELECT * FROM Invoice

DROP TABLE Items
DROP TABLE Invoice

然后我需要做的是更新Items表,说第一行已分配给Invoice.ID 1,第二行已分配给Invoice ID 2.第3行也被分配给Invoice ID 2

请注意,表格中还有许多其他列。这是一个基本的例子。

简单地说,我需要记录哪个发票,每个源行都已分配给。

4 个答案:

答案 0 :(得分:1)

确保付款与发票正确关联的关键是确保:

答:在读取未分配的项目和更新AssignedToInvoiceID之间,不会对项目进行更新。

B:在更新AssignedToInvoiceID之前,没有使用正在处理的项目创建新发票。

当您更新两个表时,它必须是一个两步过程。要确保A,它需要在具有最小REPEATABLE READ隔离的事务中运行。确保B要求具有SERIALIZABLE隔离的事务。见SET TRANSACTION ISOLATION LEVEL

可以这样做:

BEGIN TRAN 
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

DECLARE @newInvoices TABLE (PersonID INT, InvoiceID INT)

INSERT INTO Invoice (PersonID, Value)
OUTPUT inserted.ID, inserted.PersonID INTO @newInvoices(InvoiceID, PersonID)
SELECT PersonID, SUM(PaymentValue) FROM Items
WHERE AssignedToInvoiceID IS NULL
GROUP BY PersonID

UPDATE Items
    SET AssignedToInvoiceID = InvoiceID
FROM Items
INNER JOIN @newInvoices newInvoice ON newInvoice.PersonID = Items.PersonID
WHERE AssignedToInvoiceID IS NULL

COMMIT

如果您使用的是SQL Server 2012或更高版本的替代方法是使用SEQUNCE对象,这将允许在创建发票之前为Items分配新的发票ID,从而减少所需的锁定。

它的工作原理如下:

-- Run once with your table setup.
CREATE SEQUENCE InvoiceIDs AS INT START WITH 1 INCREMENT BY 1

CREATE TABLE Items
(
    ID INT NOT NULL IDENTITY(1,1),
    PersonID INT NOT NULL,
    PaymentValue DECIMAL(16,2) NOT NULL,
    AssignedToInvoiceID INT NULL
)

CREATE TABLE Invoice 
(
  -- No longer a IDENTITY column
  ID INT NOT NULL,
  PersonID INT NOT NULL,
  Value DECIMAL(16,2)

)

BEGIN TRAN 

DECLARE @newInvoiceLines TABLE (PersonID INT, InvoiceID INT, PaymentValue DECIMAL(16,2))

-- Reading and updating AssignedToInvoiceID happens in one query so is thread safe.
UPDATE Items
    SET AssignedToInvoiceID = newInvoices.InvoiceID
OUTPUT inserted.PersonID, inserted.AssignedToInvoiceID, inserted.PaymentValue 
    INTO @newInvoiceLines(PersonID, InvoiceID, PaymentValue)
FROM Items
INNER JOIN (
    SELECT PersonID, NEXT VALUE FOR InvoiceIDs AS InvoiceID
    FROM Items
    GROUP BY PersonID
) AS newInvoices ON newInvoices.PersonID = Items.PersonID
WHERE Items.AssignedToInvoiceID IS NULL

INSERT INTO Invoice (ID, PersonID, Value)
SELECT InvoiceID, PersonID, SUM(PaymentValue) FROM @newInvoiceLines
GROUP BY PersonID, InvoiceID

COMMIT

您仍然希望使用交易来确保创建发票。

答案 1 :(得分:0)

根据我的理解,您可以在将记录插入发票表后从联接运行更新,如下所示:

update items
set assignedtoinvoiceid = v.id
from
items m
inner join invoice v on m.personid = v.personid

Demo

答案 2 :(得分:0)

每次发票和#34;运行"时,请为每个人选择最新发票,例如

update items
set AssignedToInvoiceID = inv.id
from 
    (select personid, max(id) id
    from invoice
    group by personid) inv
where items.personid = inv.personid
    and AssignedToInvoiceID is null

这假设AssignedToInvoiceID在没有填充时为空,如果它被默认为空字符串或其他东西,则需要更改where条件。

答案 3 :(得分:0)

1)在从MAX(ID)表插入新行之前,从Invoice表中获取Items。将此值存储到变量中:@MaxInvoiceID

2)将记录插入Invoice表后,使用AssignedToInvoiceID更新Items表中的Invoice.ID>@MaxInvoiceID

参考下面的代码:

CREATE TABLE #Items
(
    ID INT NOT NULL IDENTITY(1,1),
    PersonID INT NOT NULL,
    PaymentValue DECIMAL(16,2) NOT NULL,
    AssignedToInvoiceID INT NULL
)

CREATE TABLE #Invoice 
(
  ID INT NOT NULL IDENTITY(1,1),
  PersonID INT NOT NULL,
  Value DECIMAL(16,2)

)

DECLARE @MaxInvoiceID INT;
SELECT @MaxInvoiceID=ISNULL(MAX(ID),0) FROM #Invoice
SELECT @MaxInvoiceID

INSERT INTO #Items (PersonID, PaymentValue) VALUES (1, 100)
INSERT INTO #Items (PersonID, PaymentValue) VALUES (2, 132)
INSERT INTO #Items (PersonID, PaymentValue) VALUES (2, 65)
INSERT INTO #Items (PersonID, PaymentValue) VALUES (1, 25)
INSERT INTO #Items (PersonID, PaymentValue) VALUES (3, 69)

SELECT * FROM #Items

INSERT INTO #Invoice (PersonID, Value)
SELECT PersonID, SUM(PaymentValue) 
FROM #Items
WHERE  AssignedToInvoiceID IS NULL
GROUP BY PersonID

SELECT * FROM #Invoice

UPDATE Itm
SET Itm.AssignedToInvoiceID=Inv.ID
FROM #Items Itm
JOIN #Invoice Inv ON Itm.PersonID=Inv.PersonID AND Itm.AssignedToInvoiceID IS NULL AND Inv.ID>@MaxInvoiceID

SELECT * FROM #Items

DROP TABLE #Items
DROP TABLE #Invoice