在SQL Server update语句中使用相关子查询会产生意外结果

在SQL Server update语句中使用相关子查询会产生意外结果,sql,sql-server,correlated-subquery,Sql,Sql Server,Correlated Subquery,我将主键列引入到一个还没有主键列的表中。在添加默认值为0的正常字段Id int后,我尝试使用以下update语句为每个记录创建唯一的值: update t1 set t1.id = (select count(*) from mytable t2 where t2.id <> t1.id) from mytable t1 我希望为每一行执行子查询,因为我引用的是t1。每次执行子查询时,计数应该少一个,但它不起作用 结果是每个记录中的Id仍然为0。我以前曾在其他DBMS上成功地使用过

我将主键列引入到一个还没有主键列的表中。在添加默认值为0的正常字段Id int后,我尝试使用以下update语句为每个记录创建唯一的值:

update t1
set t1.id = (select count(*) from mytable t2 where t2.id <> t1.id)
from mytable t1
我希望为每一行执行子查询,因为我引用的是t1。每次执行子查询时,计数应该少一个,但它不起作用

结果是每个记录中的Id仍然为0。我以前曾在其他DBMS上成功地使用过它。我在这里使用的是SQLServer2008


如何为每个记录生成唯一值并更新Id字段?

SQL正在使用Id不等于0的记录数更新每一行。由于所有行ID都等于0,因此没有不等于0的行,因此不会更新任何内容

请尝试在此处查看此答案:


SQL正在使用ID不等于0的记录数更新每一行。由于所有行ID都等于0,因此没有不等于0的行,因此不会更新任何内容

请尝试在此处查看此答案:

试图解释为什么它不能像您预期的那样工作:

我希望为每一行执行子查询,因为我引用的是t1

它会被执行,并且会影响所有行。但UPDATE语句是一条语句,如果有WHERE子句,它将作为一条语句执行,影响整个表或其中的一部分

每次执行子查询时,计数应该少一个,但它不起作用

您希望执行更新时,每行对子查询进行一次计算。但它是一个首先对所有受影响的行求值的语句,然后对行进行更改和更新。DBMS可能会以其他方式执行此操作,但结果应该是这样的

结果是每个记录中的Id仍然为0

当所有行在执行前都具有相同的0值时,这就是此语句的正确和预期行为。计数*是0

我以前曾在其他DBMS上成功地使用过它

我的猜测是,您已经在MySQL中使用了它。更正/更新:我猜错了,这个更新语法对MySQL无效,显然查询在Firebird中工作正常。在该DBMS中,更新不以标准方式工作。正如您所了解的那样,它可以一行一行地工作,而不是使用完整的表

我在这里使用的是SQLServer2008

此DBMS在更新时正常工作。您可以编写一个不同的Update语句来获得所需的结果,或者更好地使用自动生成的标识列,正如其他人所建议的那样

试图解释为什么它不能像您期望的那样工作:

我希望为每一行执行子查询,因为我引用的是t1

它会被执行,并且会影响所有行。但UPDATE语句是一条语句,如果有WHERE子句,它将作为一条语句执行,影响整个表或其中的一部分

每次执行子查询时,计数应该少一个,但它不起作用

您希望执行更新时,每行对子查询进行一次计算。但它是一个首先对所有受影响的行求值的语句,然后对行进行更改和更新。DBMS可能会以其他方式执行此操作,但结果应该是这样的

结果是每个记录中的Id仍然为0

当所有行在执行前都具有相同的0值时,这就是此语句的正确和预期行为。计数*是0

我以前曾在其他DBMS上成功地使用过它

我的猜测是,您已经在MySQL中使用了它。更正/更新:我猜错了,这个更新语法对MySQL无效,显然查询在Firebird中工作正常。在该DBMS中,更新不以标准方式工作。正如您所了解的那样,它可以一行一行地工作,而不是使用完整的表

我在这里使用的是SQLServer2008


此DBMS在更新时正常工作。您可以编写一个不同的Update语句来获得所需的结果,或者更好地使用自动生成的标识列,正如其他人所建议的那样

为什么不添加一个ID INT IDENTITY1,1列??然后SQL Server将自动为ID创建唯一的值,您根本不需要更新任何内容……谢谢。我试试看。我使用许多不同的DBMS,所以我总是尝试最通用的方法。为什么不添加一个ID INT IDENTITY1,1列??然后SQL Server将自动为ID创建唯一的值,您根本不需要更新任何内容……谢谢。我试试看。我使用许多不同的DBMS,所以我总是尝试最通用的方法。谢谢你详细的回复。我在等着一排的李
你在Firebird DBMS中也说过,你的账户听起来很有道理:但我必须问:mySQL中的相同更新真的能像OP预期的那样工作吗?只是SQL的工作原理并非如此!你完全正确。我不知道MySQL在这种情况下的行为。查询不能像MySQL中那样工作。我错了。语法是不同的更新。。。从…起设置并且不能同时将同一个表指定为Update的目标和WHERE子句内的表。@onedaywhen:此查询在MySQL中不起作用。但是MySQL的更新是以非标准方式工作的。例如,您不能更新t SET id=id+1,如果id是唯一的或主键,您将得到重复的主键错误。但是,您可以这样做:更新t SET id=id+1 ORDER BY id DESC此方式不重复,这清楚地表明行已更新,约束已逐行检查!谢谢你详细的答复。我期待着一行一行的行为,就像你在Firebird DBMS中所说的那样。你的帐户听起来似乎很有道理:但我必须问:mySQL中的相同更新真的能像预期的那样工作吗?只是SQL的工作原理并非如此!你完全正确。我不知道MySQL在这种情况下的行为。查询不能像MySQL中那样工作。我错了。语法是不同的更新。。。从…起设置并且不能同时将同一个表指定为Update的目标和WHERE子句内的表。@onedaywhen:此查询在MySQL中不起作用。但是MySQL的更新是以非标准方式工作的。例如,您不能更新t SET id=id+1,如果id是唯一的或主键,您将得到重复的主键错误。但是,您可以这样做:更新t SET id=id+1 ORDER BY id DESC此方式不重复,这清楚地表明行已更新,约束已逐行检查!相反,我使用此脚本更新Id字段:DECLARE count INT SET count=SELECT count*FROM MYTABLE,其中Id=0,而count>0 BEGIN update TOP 1 MYTABLE SET Id=@count,其中Id=0 SET count=SELECT count*FROM MYTABLE,其中Id=0 end。相反,我使用此脚本更新Id字段:DECLARE count INT SETcount=从MYTABLE中选择count*,其中ID=0,而count>0开始更新前1个MYTABLE集合ID=@count,其中ID=0集合计数=从MYTABLE中选择count*,其中ID=0结束