如何使用 Table 变量设置 ExecuteSqlCommand

How to set ExecuteSqlCommand with Table variable

我想通过 C# 应用程序 运行 查询。在应用程序之外没有执行此操作的选项。我有以下代码:

var keyGroupsToCleanUp = new List<string>
{
    "Address",
    "Manufacturer",
    "Product",
    "Customer",
    "Picture",
    "Category",
    "Vendor",
    "SS_A_Attachment",
    "SS_A_AttachmentDownload",
    "SS_MAP_EntityMapping",
    "SS_MAP_EntityWidgetMapping",
};

foreach (var keyGroup in keyGroupsToCleanUp)
{
    _databaseFacade.ExecuteSqlCommand($@"
    DELETE
    FROM GenericAttribute
    WHERE KeyGroup = {keyGroup} AND [Key] = 'CommonId'
    AND EntityId NOT IN (SELECT Id FROM [{keyGroup}]);
    ");
}

我想遍历列表中的每个名称,运行 下面的查询针对每个名称。当我尝试这样做时,我收到以下错误:

System.Data.SqlClient.SqlException (0x80131904): Invalid object name '@p1'.

根据我在网上搜集到的信息,这是因为Table名称不能是字符串。您必须声明一个变量并将此变量用于 table 名称。我了解到 Table 变量包含需要声明的列,我感到一阵恐惧。 None 个 table 具有相同的列结构。

我想做的事情可行吗?如果可以,我该怎么做?


GenericAttributes table 是一个很大的 table,由六列组成。

在我加入这个正在使用的项目的时候,已经用到无可替代的地步了。您可以在此处通过将 KeyGroup 指定为数据库 table 来为数据库 table 保存其他数据。我们有一个名为“地址”的 table,我们在地址的 GenericAttributes table 中保存了额外的数据(我知道这没有意义)。这会导致很多问题,因为关系数据库不适用于此。我在上面编写的查询查找现在已分离的 GenericAttributes Table 中的行。例如,EntityId 0 在 Address 中不作为 Id 存在,因此将在此处返回。然后必须删除该行,因为它链接到不存在的 entityId。

这是实现此目的的查询示例:

// Address
_databaseFacade.ExecuteSqlCommand(@"
DELETE
FROM GenericAttribute
WHERE KeyGroup = 'Address' AND [Key] = 'CommonId'
AND EntityId NOT IN (SELECT Id FROM [Address]);
");

我必须这样做 11 table 秒,所以我想让它更容易一些。每个查询都以相同的方式编写。唯一改变的是 KeyGroup 和它寻找的 table。它们将始终具有相同的名称。

这是另一个调用产品的示例。它们是相同的,唯一的区别是 KeyGroup 和 NOT IN 语句中的 Table。

// Product
_databaseFacade.ExecuteSqlCommand(@"
DELETE
FROM GenericAttribute
WHERE KeyGroup = 'Product' AND [Key] = 'CommonId'
AND EntityId NOT IN (SELECT Id FROM Product);
");

尝试以下操作:

foreach (var keyGroup in keyGroupsToCleanUp)
{
    var sql = @"DELETE FROM GenericAttribute
                WHERE KeyGroup = @Group
                AND [Key] = 'CommonId'
                AND EntityId NOT IN (SELECT Id FROM @Group)"; // Or [@Group], depends on schema

    _databaseFacade.ExecuteSqlCommand(
        sql,
        new SqlParameter("@Group", keyGroup));

此代码假定您的门面中的 ExecuteSqlCommand 遵循标准的 Microsoft 模式(与 Microsoft 的模式相同)。

据推测,您正在使用 Entity Framework Core。 ExecuteSqlCommand 方法接受 FormattableString,并将任何占位符转换为命令参数。但是您的占位符似乎是 column/table 名称,不能作为参数传递。

由于还有一个接受 string 的重载,它具有不同的行为,此方法已被标记为已过时,并由 ExecuteSqlInterpolatedExecuteSqlRaw 取代。

假设您的 none 值会受到用户的影响,并且您很高兴不会引入 a SQL Injection vulnerability,您可以改用 ExecuteSqlRaw

_databaseFacade.ExecuteSqlRaw($@"
    DELETE
    FROM GenericAttribute
    WHERE KeyGroup = [{keyGroup}] AND [Key] = 'CommonId'
    AND EntityId NOT IN (SELECT Id FROM [{keyGroup}]);
    ");

为了确保没有注入漏洞,可以使用动态SQL和QUOTENAME

_databaseFacade.ExecuteSqlRaw(@"
DECLARE @sql nvarchar(max) = N'
    DELETE
    FROM GenericAttribute
    WHERE KeyGroup = @keyGroup AND [Key] = ''CommonId''
    AND EntityId NOT IN (SELECT Id FROM ' + {0} + ');
';

EXEC sp_executesql @sql,
    N'@keyGroup nvarchar(100)',
    @keyGroup = {0};
    ", keyGroup);

请注意 ExecuteSqlRaw 将如何插入字符串。 不要自己用$

插值