SQL表值参数仅在将其数据复制到临时表时有效

问题描述 投票:0回答:2

我正在使用dapper将数据通过c#传递到sql存储过程。

c#(使用dapper):

 var dt = new DataTable("dbo.TVPtype");
 dt.SetTypeName("dbo.TVPtype");
 dt.Columns.Add("ID", typeof(int));
 dt.Rows.Add(5);
_db.Execute("mysp", param: new { TVP = dt.AsTableValuedParameter("dbo.TVPtype"), commandType: CommandType.StoredProcedure);

下面的SQL查询不起作用,我也不知道为什么!并从mytable中删除所有数据。SQL存储过程:

CREATE PROCEDURE [dbo].[mysp] @TVP dbo.TVPtype READONLY AS
DELETE FROM [dbo].[mytable] WHERE NOT EXISTS
(
SELECT NULL FROM @TVP na
WHERE ID = na.ID
)

为了解决这个问题,我在存储过程中使用了一个临时表,例如波纹管,它工作得很好。我的解决方案使用临时表:

CREATE table temptb (ID int)
insert into temptb select * from @TVP

现在我在删除查询中使用temptb代替@TVP:

DELETE FROM [dbo].[mytable] WHERE NOT EXISTS
(
SELECT NULL FROM temptb na
WHERE ID = na.ID
)

它运作良好,可以删除特定数据(不是所有数据)!那么我的第一个查询出了什么问题?

c# sql stored-procedures table-valued-parameters
2个回答
1
投票

我不能告诉你,为什么它与临时表一起使用,但是如果将其更改为:,则问题出在DELETE语句中:

DELETE t1 
FROM [dbo].[mytable] AS t1 WHERE NOT EXISTS
(
    SELECT NULL FROM @TVP na
    WHERE t1.ID = na.ID
)

它将起作用。看到,当我向mytable条件添加别名时,就清楚了应该比较哪个ID。在您的示例中,它可能会将@TVP ID与自身进行比较(这是我的猜测)。

我想在您的查询中再添加两个点:

  1. 最好在检查存在性时选择不为NULL的内容,因为NULL在SQL中是特殊的(尽管它在您的示例中有效)。例如SELECT 1 FROM @TVP...更具可读性

  2. 为什么不这样做:

摘要:

DELETE FROM [dbo].[mytable] AS t1 WHERE ID NOT IN
(
    SELECT na.ID FROM @TVP na
)

1
投票

限定您的列名!您认为您正在写:

DELETE FROM [dbo].[mytable]
    WHERE NOT EXISTS (SELECT 1
                      FROM @TVP na
                      WHERE mytable.ID = na.ID
                     );

但是你不是。 SQL的范围规则将您的查询解释为:

DELETE FROM [dbo].[mytable]
    WHERE NOT EXISTS (SELECT 1
                      FROM @TVP na
                      WHERE na.ID = na.ID
                     );

这相当荒谬。 WHERE与外部查询不相关。它将删除所有行(如果@TVP为空或ID始终为NULL)或不删除任何行(任何其他情况)。

如果您有资格使用[[all列引用,您将永远不会遇到这个问题。

© www.soinside.com 2019 - 2024. All rights reserved.