【问题标题】:UDF don't give the same resultUDF 不给出相同的结果
【发布时间】:2018-09-30 13:10:36
【问题描述】:

当我在触发器之外执行 UDF 时结果不一样

在触发器中执行时,UDF 总是返回 true

但在触发器之外结果是真还是假

ALTER FUNCTION [dbo].[MandatExist]
(
   @Numero      int,
   @IdBranche   int,
   @Exercice    int 
)
RETURNS   bit
AS
BEGIN
    DECLARE @Result   bit
    DECLARE @Nbr      int 
    DECLARE @Categ    int

    SELECT @Categ = CategorieNumero
    FROM Branche
    WHERE IdBranche = @IdBranche

    SELECT @Nbr=COUNT(*)
    FROM  Mandat AS M INNER JOIN Branche AS B ON M.IdBranche=B.IdBranche
    WHERE (Numero = @Numero) AND (B.CategorieNumero = @Categ) AND (Exercice = @Exercice)

    IF @Nbr = 0
      SET @Result = 0
    ELSE 
      SET @Result = 1

    RETURN @Result

END

触发器调用 MandatExist 来获取号码是否存在

ALTER TRIGGER [dbo].[ValidInsertUpdate_Mandat]
   ON  [dbo].[Mandat]
   FOR INSERT,UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    DECLARE  @Cloturer       AS bit
    DECLARE  @Exercice       AS int
    DECLARE  @IdBranche      AS int
    DECLARE  @Numero         AS int
    DECLARE  @Message        AS nvarchar(100)

    SELECT @Cloturer=Cloturer, @Exercice=Exercice, @Numero=Numero, @IdBranche=IdBranche
    FROM INSERTED


    IF (dbo.MandatExist(@Numero, @IdBranche, @Exercice)=1)
    BEGIN
      SET @Message = 'Numero de mandat existant.'
      RAISERROR(@Message, 16, 1)
      ROLLBACK TRAN
    END 

【问题讨论】:

  • 请在问题后面附加 UDF 的定义。
  • 如何从触发器调用 UDF?所述触发器的代码是什么?调用时所述数据的值是多少?我们看不到您看到的内容,因此除非您向我们展示,否则我们无能为力。
  • inserteddeleted 是表,因此它们可以表示集合操作的结果。假设触发器总是只处理一行,而设计触发器通常是一个糟糕的计划。如果您绝对确定不会超过一排,那么添加对行数的检查,并使用RaIsErrorThrow 明确告知稍后来的人他们有试图执行不可接受的语句。 (if ( select Count(*) from inserted ) > 1 RaIsError( 'FooTable_Insert: No more than one row may be processed.', 25, 42 ) with log)
  • 提示:当检查是否存在一行或多行时,使用EXISTS 比获取精确的COUNT 更有效,然后只检查它是否大于零。跨度>

标签: sql sql-server tsql triggers user-defined-functions


【解决方案1】:

INSERTED 是一个表格,因此可能包含多于一行,这意味着此代码

SELECT @Cloturer=Cloturer, @Exercice=Exercice, @Numero=Numero, @IdBranche=IdBranche
FROM INSERTED

本质上是不正确的。

UDF 不是基于集合的编程的最佳选择,并且可能会导致性能下降。具体来说,这个 UDF 完全没有意义,没有理由将此代码封装到单独的模块中。这是一个简单的EXISTS 代码。

整个函数可以而且必须用EXISTS 语句替换,触发器的整个代码可能如下所示:

IF EXISTS(
  SELECT 1
  FROM INSERTED
  INNER JOIN ...
  WHERE ...
)
BEGIN
  RAISERROR(...)
END

我不确定您的表格和列的含义是什么,但我假设您正在尝试检查一些唯一性。因此,假设您不希望同一 CategorieNumero 有另一个 Mandat 记录,最终的 EXISTS 可能如下所示:

IF EXISTS(
   SELECT 1
   FROM INSERTED i
   INNER JOIN Branch b on b.IdBranche = i.IdBranch
   -- other branches with the same CategorieNumero; why isn't CategorieNumero unique?
   INNER JOIN Branch b_dup on b_dup.CategorieNumero = b.CategorieNumero  
   -- existing Mandat rows for the same CategorieNumero with any IdBranch
   INNER JOIN Mandat m_dup on m_dup = b_dup.IdBranch 
   -- ensure you're not comparing inserted/updated Mandat row to itself
   WHERE i.ID != m_dup.ID 
)
...

但我不清楚您的意图,我认为在澄清之后,独特的约束将很容易满足您的大部分需求。

如果您不希望每组 (Exercice, Numero, IdBranch) 多出一行 - 只需向 Mandat 表添加唯一约束并删除触发器和函数!

不要过度使用触发器。还有 UDF。

【讨论】:

    【解决方案2】:

    我用过伊万的解决方案

    IF EXISTS(
                  SELECT 1
                  FROM INSERTED I INNER JOIN Branche b ON b.IdBranche = i.IdBranche
                                INNER JOIN Branche b_dup ON b_dup.IdBranche = b.IdBranche
                                INNER JOIN Mandat m_dup on (m_dup.Exercice = i.Exercice) AND (m_dup.Numero = i.Numero) AND (b_dup.IdBranche=i.IdBranche) 
                                WHERE i.IdMandat != m_dup.IdMandat
                 )
        BEGIN
          RAISERROR('error', 16, 1)
          ROLLBACK TRAN
        END 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-26
      • 2016-04-28
      • 2017-05-21
      • 2022-01-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多