我正在使用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
)
它运作良好,可以删除特定数据(不是所有数据)!那么我的第一个查询出了什么问题?
我不能告诉你,为什么它与临时表一起使用,但是如果将其更改为:,则问题出在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与自身进行比较(这是我的猜测)。
我想在您的查询中再添加两个点:
最好在检查存在性时选择不为NULL的内容,因为NULL在SQL中是特殊的(尽管它在您的示例中有效)。例如SELECT 1 FROM @TVP...
更具可读性
为什么不这样做:
摘要:
DELETE FROM [dbo].[mytable] AS t1 WHERE ID NOT IN
(
SELECT na.ID FROM @TVP na
)
限定您的列名!您认为您正在写:
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列引用,您将永远不会遇到这个问题。