【问题标题】:How do I check that values are being inserted into a table when the result of the insert shows nothing?当插入结果没有显示时,如何检查值是否被插入到表中?
【发布时间】:2019-12-10 15:45:49
【问题描述】:

我遇到了一个关于应该将数据插入三个表的 SQL Server 过程的问题。下面的代码显示该过程接收参数,然后将所述参数插入三个表中:Address 表、D.O.B 表和Users 表。

忽略与其他东西有关的盐,我的问题是,当我从 ASP.NET MVC 文件运行此过程时,没有任何内容插入到 Users 表中,即使 Address 和 @ 987654326@ 表都插入了各自的值。我之前遇到过这样的问题,但问题已解决,因为问题是当我对其使用 HASHBYTES 过程时,其中一个值返回 NULL,但是,我认为没有什么可以做类似的事情。

ALTER PROCEDURE [dbo].[StoreDetails]
    @FirstName VARCHAR(50),
    @Surname VARCHAR(50),
    @Password VARCHAR(100),
    @PhoneNumber VARCHAR(50),
    @Email VARCHAR(100), 
    @IsAdmin BIT,
    @Address VARCHAR(100),
    @DOB DATE
AS
BEGIN 
    SET NOCOUNT ON

    DECLARE @Salt UNIQUEIDENTIFIER = NEWID()

    INSERT INTO dbo.OAddress(Address)
    VALUES (@Address)

    INSERT INTO dbo.ODOB(DOB)
    VALUES (@DOB)

    INSERT INTO dbo.OUsers (FirstName, Surname, Password, Salt, PhoneNumber, Email, IsAdmin, AddressID, DOBID)
    VALUES (@FirstName, @Surname, @Password, @Salt, @PhoneNumber, @Email, @IsAdmin, SCOPE_IDENTITY(), SCOPE_IDENTITY())
END

我相信 C# 方面确实按预期工作,不久前我设法让这段代码工作,直到我更改存储过程以尝试更改 Password 参数以使用 HASHBYTES 进行散列但是我决定只是恢复到它正常工作的时候,但你可以看到我失败了。

【问题讨论】:

  • 您可能遇到外键违规。您正在使用 SCOPE_IDENTITY 作为地址和 DOB id,这不太可能是您想要的。
  • 您是否尝试从 Sql Server Management Studio 调用此 SP?
  • 您一定是从 SQL 服务器端收到错误,从 SQL 服务器管理工​​作室运行程序以了解确切的问题,该问题可能是由于外键限制。
  • 您对上一次插入中的那 两个 SCOPE_IDENTITY() 调用有何期望?这看起来很腥......
  • @marc_s 调用 SCOPE_IDENTITY() 过程是因为它是一种获取分配给 OUser 表中实体的正确外键的复杂方式。

标签: c# sql-server model-view-controller


【解决方案1】:

您可能需要几个OUTPUT 子句和一些表变量:

ALTER PROCEDURE [dbo].[StoreDetails] @FirstName varchar(50),
                                     @Surname varchar(50),
                                     @Password varchar(100),
                                     @PhoneNumber varchar(50),
                                     @Email varchar(100),
                                     @IsAdmin bit,
                                     @Address varchar(100),
                                     @DOB date
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @Salt uniqueidentifier = NEWID();
    DECLARE @AddressID table (ID int);
    DECLARE @DOBID table (ID int);

    INSERT INTO dbo.OAddress ([Address])
    OUTPUT inserted.ID
    INTO @AddressID (ID) --Guessed named for inserted
    SELECT @Address;

    INSERT INTO dbo.ODOB (DOB)
    OUTPUT inserted.ID
    INTO @DOBID (ID) --Guessed named for inserted
    SELECT @DOB;

    INSERT INTO dbo.OUsers (FirstName,
                            Surname,
                            Password,
                            Salt,
                            PhoneNumber,
                            Email,
                            IsAdmin,
                            AddressID,
                            DOBID)
    SELECT @FirstName,
           @Surname,
           @Password,
           @Salt,
           @PhoneNumber,
           @Email,
           @IsAdmin,
           A.ID,
           D.ID
    FROM @AddressID A
         CROSS JOIN @DOBID D;
END;

请注意我在 OUTPUT 子句中所做的 cmets。

【讨论】:

  • 没有理由使用插入/选择语句来插入由标量变量填充的单行。不要添加使用输出子句的额外复杂性。然后交叉连接输出子句中使用的表。比需要的复杂得多。正确使用 SCOPE_IDENTITY 会更适合这种情况和 OP 的技能水平。
  • 您仍然需要在每个 INSERT, @SMor 之后将其分配给一个变量。但是,仅仅因为您认为 OP 技能水平不会理解 OUTPUT 子句并不意味着不会,这表明他们将如何处理需要 INSERT 多行的过程。但是,OUTPUT 并不是一个复杂的子句。
  • @Larnu 恐怕 SMor 是对的,我的 SQL 技能水平非常初学者。我想我理解 OUTPUT 子句,但如果我错了,请纠正我:地址被插入到地址表中,并且 OUTPUT 返回插入地址的 ID。然而,我完全忘记了 FROM 和 CROSS JOIN 部分。
  • FROM 是 SQL 中一个非常基本的关键字 @Displaza ;如果您不熟悉该关键字,那么您的 SQL 知识缺少一些 非常 基本部分,我建议您先阅读基本的 SELECT 语句(其中将包括 FROM) .至于CROSS JOIN,这将创建来自 2 个表的行的Cartesian Product(在这种情况下,无论如何都只会包含 1 行)。
  • @DOBID 是一张桌子,@Displaza,所以它与普通的FROM 没有什么不同。 INNER JOIN 需要 ON 子句,但@DOBID 中的行与@AddressID 中的行之间没有内在关系,除了@987654339 中尚不存在的行@;因此您不能使用INNER JOINON A.ID = D.ID 不会是正确的,因为两行很容易有不同的 ID 值。
【解决方案2】:

您需要弄清楚数据库告诉您的问题是什么。

在您的代码中放置一个断点,然后获取变量包含的所有值,这些值包含在您的参数中。然后切换到 SQL,设置所有变量,并尝试运行过程中的语句,并获得错误消息。像这样的:

declare @FirstName VARCHAR(50) = 'John'
declare @Surname VARCHAR(50) = 'Smith'
declare @Password VARCHAR(100) = 'Hek3$s*aSf8'
declare @PhoneNumber VARCHAR(50) = '333-333-3333'
declare @Email VARCHAR(100) = 'john@mailinator.com'
declare @IsAdmin BIT = 0
declare @Address VARCHAR(100) = '100 Somewhere Street, Somewheretown'
declare @DOB date = '2019-10-10'

DECLARE @Salt UNIQUEIDENTIFIER=NEWID()

INSERT INTO dbo.OAddress(Address)
VALUES(@Address)

INSERT INTO dbo.ODOB(DOB)
VALUES(@DOB)

INSERT INTO dbo.OUsers(FirstName,Surname,Password,Salt,PhoneNumber,Email,IsAdmin,AddressID,DOBID)
VALUES(@FirstName,@Surname,@Password,@Salt,@PhoneNumber,@Email,@IsAdmin,SCOPE_IDENTITY(),SCOPE_IDENTITY())

我认为我们可能需要怀疑 SCOPE_IDENTITY() 函数调用,但只有一种方法可以找出答案:运行它并查看。

【讨论】:

  • 是的,我使用一些假变量执行了该过程,它返回“INSERT 语句与 FOREIGN KEY 约束“FK_OUsers_OAddress”冲突。'
  • 这意味着SCOPE_IDENTITY() 没有以正确的顺序获取正确的键值。如果我们看一下操作的顺序,我们会:1) 插入 OAddress,然后 2) 插入 ODOB。但随后在插入语句中,我们向SCOPE_IDENTITY() 询问地址ID,但由于最后一次插入是到ODOB,所以我们得到了DOBID,并且繁荣--FK 冲突。您可以尝试将插入语句中的AddressID,DOBID 切换为DOBID,AddressID
【解决方案3】:

这里的问题是SCOPE_IDENTITY()的使用。这将返回插入语句生成的最后一个标识值。在您的代码中,SCOPE_IDENTITY() 返回为 dbo.oDOB 插入的值。

为了使其工作,您需要在每个插入语句之后将SCOPE_IDENTITY() 的值捕获到局部变量中,然后在最终插入到dbo.oUsers 中使用这些值。

以下内容应该可以满足您的需求:

ALTER PROCEDURE [dbo].[StoreDetails]
    @FirstName VARCHAR(50),
    @Surname VARCHAR(50),
    @Password VARCHAR(100),
    @PhoneNumber VARCHAR(50),
    @Email VARCHAR(100), 
    @IsAdmin BIT,
    @Address VARCHAR(100),
    @DOB DATE
AS
BEGIN 
    SET NOCOUNT ON

    DECLARE @addressKey INT,
            @dobKey INT;

    DECLARE @Salt UNIQUEIDENTIFIER = NEWID();

    INSERT INTO dbo.OAddress(Address)
    VALUES (@Address);

    SET @addressKey = SCOPE_IDENTITY();

    INSERT INTO dbo.ODOB(DOB)
    VALUES (@DOB);

    SET @dobKey = SCOPE_IDENTITY();

    INSERT INTO dbo.OUsers (FirstName, Surname, Password, Salt, PhoneNumber, Email, IsAdmin, AddressID, DOBID)
    VALUES (@FirstName, @Surname, @Password, @Salt, @PhoneNumber, @Email, @IsAdmin, @addressKey, @dobKey)
END

【讨论】:

    【解决方案4】:

    首先,您应该使用参数运行存储过程,以准确了解错误消息。

    其次,您应该handle error message 内部存储过程并在每次插入后检查。 例如:

    INSERT INTO dbo.OAddress(Address)
    VALUES(@Address)
    
    IF @@ERROR <> 0
    BEGIN
          PRINT N'A check constraint violation occurred.';
          Return;
    END
    GO
    

    但是,您还应该使用 out put param 然后 RETURN @@ERROR 来检查 c# 是否插入成功

    【讨论】:

      猜你喜欢
      • 2022-01-15
      • 1970-01-01
      • 1970-01-01
      • 2015-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-11
      • 2016-09-17
      相关资源
      最近更新 更多