SQL Table 值参数仅在其数据被复制到临时文件时才有效 table
SQL Table Value Parameter only works when its data is copied to temporary table
我正在使用 dapper 通过 C# 将数据传递到 sql 存储过程。
c#(使用小巧玲珑):
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
)
为了解决这个问题,我在下面的存储过程中使用了一个临时的 table,并且效果很好。
我的解决方案使用临时 table:
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
)
它运行良好并删除特定数据(不是所有数据)!
那么我的第一个查询有什么问题呢?
我不能告诉你,为什么它与临时 table 一起工作,但如果你将其更改为:
,问题来自 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 与其自身进行比较(这是我的猜测)。
我想为您的查询再补充两点:
最好在检查是否存在时 select 不是 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
)或不删除任何行(任何其他情况)。
如果你限定所有列引用,你将永远不会遇到这个问题。
我正在使用 dapper 通过 C# 将数据传递到 sql 存储过程。
c#(使用小巧玲珑):
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
)
为了解决这个问题,我在下面的存储过程中使用了一个临时的 table,并且效果很好。 我的解决方案使用临时 table:
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
)
它运行良好并删除特定数据(不是所有数据)! 那么我的第一个查询有什么问题呢?
我不能告诉你,为什么它与临时 table 一起工作,但如果你将其更改为:
,问题来自 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 与其自身进行比较(这是我的猜测)。
我想为您的查询再补充两点:
最好在检查是否存在时 select 不是 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
)或不删除任何行(任何其他情况)。
如果你限定所有列引用,你将永远不会遇到这个问题。