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