【问题标题】:SQL Trouble Creating/Writing Stored ProcedureSQL 麻烦创建/编写存储过程
【发布时间】:2018-07-22 04:34:48
【问题描述】:

我在创建存储过程以将项目插入我的数据库时遇到问题,存储过程还会检查项目是否存在,如果不存在则创建它们。

这是我的数据库图

这是我用于创建表的 SQL 代码(除了从表 MenuItem.MenuGroupID 添加到 MenuGroup.MenuGroupId 的外键)这将显示数据类型等。

use MenuDatabase
GO

CREATE TABLE Menu
(
MenuID int primary key identity (1,1),
MenuTitle varchar(200) null,
MenuDescriptionText varchar(200) null,
)

create table Menugroup
(
MenuGroupID int primary key identity (1,1),
MenuID int,
MenuGroupText varchar(200) null,
MenuGroupDescriptionText varchar(200) null,
)
 create table MenuItem
 (
 MenuItemID int primary key identity (1,1),
 MenuID int,
 MenuGroupID varchar(200) null,
 MenuItemCallNumber varchar(200) null,
 MenuItemTitle varchar(200) null,
 MenuItemDescriptionText varchar(200) null,
 MenuItemNutritionText varchar(200) null,
 MenuItemIngredientsText varchar(200) null,
 MenuItemQuantity varchar(200) null,
 MenuItemPreparationTime varchar(200) null,
 MenuItmeCost varchar(200) null,
 MenuItemTypeCode varchar(200) null,
 MenuItemEthnicTypeCode varchar(200) null,
 MenuItemAvailabilityBit varchar(200) null,
 )

这是我正在为存储过程编写的 SQL 脚本。

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO



ALTER PROCEDURE [dbo].[sp_InsertNewMenuItem] (
 @MenuItemTitle VARCHAR(200),
 @MenuGroupText VARCHAR(200),
 @MenuTitle VARCHAR(200)
 )

AS
BEGIN

DECLARE @MenuCount INT
SET @MenuCount = (SELECT COUNT(*) FROM Menu WHERE MenuTitle = @MenuTitle)

DECLARE @MenuID INT
IF @MenuCount= 1 
BEGIN
SET @MenuID = (SELECT MenuID FROM Menu WHERE MenuTitle = @MenuTitle)
END
ELSE
BEGIN
INSERT INTO Menu (MenuTitle) VALUES (@MenuTitle)
SET @MenuID = @@IDENTITY
END

DECLARE @MenuGroupCount INT
SET @MenuGroupCount = (SELECT COUNT(*) FROM MenuGroup WHERE MenuGroupText = @MenuGroupText)

DECLARE @MenuGroupID INT

IF @MenuGroupCount = 1 
BEGIN
 SET @MenuGroupID = (SELECT MenuGroupID FROM MenuGroup WHERE MenuGroupText = @MenuGroupText)
END
ELSE
BEGIN
INSERT INTO MenuGroup (MenuGroupText, MenuGroupID) VALUES (@MenuGroupText, @MenuID)
SET @MenuGroupID = @@IDENTITY
END

INSERT INTO MenuItem (MenuItemTitle)
     VALUES (@MenuItemTitle)

INSERT INTO MenuGroup (MenuGroupText)
     VALUES (@MenuGroupText)

INSERT INTO Menu (MenuTitle)
     VALUES (@MenuTitle)

     SET IDENTITY_INSERT MenuGroup OFF


END


GO

我目前在执行程序时在管理工作室中遇到以下错误

(1 row(s) affected)
Msg 544, Level 16, State 1, Procedure sp_InsertNewMenuItem, Line 36
Cannot insert explicit value for identity column in table 'MenuGroup' when IDENTITY_INSERT is set to OFF.

我需要尝试修复这些错误(我不明白它们为什么会发生),并确保该函数将执行所需的操作,即检查以下内容:

存储过程变量/字段|表字段|如果存在|如果不存在

MenuTitle varchar|菜单.菜单标题|获取 Menu.MenuID|插入,获取身份

Menu.MenuID| MenuGroupText varchar| MenuGroup.MenuGroupText|获取 MenuGroup.MenuGroupID|插入,获取标识 MenuGroup.MenuGroupID

MenuItem varchar|菜单项.菜单项标题|不要插入|插入

一个例子;

MenuTitle - 午餐菜单

MenuGroupText - 三明治

MenuItem - 加州鸡肉三明治

【问题讨论】:

  • 前两个错误是因为你没有在@MenuGroupCount 上调用DECLARE
  • 您在第 36 行 VALUES 之后缺少一个左括号
  • 好的,谢谢,我会修复和编辑以反映更改
  • 非常感谢,似乎解决了问题
  • 新错误:第一个是因为默认情况下你不能插入到表的主键列中,这很容易用SET INDENTITY_INSERT ON修复第二个是因为一些外键规则你有,可能是因为你在复制价值

标签: sql-server stored-procedures


【解决方案1】:

即使您解决了错误消息,您当前的存储过程也确实存在一些可以轻松修复的问题。 例如,您无需对记录进行计数即可确定它们是否存在于表中。
另外,你不应该使用@@Identity,你应该使用scope_identity()
这样做的原因是@@Identity 将返回插入数据库中的最后一个标识值,因此您可能会将值插入到您在过程中甚至不相关的表中。但是,scope_identity() 将返回插入当前作用域的最后一个标识值 - 即在您的存储过程中。

所以这部分:

DECLARE @MenuCount INT
SET @MenuCount = (SELECT COUNT(*) FROM Menu WHERE MenuTitle = @MenuTitle)

DECLARE @MenuID INT
IF @MenuCount= 1 
BEGIN
    SET @MenuID = (SELECT MenuID FROM Menu WHERE MenuTitle = @MenuTitle)
END
ELSE
BEGIN
    INSERT INTO Menu (MenuTitle) VALUES (@MenuTitle)
    SET @MenuID = @@IDENTITY
END

可以这样写:

DECLARE @MenuID INT
SELECT @MenuID = MenuID FROM Menu WHERE MenuTitle = @MenuTitle;

IF @MenuID IS NULL
BEGIN
    INSERT INTO Menu(MenuTitle) VALUES (@MenuTitle);
    SET @MenuID = SCOPE_IDENTITY()
END

请注意,我的版本只有一个 select 语句,而不是您的版本中的两个。

此外,这整个过程应该在一个事务中,这样如果一个部分失败,直到失败点的所有更改都将被还原。
所以这就是我建议您编写程序的方式(注意我已将其名称从 sp_InsertNewMenuItem 更改为 stp_InsertNewMenuItem 以解决 marc_s 的评论:

DROP PROCEDURE [dbo].[sp_InsertNewMenuItem] 
GO

CREATE PROCEDURE [dbo].[stp_InsertNewMenuItem] 
(
    @MenuItemTitle VARCHAR(200),
    @MenuGroupText VARCHAR(200),
    @MenuTitle VARCHAR(200)
)

AS
BEGIN

    BEGIN TRANSACTION

    BEGIN TRY

    DECLARE @MenuID INT
    SELECT @MenuID = MenuID 
    FROM Menu 
    WHERE MenuTitle = @MenuTitle

    IF @MenuID IS NULL
    BEGIN
        INSERT INTO Menu(MenuTitle) 
        VALUES (@MenuTitle)
        SET @MenuID = SCOPE_IDENTITY()
    END

    DECLARE @MenuGroupID INT            
    SELECT @MenuGroupID = MenuGroupID 
    FROM MenuGroup 
    WHERE MenuGroupText = @MenuGroupText

    IF @MenuGroupID IS NULL
    BEGIN
        INSERT INTO MenuGroup(MenuID, MenuGroupText) 
        VALUES (@MenuID, @MenuGroupText)
        SET @MenuGroupID = SCOPE_IDENTITY()
    END

    INSERT INTO MenuItem (MenuID, MenuGroupID, MenuItemTitle)
    VALUES (@MenuID, @MenuGroupID, @MenuItemTitle)

    COMMIT TRANSACTION

    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0
            ROLLBACK TRANSACTION
    END CATCH
END
GO

还有一件事 - 如果您的多个用户可能同时编辑菜单,您可能会得到错误的结果(由于竞争条件)。为避免这种情况,请阅读 Dan Guzman 关于该主题的 blog post

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多