目标是创建 2 个存储过程。
sp_InsertNewMenuItem
,检查MenuTitle
和MenuGroupText
是否存在。如果有,请使用 id
,如果没有,请为每个创建一个新菜单项。
它的目标是为第二个过程创建一个新的菜单项:
sp_InsertIngredient
,它从第一个过程中创建并将成分插入到菜单项中。
我已经解决了大部分问题。第一个过程检查它是否存在并执行其操作。我可以插入第二个程序中的成分。
问题出在创建的表上。
条目混杂在几个不同的行中
所以支票有效。我尝试弄乱身份,因为我们必须使用身份来完成这项任务。即使我只有主ID为identity(1,1),也会出现同样的问题。我不知道该怎么办。
我不是在找人帮我做这件事,我只是在寻求帮助。尽管我进行了研究和其他尝试,但我真的不知道该怎么做。
这是我的代码。
表格:
CREATE TABLE [dbo].[Menu]
(
[ID] INT PRIMARY KEY IDENTITY(1,1),
[MenuTitle] [nvarchar](50) NULL,
[MenuDescriptionText] [nvarchar](50) NULL
)
CREATE TABLE [dbo].[MenuGroups]
(
[ID] INT PRIMARY KEY IDENTITY(1,1),
[MenuID] INT NULL,
[MenuGroupText] [nvarchar](50) NULL,
[MenuGroupDescriptionText] [nvarchar](50) NULL,
FOREIGN KEY (MenuID) REFERENCES Menu(ID)
)
CREATE TABLE [dbo].[MenuItems]
(
[ID] INT PRIMARY KEY IDENTITY(1,1),
[MenuGroupsID] INT NULL,
[MenuItemTitle] [nvarchar](50) NULL,
[MenuItemDescriptionText] [nvarchar](50) NULL,
FOREIGN KEY (MenuGroupsID) REFERENCES MenuGroups(ID)
)
CREATE TABLE [dbo].[Ingredients]
(
[ID] INT PRIMARY KEY IDENTITY(1,1),
[IngredientTitleText] [NVARCHAR](50) NULL,
[IngredientDescriptionText] [NVARCHAR](200) NULL
)
CREATE TABLE [dbo].[IngredientsInItems]
(
[ID] INT PRIMARY KEY IDENTITY(1,1),
[IngredientsID] INT NULL,
[MenuItemID] INT NULL,
[DescriptionText] [NVARCHAR](200),
FOREIGN KEY (IngredientsID) REFERENCES Ingredients(ID),
FOREIGN KEY (MenuItemID) REFERENCES MenuItems(ID)
)
程序:
CREATE PROCEDURE Sp_insertnewmenuitem
(@MenuTitle NVARCHAR(50),
@MenuGroupText NVARCHAR(50),
@MenuItemTitle NVARCHAR(50))
AS
BEGIN
DECLARE @MenuID INT,
@MenuGroupID INT
IF NOT EXISTS (SELECT 1
FROM [dbo].[menu]
WHERE [menutitle] = @MenuTitle)
BEGIN
INSERT INTO [dbo].[menu] ([menutitle])
VALUES (@MenuTitle)
SET @MenuID = SCOPE_IDENTITY()
END
ELSE
BEGIN
SET @MenuID = (SELECT [id]
FROM [dbo].[menu]
WHERE [menutitle] = @MenuTitle)
END
IF NOT EXISTS (SELECT 1
FROM [dbo].[menugroups]
WHERE [menugrouptext] = @MenuGroupText
AND [menuid] = @MenuID)
BEGIN
INSERT INTO [dbo].[menugroups] ([menuid], [menugrouptext])
VALUES (@MenuID, @MenuGroupText)
SET @MenuGroupID = SCOPE_IDENTITY()
END
ELSE
BEGIN
SET @MenuGroupID = (SELECT [id]
FROM [dbo].[menugroups]
WHERE [menugrouptext] = @MenuGroupText
AND [menuid] = @MenuID)
END
INSERT INTO [dbo].[menuitems] ([menugroupsid], [menuitemtitle])
VALUES (@MenuGroupID, @MenuItemTitle)
END
CREATE PROCEDURE Sp_insertingredient
(@IngredientTitleText NVARCHAR(50))
AS
BEGIN
DECLARE @IngredientID INT,
@MenuItemID INT
IF NOT EXISTS (SELECT 1
FROM [dbo].[ingredients]
WHERE [ingredienttitletext] = @IngredientTitleText)
BEGIN
INSERT INTO [dbo].[ingredients] ([ingredienttitletext])
VALUES (@IngredientTitleText)
SET @IngredientID = SCOPE_IDENTITY()
END
ELSE
BEGIN
SET @IngredientID = (SELECT [id]
FROM [dbo].[ingredients]
WHERE [ingredienttitletext] = @IngredientTitleText)
END
INSERT INTO [dbo].[ingredientsinitems] ([ingredientsid], [menuitemid])
VALUES (@IngredientID, @MenuItemID)
END
您的程序不正确,会产生一些误报数据...
开发人员在开发数据库过程时总是忘记两个基本的事情:
并发:如果两个不同的用户同时启动同一个程序会发生什么
基于集合的处理:数据库不根据迭代代码进行操作,而是作为集合转换
举个例子,这段代码:
IF NOT EXISTS (SELECT 1
FROM [dbo].[ingredients]
WHERE [ingredienttitletext] = @IngredientTitleText)
BEGIN
INSERT INTO [dbo].[ingredients] ([ingredienttitletext])
VALUES (@IngredientTitleText)
同时执行将插入两行...
Time 0 | User A | User B
| SELECT 1 | SELECT 1
| FROM ingredients | FROM ingredients
| WHERE ... | WHERE ...
Time 1 | INSERT INTO ingredients | < wait because INSERT locks>
| VALUES (@IngredientTitleText) | ...
Time 2 | | INSERT INTO ingredients
| | VALUES (@IngredientTitleText)
将给出重复条目。因为读者可以多个人同时阅读....
避免重复的唯一方法是使用 UNIQUE 约束。没有别的办法了!