据记录,递归变量赋值(像这样↓↓↓↓)有时会返回不正确的结果:
SELECT @var = @var + [Name]
FROM dbo.People
...
但即使在我确信只返回一行的情况下这是否适用?
示例(小提琴):
CREATE TABLE dbo.People
(
Id INT IDENTITY PRIMARY KEY,
[Name] varchar(50) NOT NULL
);
INSERT INTO dbo.People([Name]) VALUES ('Bob');
DECLARE @var varchar(50) = '';
/*Is this bad even if the query always returns only one row?*/
SELECT @var = @var + [Name]
FROM dbo.People
WHERE Id = 1;
SELECT [@var] = @var;
文档说“在这种情况下,不能保证@Var会逐行更新。 ”这(对我来说)表明单行选择应该没问题,对吗?
另一个可能想要使用它的例子是计算一些聚合值并将它们添加到某个地方:
SELECT @Total = @Total + SUM(InvoiceTotal) /*single row, aggregate of several values*/
FROM dbo.Invoice
WHERE ...;
/*Notice no GROUP BY*/
有时 也许
可能存在一些我还没有发现的情况,在单行分配时会出现意外行为,但单行保证(不需要通过 via
TOP
或OFFSET
/FETCH
过滤排序)相对较强。据我所知,问题主要出在古怪的更新和字符串连接上。字符串连接方法也是链接文档页面中给出的示例。这两者都依赖于未记录的行为,这充其量是危险的。可能发生变化并产生不正确结果的因素数量很难完全控制。
可能值得注意的是,您也可以通过使用来解决字符串连接问题
FOR XML PATH
。微软仅STRING_AGG
在其示例中进行了记录。在两种情况下,多行聚合对我来说更有趣:
关于 NULL
这里您实际上只需要小心地用一个值初始化您的起始变量。
第一个查询将返回 NULL,第二个查询将返回正确的总数。当然,在结果中遇到 NULL 值不会改变任何事情。
隔离
这实际上只适用于您需要真正的“时间点”聚合的情况,但这在很大程度上取决于您之后对总数的操作及其重要性。
考虑一下在大多数隔离级别下,即使是读已提交,在并发修改查询的情况下,您也可能会错过或重复计算行数。
为了避免这种情况,您需要使用快照或可序列化隔离,它们都明确禁止脏读、幻读和不可重复读。
读取已提交快照隔离也可能提供足够的结果,具体取决于您要避免什么以及需要读取的严格程度。