Sql server SQL创建/写入存储过程时出现问题
我在创建存储过程以将项目插入数据库时遇到问题,存储过程还会检查项目是否存在,如果不存在,则会创建项目 这是我的数据库图 这是我用来创建表的SQL代码,例外是从表MenuItem.MenuGroupID添加了一个外键到MenuGroup.MenuGroupID,这将显示数据类型等Sql server SQL创建/写入存储过程时出现问题,sql-server,stored-procedures,Sql Server,Stored Procedures,我在创建存储过程以将项目插入数据库时遇到问题,存储过程还会检查项目是否存在,如果不存在,则会创建项目 这是我的数据库图 这是我用来创建表的SQL代码,例外是从表MenuItem.MenuGroupID添加了一个外键到MenuGroup.MenuGroupID,这将显示数据类型等 use MenuDatabase GO CREATE TABLE Menu ( MenuID int primary key identity (1,1), MenuTitle varchar(200) null, M
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
我当前在ManagementStudio中执行该过程时遇到以下错误
(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.MenuTitle | Get Menu.MenuID | Insert,Get Identity
Menu.MenuID | MenuGroupText varchar | MenuGroup.MenuGroupText |获取MenuGroup.MenuGroupID |插入,获取标识MenuGroup.MenuGroupID
MenuItem varchar | MenuItem.MenuItemTitle |不插入|插入
一个例子
菜单-午餐菜单
菜单组文本-三明治
MenuItem-California Chicken Sandwich即使您解决了错误消息,您当前的存储过程也确实存在一些可以轻松修复的问题。 例如,您不需要对记录进行计数来确定它们是否存在于表中。 另外,您不应该使用@@Identity,您应该使用scope\u Identity。 原因是@@Identity将返回插入数据库中的最后一个标识值,因此您可能会将这些值插入到您的过程中甚至不相关的表中。但是,scope_identity将返回插入当前作用域中的最后一个标识值,即存储过程中的标识值
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
所以这一部分:
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_的意见:
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关于该主题的文章。前两个错误是因为您没有在@MenuGroupCount上调用DECLARE。您在第36行的值后缺少了一个括号。OK谢谢,我将修复并编辑以反映更改Hanks一大堆似乎修复了问题的新错误:第一个错误是因为默认情况下您无法插入到表的主键列,使用SET INDENTITY_INSERT很容易修复第二个列,这是因为您有一些外键规则,可能是因为您复制了值