Mar*_*ith 115
ROWID伪列
对于数据库中的每一行,ROWID伪列返回行的地址.Oracle数据库rowid值包含查找行所需的信息:
- 对象的数据对象编号
- 行所在的数据文件中的数据块
- 数据块中行的位置(第一行为0)
- 行所在的数据文件(第一个文件为1).文件号是相对于表空间的.
SQL Server中与此最接近的是rid具有三个组件File:Page:Slot.
在SQL Server 2008中,可以使用未记录和不受支持的%%physloc%%虚拟列来查看此内容.这将返回一个binary(8)值,其页面ID在前四个字节中,然后是2个字节用于文件ID,后面是2个字节用于页面上的插槽位置.
标量函数sys.fn_PhysLocFormatter或sys.fn_PhysLocCrackerTVF可用于将其转换为更易读的形式
CREATE TABLE T(X INT);
INSERT INTO T VALUES(1),(2)
SELECT %%physloc%% AS [%%physloc%%],
sys.fn_PhysLocFormatter(%%physloc%%) AS [File:Page:Slot]
FROM T
Run Code Online (Sandbox Code Playgroud)
示例输出
+--------------------+----------------+
| %%physloc%% | File:Page:Slot |
+--------------------+----------------+
| 0x2926020001000000 | (1:140841:0) |
| 0x2926020001000100 | (1:140841:1) |
+--------------------+----------------+
Run Code Online (Sandbox Code Playgroud)
请注意,查询处理器不会利用此功能.虽然可以在一个WHERE条款中使用它
SELECT *
FROM T
WHERE %%physloc%% = 0x2926020001000100
Run Code Online (Sandbox Code Playgroud)
SQL Server 不会直接搜索指定的行.相反,它将执行全表扫描,评估%%physloc%%每一行并返回匹配的行(如果有的话).
要反转前面提到的2个函数执行的过程并获得binary(8)与已知文件,页面,插槽值对应的值,可以使用下面的值.
DECLARE @FileId int = 1,
@PageId int = 338,
@Slot int = 3
SELECT CAST(REVERSE(CAST(@PageId AS BINARY(4))) AS BINARY(4)) +
CAST(REVERSE(CAST(@FileId AS BINARY(2))) AS BINARY(2)) +
CAST(REVERSE(CAST(@Slot AS BINARY(2))) AS BINARY(2))
Run Code Online (Sandbox Code Playgroud)
如果要唯一地标识表中的行而不是结果集,那么您需要查看使用IDENTITY列之类的内容.请参阅SQL Server帮助中的"IDENTITY属性".SQL Server不像Oracle那样为表中的每一行自动生成ID,因此您必须不必创建自己的ID列并在查询中显式获取它.
编辑:对于结果集行的动态编号,请参见下文,但这可能与Oracle的ROWNUM相当,我假设您希望上面的内容在页面上的所有注释中.对于SQL Server 2005及更高版本,您可以使用新的" 排名函数"函数来实现行的动态编号.
例如,我在我的查询中执行此操作:
select row_number() over (order by rn_execution_date asc) as 'Row Number', rn_execution_date as 'Execution Date', count(*) as 'Count'
from td.run
where rn_execution_date >= '2009-05-19'
group by rn_execution_date
order by rn_execution_date asc
Run Code Online (Sandbox Code Playgroud)
会给你:
Row Number Execution Date Count
---------- ----------------- -----
1 2009-05-19 00:00:00.000 280
2 2009-05-20 00:00:00.000 269
3 2009-05-21 00:00:00.000 279
Run Code Online (Sandbox Code Playgroud)
关于动态编号行的support.microsoft.com上还有一篇文章.
小智 9
我必须重复一个包含许多列的非常大的表,速度很重要.因此,我使用适用于任何表的此方法:
delete T from
(select Row_Number() Over(Partition By BINARY_CHECKSUM(*) order by %%physloc%% ) As RowNumber, * From MyTable) T
Where T.RowNumber > 1
Run Code Online (Sandbox Code Playgroud)
查看新的ROW_NUMBER函数.它的工作原理如下:
SELECT ROW_NUMBER() OVER (ORDER BY EMPID ASC) AS ROWID, * FROM EMPLOYEE
Run Code Online (Sandbox Code Playgroud)
小智 6
有几个问题的答案上面会解决缺乏直接引用特定行,但将无法正常工作,如果变化发生在一个表中的其他行.这是我的答案在技术上很短的标准.
Oracle ROWID的一个常见用途是提供一种(某种程度上)稳定的方法来选择行,然后返回到行来处理它(例如,更新它).查找行的方法(复杂连接,全文搜索或逐行浏览以及对数据应用程序测试)可能无法轻松或安全地重新使用以限定UPDATE语句.
SQL Server RID似乎提供相同的功能,但不提供相同的性能.这是我看到的唯一问题,不幸的是,保留ROWID的目的是避免重复昂贵的操作来查找例如非常大的表中的行.尽管如此,许多情况下的表现都是可以接受的.如果Microsoft在将来的版本中调整优化程序,则可以解决性能问题.
也可以简单地使用FOR UPDATE并在程序程序中保持CURSOR打开.然而,这在大型或复杂的批处理中可能证明是昂贵的.
警告:例如,如果SELECT和UPDATE之间的DBA重建数据库,那么即使Oracle的ROWID也不会稳定,因为它是物理行标识符.所以ROWID设备应该只在一个范围很广的任务中使用.
小智 5
如果您想对表中的行进行永久编号,请不要使用 SQL Server 的 RID 解决方案。它的性能比旧 386 上的 Access 差。对于 SQL Server,只需创建一个 IDENTITY 列,并使用该列作为聚集主键。这将在表上放置一个永久、快速的整数 B 树,更重要的是每个非聚集索引将使用它来定位行。如果您尝试像 Oracle 一样在 SQL Server 中进行开发,您将创建一个性能很差的数据库。您需要针对引擎进行优化,而不是假装它是不同的引擎。
另外,请不要使用 NewID() 用 GUID 填充主键,否则会降低插入性能。如果必须使用 GUID,请使用 NewSequentialID() 作为列默认值。但INT仍然会更快。
另一方面,如果您只想对查询结果的行进行编号,请使用 RowNumber Over() 函数作为查询列之一。
来自http://vyaskn.tripod.com/programming_faq.htm#q17:
Oracle 有一个 rownum 来使用行号或行 id 访问表的行。SQL Server 中有类似的东西吗?或者如何在 SQL Server 中生成带有行号的输出?
没有与 SQL Server 中 Oracle 的 rownum 或 row id 直接等效的东西。严格来说,在关系数据库中,表中的行没有排序,行 ID 没有任何意义。但如果您需要该功能,请考虑以下三种替代方案:
IDENTITY在表中添加一列。使用以下查询为每行生成行号。以下查询为 pubs 数据库的authors 表中的每一行生成行号。为了使该查询正常工作,表必须具有唯一键。
Run Code Online (Sandbox Code Playgroud)SELECT (SELECT COUNT(i.au_id) FROM pubs..authors i WHERE i.au_id >= o.au_id ) AS RowID, au_fname + ' ' + au_lname AS 'Author name' FROM pubs..authors o ORDER BY RowID使用临时表方法,将整个结果集以及函数生成的行 ID 存储到临时表中
IDENTITY()。创建临时表的成本很高,尤其是在使用大型表时。如果您的表中没有唯一的键,请采用这种方法。