【问题标题】:passing parameters into SQL server - "must declare scalar variable"将参数传递到 SQL 服务器 - “必须声明标量变量”
【发布时间】:2023-03-16 06:06:01
【问题描述】:

编辑:

为了稍微清理一下 SQL,我按照 PeterHE 的建议编写了代码,并硬编码(仅用于测试)测试值。现在我收到一条新的错误消息:“in_eq_Equipment.in_eq_CategoryID_fk”无法绑定。”

这是修改后的测试代码:

    select in_eq_ID, in_eq_TagNumber as TagNumber, Title1Item, in_eq_AssetDescription as Description, in_eq_ExtendedDescription as ExtendedDescription, in_eq_SerialNumber as SerialNumber, in_eq_ValuationAmount as TotalValue, in_eq_CustodianName as Name, in_eq_ComplexBuilding as ShortLocation, in_eq_SubLocationCode as ShortRoomNumber, in_ca_Categories.in_ca_CategoryName as CategoryName, in_eq_DispositionDate as DispositionDate, Departments.DepartmentCode, DATEADD (dd, 0, DATEDIFF (dd, 0, in_eq_Equipment.in_eq_AcquisitionDate)) as AcquisitionDate

from in_eq_Equipment, Departments

LEFT JOIN in_ca_Categories ON in_eq_Equipment.in_eq_CategoryID_fk = in_ca_Categories.in_ca_CategoryID       
where in_eq_Equipment.DepartmentID = CAST ('00000000-0000-0000-0000-000000000000' AS nvarchar(36)) and upper (in_eq_AssetDescription) LIKE upper ('%T$')

生锈的老程序员回来了...尝试测试存储过程并遇到此错误消息。错误:

消息 137,级别 15,状态 2,第 18 行 必须声明标量变量“@DepartmentID”。

// 测试调用代码:

DECLARE @SearchString varchar(30), @DispositionText varchar(200)
declare @DepartmentID uniqueidentifier;

SET @SearchString = 'T'
SET @DispositionText = '';
SET @DepartmentID = '00000000-0000-0000-0000-000000000000';
EXEC GetInventoryByAssetDescription @SearchString, @DispositionText, @DepartmentID

实际的存储过程代码(是的,我知道这是不好的代码——我最初没有编写它,但必须在短时间内修改它):

USE [Inventory]
GO
/****** Object:  StoredProcedure [dbo].[GetInventoryByAssetDescription]    Script Date: 12/23/2019 9:09:51 AM ******/
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO
ALTER PROCEDURE [dbo].[GetInventoryByAssetDescription]
      (
            @SearchString varchar(30),
            @DispositionText varchar(200) = null,
            @DepartmentID uniqueidentifier
      )  
AS
begin
    SET NOCOUNT ON
    declare @sql nvarchar (2000)

    select @SearchString=UPPER(@SearchString)
    set @sql = '   select in_eq_ID,
        in_eq_TagNumber as TagNumber,
        Title1Item,
        in_eq_AssetDescription as Description,
        in_eq_ExtendedDescription as ExtendedDescription,
        in_eq_SerialNumber as SerialNumber,
        in_eq_ValuationAmount as TotalValue,
        in_eq_CustodianName as Name,
        in_eq_ComplexBuilding as ShortLocation,
        in_eq_SubLocationCode as ShortRoomNumber,
        in_ca_Categories.in_ca_CategoryName as CategoryName,
        in_eq_DispositionDate as DispositionDate,
        Departments.DepartmentCode,
        DATEADD (dd, 0, DATEDIFF (dd, 0, in_eq_Equipment.in_eq_AcquisitionDate)) as AcquisitionDate
        from in_eq_Equipment, Departments
        where in_eq_Equipment.DepartmentID = @DepartmentID
            LEFT JOIN in_ca_Categories ON in_eq_Equipment.in_eq_CategoryID_fk = in_ca_Categories.in_ca_CategoryID
            WHERE upper (in_eq_AssetDescription) LIKE upper ('''+ @SearchString + ''')
            '


        set  @sql=@sql+'   ' + ISNULL(@DispositionText,' ')  + '  order by in_eq_AssetDescription'
        execute (@sql)
        return
end

【问题讨论】:

  • 是的,这很糟糕。不仅糟糕 - 可怕。它有很多问题。重复的上层逻辑,老风格的join,设备和部门之间看似错误的join,dispositiontext很容易出错。最重要的是 - 容易受到 SQL INJECTION 的影响。
  • 如果允许修改存储过程,为什么要使用动态SQL,因为它性能很差。您的 SQL SELECT 中没有任何内容需要动态参数。一切都是直截了当的,仅从输入中获取。我想说,只需删除该动态 sql 逻辑,有一个普通的 SQL SELECT 语句,它只根据输入参数查询您各自的表。
  • 这个原始的 SQL 显然是在 2005 年创建的。我太生疏了,在时间线下,怀疑我有时间彻底清理它。只是试图在这一点上做一个小的修改。感谢您的意见。
  • 你所拥有的没有意义。 @DispositionText 是什么?你只是将它注入到你的语句中,这是没有意义的。你为什么交叉加入departments?不应该是INNER JOIN,然后将@DepartmentIDdepartments 表中的列进行比较吗?为什么你也有 2 个WHERE 子句?
  • “这个原始 SQL 显然是在 2005 年创建的”,并且在 2005 年永远不会工作。FROM TAble1, Table2 WHERE Table1.Col = 1 LEFT JOIN Table3 ON... 的语法完全错误。

标签: sql-server scalar


【解决方案1】:

动态 SQL 非常糟糕。我只是提供防止您看到的错误的代码:

USE [Inventory]
GO
/****** Object:  StoredProcedure [dbo].[GetInventoryByAssetDescription]    Script Date: 12/23/2019 9:09:51 AM ******/
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO
ALTER PROCEDURE [dbo].[GetInventoryByAssetDescription]
      (
            @SearchString varchar(30),
            @DispositionText varchar(200) = null,
            @DepartmentID uniqueidentifier
      )  
AS
begin
    SET NOCOUNT ON
    declare @sql nvarchar (2000)

select @SearchString=UPPER(@SearchString)
set @sql = '   select in_eq_ID,
    in_eq_TagNumber as TagNumber,
    Title1Item,
    in_eq_AssetDescription as Description,
    in_eq_ExtendedDescription as ExtendedDescription,
    in_eq_SerialNumber as SerialNumber,
    in_eq_ValuationAmount as TotalValue,
    in_eq_CustodianName as Name,
    in_eq_ComplexBuilding as ShortLocation,
    in_eq_SubLocationCode as ShortRoomNumber,
    in_ca_Categories.in_ca_CategoryName as CategoryName,
    in_eq_DispositionDate as DispositionDate,
    Departments.DepartmentCode,
    DATEADD (dd, 0, DATEDIFF (dd, 0, in_eq_Equipment.in_eq_AcquisitionDate)) as AcquisitionDate
    from in_eq_Equipment, Departments
        LEFT JOIN in_ca_Categories ON in_eq_Equipment.in_eq_CategoryID_fk = in_ca_Categories.in_ca_CategoryID
        WHERE 
           in_eq_Equipment.DepartmentID = '''+CAST(@DepartmentID AS nvarchar(36))+'''
           upper (in_eq_AssetDescription) LIKE upper ('''+ @SearchString + ''')' --searchString is already converted UPPER CASE at the beginning, not sure why its converted here again.

    set  @sql=@sql+'   ' + ISNULL(@DispositionText,' ')  + '  order by in_eq_AssetDescription'
    execute (@sql)
    return

结束

下面是重构的存储过程,它直接接受您的 SQL 输入并返回结果集:

USE [Inventory]
GO
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO
ALTER PROCEDURE [dbo].[GetInventoryByAssetDescription]
      (
            @SearchString varchar(30),
            @DispositionText varchar(200) = null,
            @DepartmentID uniqueidentifier
      )  
AS
begin
    SET NOCOUNT ON

    SET @SearchString = '''%' + UPPER('test') + ''''
select in_eq_ID
       , in_eq_TagNumber as TagNumber
       , Title1Item
       , in_eq_AssetDescription as [Description]
       , in_eq_ExtendedDescription as ExtendedDescription
       , in_eq_SerialNumber as SerialNumber
       , in_eq_ValuationAmount as TotalValue
       , in_eq_CustodianName as [Name]
       , in_eq_ComplexBuilding as ShortLocation
       , in_eq_SubLocationCode as ShortRoomNumber
       , in_ca_Categories.in_ca_CategoryName as CategoryName
       , in_eq_DispositionDate as DispositionDate
       , Departments.DepartmentCode
       , DATEADD (dd, 0, DATEDIFF (dd, 0, in_eq_Equipment.in_eq_AcquisitionDate)) as AcquisitionDate
from      in_eq_Equipment
JOIN      Departments      ON in_eq_equipment.departmentid = departments.id
LEFT JOIN in_ca_Categories ON in_eq_Equipment.in_eq_CategoryID_fk = in_ca_Categories.in_ca_CategoryID       
where (in_eq_Equipment.DepartmentID =  @DepartmentID OR @DepartmentID = '00000000-0000-0000-0000-000000000000') 
     and upper (in_eq_AssetDescription) LIKE @SearchString 
     --AND ISNULL(@DispositionText,' ') = ' '  -- Not able to understand this condition from your dynamic SQL
order by in_eq_AssetDescription  
        return
end

【讨论】:

  • 我实际上正在重构代码,但是您的第二个代码片段遇到了与我相同的错误:Msg 4104, Level 16, State 1, Procedure GetInventoryByAssetDescription, Line 33无法绑定零件标识符“in_eq_Equipment.in_eq_CategoryID_fk”。
  • @Brian 我的第二个代码片只是用直接 SQL 而不是动态 SQL 替换来自 POST 的相同查询。该错误意味着,在 in_eq_Equipment 表中找不到 in_eq_CategoryID_fk 列。在不了解您的表结构的情况下,很难修复该错误。
  • 谢谢,Sam... 但 in_eq_CategoryID_fk 实际上在 in_eq_Equipment 表中。这就是造成我困惑的原因。
  • in_eq_Equipment、部门加入情况如何?
  • in_eq_equipment.departmentid = department.id 我现在正在重写 SP……因为我太生疏了,所以需要一点时间。 :(
【解决方案2】:

where 子句:

where in_eq_Equipment.DepartmentID = @DepartmentID

应该是:

where in_eq_Equipment.DepartmentID = '''+CAST(@DepartmentID AS nvarchar(36))+'''

附: 看起来您根本不需要动态 sql。此外,即使您需要,也应更改为使用带参数的 sp_executesql。

【讨论】:

  • 不,不应该,应该是参数。这使 SP 对注入敞开了大门。这个答案是危险的,因为它没有解决问题,实际上进一步支持注入..
  • 但彼得的建议确实纠正了 OP 的错误。这不是最好的解决方案,但它确实有效。
  • 它确实“工作”,是的@SMor,就像WHILE 用于将许多行从一个表插入到另一个表,或使用胶粘剂修复泄漏的管道。但这并不意味着你应该这样做。
  • 是的,但您实际上应该证明这一点,@PeterHe。特别是因为 SQL 根本不是动态的;实际上不需要注入,因此存在巨大的安全漏洞。
  • @Larnu,没有。他需要尝试一下。答案给出了解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多