UDF 不给出相同的结果

Posted

技术标签:

【中文标题】UDF 不给出相同的结果【英文标题】:UDF don't give the same result 【发布时间】: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 更有效,然后只检查它是否大于零。跨度> 【参考方案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 

【讨论】:

以上是关于UDF 不给出相同的结果的主要内容,如果未能解决你的问题,请参考以下文章

梯度下降和正规方程没有给出相同的结果,为啥?

这两个postgres表达式会给出相同的结果吗?

使用 Matlab FFT 计算的频谱对于不同长度的样本(点数相同但 Fs 不同)给出的结果不一致?

Scikit-learn 脚本给出的结果与教程大不相同,当我更改数据框时会出错

Hive 自动增量 UDF 没有给出想要的结果

Distinct vs row_number() - 使用相同条件的查询在oracle中给出不同的结果?