SQL Server 函数和触发器的问题

Posted

技术标签:

【中文标题】SQL Server 函数和触发器的问题【英文标题】:Issues with SQL Server functions and triggers 【发布时间】:2016-04-24 03:35:46 【问题描述】:

我一直在使用 SQL Server 的“高级”功能并创建函数、触发器来处理这个项目,但其中两个给我带来了严重的问题。

我应该创建一个包含一个输入参数并返回一个表的函数。该函数本身将返回每个保险费用(基数、配偶等)的个人成本摘要、这些费用的总和以及给定员工拥有的受抚养人数量的员工姓名 - 用户输入受抚养人的数量作为输入参数。

CREATE FUNCTION fnInsCosts
    (@NoDependants int)
--returns table datatype
RETURNS table
AS 
    --Set the Return to select the columns and aggregated columns from vwPayRoll as listed in exercise 3 of Module 12 --assignment sheet where Dependants is = to the input variable.
    RETURN (SELECT 
                EmpName, 
                SUM(BaseCost) AS TotBaseCost, SUM(SpouseIns) AS TotSpouseCost, 
                SUM(DepIns) AS TotDepCost, SUM(DentalCost) AS TotDentalCost, 
                SUM(BaseCost * SpouseIns * DepIns * DentalCost) AS TotInsCost
            FROM  
                vwPayroll
            WHERE 
                Dependants = @NoDependants
            GROUP BY 
                Dependants);

SELECT * FROM dbo.fnInsCosts(2);
SELECT * FROM dbo.fnInsCosts(0);-- Unfinished/error with select and EmpName?

这是我尝试运行整个程序时遇到的错误:

消息 156,级别 15,状态 1,过程 fnInsCosts,第 15 行 关键字“SELECT”附近的语法不正确。

当我运行除了我调用它的部分之外的所有内容时,它会这样说:

消息 8120,级别 16,状态 1,程序 fnInsCosts,第 10 行 列“vwPayroll.EmpName”在选择列表中无效,因为它既不包含在聚合函数中,也不包含在 GROUP BY 子句中。

这是最后一个;我正在创建一个触发器,我需要创建两个表副本:

--Test for the existence of a fake table named TempEmpData. If it exists, drop it.
IF OBJECT ID('TempEmpData') IS NOT NULL
    DROP TABLE TempEmpData;

--Test for the existence of a fake table named TempWork. If it exists, drop it.
IF OBJECT ID('TempWork') IS NOT NULL
    DROP TABLE TempWork;

--Select everything from EmpData into the appropriate fake table
SELECT * INTO TempEmpData FROM EmpData

--Select everything from Work into the appropriate fake table
SELECT * INTO TempWork FROM Work
GO

CREATE TRIGGER TempEmpDate_INSERT_UPDATE_DELETE
ON TempEmpData
AFTER INSERT, UPDATE, DELETE
AS
    --(USE THIS CONDITIONAL STRUCTURE- substitute variable names where needed and remove the "--")
    IF EXISTS (SELECT * FROM Deleted JOIN TempEmpData ON Deleted.EmpID = TempEmpData.EmpID)
             --the correct primary key)
    BEGIN;
        --Custom error message
        THROW 11, 'EmpID is in use; transaction cancelled!', 1;
        --rollback the transaction
        ROLLBACK TRAN;
    END;
    ELSE
    BEGIN
        --Update the appropriate fake table
        CREATE TRIGGER TempEmpData_INSERT_UPDATE
        ON TempEmpData
        AS 
            --Set the appropriate column to the correct value
            --Where the correct primary key is in a subquery selecting that same key from the
            --system table that handles inserts
            UPDATE TempEmpData 
            SET BenPlanID = 0;

            DELETE TempEmpData
            WHERE EmpID = 41;

            INSERT TempEmpData
            VALUES ('Bill', 'Smith', '11/14/2014', 0, 0, 1, NULL, 2);

            SELECT * 
            FROM TempEmpData
            ORDER BY EmpID DESC
        END;

以下是错误:

消息 4145,第 15 级,状态 1,第 4 行 在期望条件的上下文中指定的非布尔类型表达式,靠近“ID”。

消息 4145,第 15 级,状态 1,第 8 行 在预期条件的上下文中指定的非布尔类型的表达式,靠近“ID”。

消息 156,级别 15,状态 1,过程 TempEmpDate_INSERT_UPDATE_DELETE,第 17 行 关键字“TRIGGER”附近的语法不正确。

如果有任何见解,我将非常感激。

【问题讨论】:

第二个错误是因为你按Dependants分组,但你应该按EmpName分组 而函数是OBJECT_ID而不是OBJECT ID 【参考方案1】:

您正在使用“新”内联语法来创建函数,太棒了!

第一个错误来自缺少“GO”,以将函数的创建与对它的调用分开

CREATE FUNCTION fnInsCosts()
--You function code here

GO

SELECT * FROM dbo.fnInsCosts(2);

函数中的第二个错误有其原因 - 正如 JamesZ 已经指出的 - 在错误的分组列中:

SELECT EmpName, 
       SUM(BaseCost) AS TotBaseCost, 
       SUM(SpouseIns) AS TotSpouseCost, 
       SUM(DepIns) AS TotDepCost, 
       SUM(DentalCost) AS TotDentalCost, 
       SUM(BaseCost * SpouseIns * DepIns * DentalCost) AS TotInsCost
FROM  vwPayroll
WHERE Dependants = @NoDependants
GROUP BY EmpName

你看,唯一没有任何聚合函数的列是 EmpName...

最后一个 - JamesZ 再次声明已经是函数名称中缺少的下划线OBJECT ID

IF OBJECT_ID('TempEmpData') IS NOT NULL

在您的代码中,T-SQL 正在搜索“对象”的含义和一个名为“ID”的函数...(与 IF OBJECT ID('TempWork') IS NOT NULL 相同)

最后一点:在 SO 上,强烈建议每个问题创建一个问题。这个问题至少应该分成两个单独的问题。想想未来的读者寻找类似的问题......

编辑 Ups,还有一个……

您的最后一条错误消息指向一些我不明白您真正想要实现的代码的代码...您是从触发器中创建触发器吗?可能是,MERGE 声明是满足您需求的更好方法...

【讨论】:

感谢您的信息。对于那个很抱歉;我以为如果我把问题分开会占用太多空间,但这是有道理的。 至于我的最后一个查询,这是摘要:“此触发器的目的是防止从 TempEmpData 表中删除员工,如果他们在 TempWork 表中有相应的记录,并确保新员工不会无意中获得保险计划(ID 0 是 90 天试用期列表)。” @Pau808 不能在其他代码中使用CREATE TRIGGER 语句。这必须是批次中的第一条语句....我建议开始一个新问题,在其中描述您的问题并展示您迄今为止尝试过的内容(带有一些示例数据或Fiddle。放置一个链接到您问题中某个地方的此问题,这将连接这些问题。快乐编码! @Pau808 你可能会读到this:页面中间“触发器限制” @Pau808 请问一下,为什么又把验收单去掉了?我的回答有什么未解决的问题吗?

以上是关于SQL Server 函数和触发器的问题的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 触发 CAST 函数

学习 SQL Server :内置函数,触发器

深入理解SQL Server 2005 中的 COLUMNS_UPDATED函数

SQL Server 存储过程和触发器

SQL Server T—SQL 存储过程 触发器

使用 DBContext 将 SQL Server 数据库与时间触发的 Azure 函数连接起来