使用动态 SQL 定位行时出现问题

use*_*054 1 sql-server stored-procedures dynamic-sql t-sql

我一般是 SQL 新手,所以我在设计的一些存储过程中遇到了一些问题。我的程序接受 3 个参数。位置(或表名基本上是 varchar)、房间号 (varchar) 和 'xxx-xxxx' 形式的电话号码 (char)。这是该过程的代码。

ALTER PROCEDURE [dbo].[AddPhoneNumberToRoom]
    @Location    varchar(25),
    @Room        varchar(10),
    @PhoneNumber char(8)

AS
BEGIN
SET NOCOUNT ON;

    DECLARE @String varchar(100);
    SELECT @String = ' UPDATE ' + @Location + 
                     ' SET PhoneNumber = ' + @PhoneNumber +
                     ' WHERE Room = ' + @Room;
    EXEC(@String);
END
Run Code Online (Sandbox Code Playgroud)

我收到的错误是:

UPDATE 语句与 FOREIGN KEY 约束“FK_Apartments_PhoneNumbers”冲突。冲突发生在数据库“CMPhone”、表“dbo.PhoneNumbers”、列“PhoneNumber”中。

电话号码在 PhoneNumber 表中,所以当我这样做时:

UPDATE Apartments SET PhoneNumber='123-4567' WHERE Room='2'
Run Code Online (Sandbox Code Playgroud)

它工作得很好。该PhoneNumbers表还看起来是这样的,只是在它的一个数字。

PhoneNumber
-----------
123-4567
Run Code Online (Sandbox Code Playgroud)

Aar*_*and 5

PRINT @String;将产生它所说的PhoneNumber = 123-4567(它把它当作一个表达式,123减去4567,它等于-4444),而不是PhoneNumber = '123-4567'像你硬编码的例子。所以你应该用两个单引号来转义你附加的值:

SELECT @String = ' UPDATE ' + @Location + 
                 ' SET PhoneNumber = ''' + @PhoneNumber + ''' 
                   WHERE Room = ''' + @Room + ''';';
Run Code Online (Sandbox Code Playgroud)

但更好的是,为了更好地保护自己免受 SQL 注入(请参阅此处此处此处),您应该 (a) 检查该@Location表是否有效,(b)QUOTENAME()无论如何,以及 (c) 传入其他两个值作为正确键入的参数,而不是将它们附加到字符串:

IF OBJECT_ID(N'dbo.' + @Location) IS NOT NULL
BEGIN
  DECLARE @sql NVARCHAR(MAX);
  SET @sql = N'UPDATE dbo.' + QUOTENAME(@Location)
           + N' SET PhoneNumber = @PhoneNumber
                WHERE Room = @Room;';
  EXEC sys.sp_executesql @sql, 
      N'@PhoneNumber CHAR(8), @Room VARCHAR(10)',
      @PhoneNumber, @Room;
END
Run Code Online (Sandbox Code Playgroud)

更妙的是,您不应该为每个Location恕我直言有一个单独的表- 位置应该是一列。那么您根本不需要动态 SQL。