【问题标题】:Stored procedure with AS MERGE not returning anything?AS MERGE 的存储过程不返回任何内容?
【发布时间】:2018-02-21 15:17:19
【问题描述】:

编辑:发票顺序编号是多个国家/地区的法律。

编辑:我的变量命名不佳表明我想使用生成的 Id 作为键。不是这种情况。应该坚持使用“invoiceNumber”。


我的问题与https://stackoverflow.com/a/24196374/1980516这里提出的完全相同

但是,由于建议的解决方案引发了语法错误,因此我对其进行了调整以使用游标。

首先,对于给定的 Business+Year 组合,有一个生成新 Nr 的存储过程:

CREATE PROCEDURE PROC_NextInvoiceNumber @businessId INT, @year INT, @Nr NVARCHAR(MAX) OUTPUT 
AS MERGE INTO InvoiceNextNumbers ini
    USING (VALUES (@businessId, @year)) Incoming(BusinessId, Year)
        ON Incoming.BusinessId = ini.BusinessId AND Incoming.Year = ini.Year
    WHEN MATCHED THEN UPDATE SET ini.Nr = ini.Nr + 1
    WHEN NOT MATCHED BY TARGET THEN INSERT (BusinessId, Year, Nr) 
VALUES(@businessId, @year, 1)

OUTPUT INSERTED.Nr;

然后,使用该存储过程,我创建了一个 INSTEAD OF INSERT 触发器:

CREATE TRIGGER TRIG_GenerateInvoiceNumber ON Invoices INSTEAD OF INSERT
AS 
  BEGIN

    DECLARE @BusinessId INT
    DECLARE @InvoiceId INT
    DECLARE @BillingDate DATETIME2(7)

    -- Cursors are expensive, but I don't see any other way to call the stored procedure per row
    -- Mitigating factor: Mostly, we're only inserting one Invoice at a time
    DECLARE InsertCursor CURSOR FAST_FORWARD FOR
    SELECT BusinessId, Id, BillingDate FROM INSERTED

    OPEN InsertCursor
    FETCH NEXT FROM InsertCursor
    INTO @BusinessId, @InvoiceId, @BillingDate
    WHILE @@FETCH_STATUS = 0
    BEGIN
        DECLARE @year INT
        SET @year = year(@BillingDate)

        DECLARE @Number NVARCHAR(MAX)
        EXEC PROC_NextInvoiceNumber @BusinessId, @year, @Number OUTPUT
        -- SET @Number = 'this works'

        INSERT INTO Invoices (BusinessId, Id, BillingDate, Number) 
            VALUES (@BusinessId, @InvoiceId, @BillingDate, @Number)

        FETCH NEXT FROM InsertCursor
        INTO @BusinessId, @InvoiceId, @BillingDate
    END
    CLOSE InsertCursor
    DEALLOCATE InsertCursor

    END

如果我取消注释 SET @Number = 'this works',那么在我的数据库中,在 Invoice.Number 中成功设置了确切的字符串('this works')。

不知何故,我的 OUTPUT 参数未设置,我不知道为什么不设置.. 有人可以解释一下吗?

EDIT 更新以响应 cmets(谢谢):

  • 我有一个用于 Invoice 的复合键 (BusinessId, Id)。所需的最终结果是一个唯一的发票 Identifier 编号,格式为“20180001”,它是一个连续的数字序列 businessId 中。所以业务 1 的发票编号为 20180001、20180002、20180003,而业务 2 的发票编号为 20180001、20180002、20180003。(但复合主键不同)

  • 我也不想要那个光标,但我在上面提到的问题所建议的框架内没有看到其他方法。

  • 使用现有业务 ID 和年份手动调用 PROC_NextInvoiceNumber 返回 NULL。

  • 如果我尝试在 PROC_NextInvoiceNumber 中设置 Id,如果我将其设置在 MERGE 内,我会得到 A MERGE statement must be terminated by a semi-colon (;).,如果我设置在 MERGE 外,我会得到 The multi-part identifier "INSERTED.Nr" could not be bound.

【问题讨论】:

  • PROC_NextInvoiceIdentifier 的代码在哪里?当我在一个而不是触发器中看到一个光标时,我感到畏缩。该触发器的主体处于循环中,因此您可以获得键值。这是一个巨大的危险信号,表明某些东西设计得不好。由于语法错误而求助于游标?您应该修复代码,而不是使用光标。
  • 没有AS MERGE。 MERGE 是一个类似于 INSERT、UPDATE 或 DELETE 的命令。 无法捕获存储过程结果,除非将结果插入到表中。为什么你在触发器中使用 cursor,而不是在里面写 MERGE?您可以将 MERGE 放在子查询或 CTE 中,并将其结果插入 Invoices
  • BTW MERGE 将为插入和更新的行返回结果both。这意味着您最终会在 Invoices 表中出现重复项。为什么不在触发器中放置单独的 INSERT/UPDATE 语句?结果会干净很多
  • 您没有在存储过程代码中的任何位置设置/更改 OUTPUT 参数 @id 的值。当您自行运行该过程(在触发器之外)时,该过程是否返回预期结果?
  • PS:SQL Server 有sequencesPROC_NextInvoiceIdentifier 可以在 SELECT 或 VALUES 子句中替换为 NEXT VALUE FOR MyInvoiceSequence

标签: sql sql-server


【解决方案1】:

永远不会设置您的 OUTPUT 参数。您正在使用OUTPUT clause of the MERGE 语句来创建结果集。这与为参数赋值无关。

MERGE INTO..
USING ... ON ...
WHEN MATCHED THEN UPDATE ...
WHEN NOT MATCHED BY TARGET THEN INSERT ...
OUTPUT INSERTED.Nr;  /* <-- HERE this is the OUTPUT *clause*  */

更改代码以实际将某些内容分配给@Nr:

SET @Nr = ...

典型的方式是使用 OUTPUT 子句将想要的值存储到一个表变量中,然后将该值赋给想要的输出 *variable:

DECLARE @t TABLE (Nr NVARCHAR(MAX));
MERGE INTO..
USING ... ON ...
WHEN MATCHED THEN UPDATE ...
WHEN NOT MATCHED BY TARGET THEN INSERT ...
OUTPUT INSERTED.Nr INTO @t;

SELECT @Nr = Nr FROM @t;

【讨论】:

  • 什么是“插入语句的输出子句of”?我在存储过程中有一个输出子句,在触发器中有一个插入语句,但在插入语句中没有输出子句。此外,如果我执行“SET OUTPUT =”或“@Id =”,我会得到A MERGE statement must be terminated by a semi-colon。据我所知,我正在做的事情遵循语法docs.microsoft.com/en-us/sql/t-sql/statements/…。你能告诉我它是怎么做的吗?
  • @Diana 我更新了帖子以更好地表达我的意思。
  • 就是这样!我真的没有看到那是我所缺少的。谢谢!
猜你喜欢
  • 2011-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-01
  • 1970-01-01
  • 2021-11-30
相关资源
最近更新 更多