Sql server 2008 为什么这个子查询似乎不起作用?

Sql server 2008 为什么这个子查询似乎不起作用?,sql-server-2008,tsql,Sql Server 2008,Tsql,在任何事情之前,我不是在寻找一个重写。这是呈现给我的,我似乎不知道这是一个一般的bug,还是由于脚本的特殊性而出现的某种语法疯狂。好的,在设置中说: Microsoft SQL Server标准版64位 版本10.50.2500.0 在位于通用数据库中的表上,定义为: 插入一些值: 现在运行查询,从每个组中选择一个,记住没有重写建议!: SELECT RXXID FROM ( SELECT RXX.RegionID as RXXID, ROW_NUMBER()

在任何事情之前,我不是在寻找一个重写。这是呈现给我的,我似乎不知道这是一个一般的bug,还是由于脚本的特殊性而出现的某种语法疯狂。好的,在设置中说:

Microsoft SQL Server标准版64位

版本10.50.2500.0

在位于通用数据库中的表上,定义为:

插入一些值:

现在运行查询,从每个组中选择一个,记住没有重写建议!:

SELECT RXXID FROM (
   SELECT
       RXX.RegionID as RXXID,
       ROW_NUMBER() OVER (PARTITION BY RXX.RegionGroupID ORDER BY RXX.RegionGroupID) AS RXXNUM
   FROM Regions as RXX
) AS tmp
WHERE tmp.RXXNUM = 1
你应该得到:

现在将其粘贴在update语句中,预设为0,并在以下内容之后选择“全部”:

UPDATE Regions SET IsDefault = 0

UPDATE Regions
SET IsDefault = 1
WHERE RegionID IN (
    SELECT RXXID FROM (
       SELECT
           RXX.RegionID as RXXID,
           ROW_NUMBER() OVER (PARTITION BY RXX.RegionGroupID ORDER BY RXX.RegionGroupID) AS RXXNUM
       FROM Regions as RXX
    ) AS tmp
    WHERE tmp.RXXNUM = 1
)


SELECT * FROM Regions
ORDER BY RegionGroupID
得到这个结果:

zomg wtf lamaz

虽然我并不自称是SQL大师,但这似乎既不恰当也不正确。为了让事情变得更疯狂,如果你放下主键,它似乎会起作用:

删除主键:

并重新运行update语句集,结果:

那不是b吗


有人知道这里发生了什么吗?我猜是某种子查询缓存,这是一个bug吗?它确实不像SQL应该做的那样?

直接作为CTE更新即可:

WITH tmp AS (
SELECT 
       RegionID as RXXID,
       RegionGroupID,
       IsDefault,
       ROW_NUMBER() OVER (PARTITION BY RegionGroupID ORDER BY RegionID) AS RXXNUM
   FROM Regions

) 
UPDATE tmp SET IsDefault = 1 WHERE RXXNUM = 1
select * from Regions
添加了更多列来说明。你可以在电视上看到这个

虽然不能100%确定示例中发生了什么,但由于您是按同一列进行分区和排序的,因此无法真正确定是否会恢复相同的顺序,因为它们都是并列的。你不应该像我在sqlfiddle上做的那样,按RegionID或其他列排序吗

回到你的问题:

如果将使用聚集索引的更新更改为SELECT,则会返回所有9行。 如果您删除PK并执行SELECT,则只会得到3行。回到你的更新声明。检查执行计划表明,它们略有不同:

这里您可以看到,在第一个with PK查询中,您将扫描聚集索引以查找外部引用,请注意,它没有别名RXX。然后,对于顶部的每一行,查找RXX。是的,由于您的行号排序,每个RegionID可以是每个RegionGroupID的第1行。我猜SQL Server会根据您的PK知道这一点,并且可以说对于每个RegionID,这个RegionID可以是第1行。因此,这一说法是相当正确的

在第二个查询中,没有索引,您在区域上得到一个表扫描,然后它使用RXX构建一个探测表,并以不同的方式连接单次传递,现在每个regiongroupid的行数只能为1。这样,在扫描中,每个RegionID只有一个行号,尽管您不能100%确定每次都是相同的

这意味着: 如果使用的子查询对每次执行都没有确定的顺序,则应避免使用多个过程嵌套的循环联接类型,而应使用单过程合并或散列联接

要在不更改查询结构的情况下修复此问题,请在第一次更新中添加选项哈希连接或选项合并连接:

因此,当您有PK时,需要以下update语句:

UPDATE Regions SET IsDefault = 0

UPDATE Regions 
SET IsDefault = 1 
WHERE RegionID IN (
    SELECT RXXID FROM (
       SELECT 
           RXX.RegionID as RXXID,
           ROW_NUMBER() OVER (PARTITION BY RXX.RegionGroupID ORDER BY RXX.RegionGroupID) AS RXXNUM
       FROM Regions as RXX
    ) AS tmp
    WHERE tmp.RXXNUM = 1 
)
OPTION (HASH JOIN)

SELECT * FROM Regions
ORDER BY RegionGroupID
以下是使用这两种连接类型的执行计划注意属性中的实际行数:3:


您用普通语言进行的查询类似于: 对于区域中的每一行,检查某些子查询中是否存在RegionID。这意味着对区域中的每一行执行子查询。我知道情况并非如此,但这是查询的语义

因为您使用RegionGroupID作为顺序和分区,所以实际上不知道将返回什么RegionID,所以每次对子查询进行检查时,它很可能是一个新的ID

更新:

对派生表使用连接进行更新,而不是在中使用连接,会更改查询的语义,同时也会更改结果

这与预期的效果一样:

UPDATE R 
SET IsDefault = 1
FROM Regions as R
  inner join 
      (
        SELECT RXXID FROM (
           SELECT 
               RXX.RegionID as RXXID,
               ROW_NUMBER() OVER (PARTITION BY RXX.RegionGroupID ORDER BY RXX.RegionGroupID) AS RXXNUM
           FROM Regions as RXX
        ) AS tmp
        WHERE tmp.RXXNUM = 1 
      ) as C
    on R.RegionID = C.RXXID

我意识到这并不能回答你的问题,我现在在一个环境中研究这一点,但它为你提供了一个解决方法,并避免了不必要的子选择。如果你将ckozl的查询更改为orderby到RegionID,你会得到预期的b结果,在10.0版中,我得到的结果和他提出的问题的结果一样,你得到的是一张赞成票。按RXX.RegionGroupID划分的分区上的行数按RXX.RegionID排序,因为RXXNUM完美地修复了它。但50%的问题是为什么这种行为是这样的。。。因为如果有人没有得到它,你会因为你的努力而得到饼干,这似乎毫无意义。干得好!是的,答案更新了,希望用可读的术语解释为什么会发生这种情况:触地得分!非常好的描述我喜欢的是措辞不确定的子查询,做得很漂亮。我觉得我需要重申,我没有写这个查询,只是在被要求描述它的行为时才感到困惑。。感谢您的辛勤工作-这是使用嵌套循环joim工作的,因为C是第一个也是唯一一次构建的。顺便说一句,这是一个非常有趣的问题!
RegionID    RegionGroupID IsDefault
----------- ------------- ---------
0           1             1
1           1             1
2           1             1
3           2             1
4           2             1
5           2             1
6           3             1
7           3             1
8           3             1
IF  EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[Regions]') AND name = N'PK_Regions')
ALTER TABLE [dbo].[Regions] DROP CONSTRAINT [PK_Regions]
RegionID    RegionGroupID IsDefault
----------- ------------- ---------
0           1             1
1           1             0
2           1             0
3           2             1
4           2             0
5           2             0
6           3             1
7           3             0
8           3             0
WITH tmp AS (
SELECT 
       RegionID as RXXID,
       RegionGroupID,
       IsDefault,
       ROW_NUMBER() OVER (PARTITION BY RegionGroupID ORDER BY RegionID) AS RXXNUM
   FROM Regions

) 
UPDATE tmp SET IsDefault = 1 WHERE RXXNUM = 1
select * from Regions
UPDATE Regions SET IsDefault = 0

UPDATE Regions 
SET IsDefault = 1 
WHERE RegionID IN (
    SELECT RXXID FROM (
       SELECT 
           RXX.RegionID as RXXID,
           ROW_NUMBER() OVER (PARTITION BY RXX.RegionGroupID ORDER BY RXX.RegionGroupID) AS RXXNUM
       FROM Regions as RXX
    ) AS tmp
    WHERE tmp.RXXNUM = 1 
)
OPTION (HASH JOIN)

SELECT * FROM Regions
ORDER BY RegionGroupID
UPDATE R 
SET IsDefault = 1
FROM Regions as R
  inner join 
      (
        SELECT RXXID FROM (
           SELECT 
               RXX.RegionID as RXXID,
               ROW_NUMBER() OVER (PARTITION BY RXX.RegionGroupID ORDER BY RXX.RegionGroupID) AS RXXNUM
           FROM Regions as RXX
        ) AS tmp
        WHERE tmp.RXXNUM = 1 
      ) as C
    on R.RegionID = C.RXXID