在SQL Server中等效于Oracle的RowID


Answers:


115

从Oracle文档

ROWID伪列

对于数据库中的每一行,ROWID伪列返回该行的地址。Oracle Database rowid值包含查找行所必需的信息:

  • 对象的数据对象编号
  • 该行所在的数据文件中的数据块
  • 该行在数据块中的位置(第一行为0)
  • 该行所在的数据文件(第一个文件为1)。文件号是相对于表空间的。

在SQL Server中与此最接近的等效项是rid具有三个组成部分的File:Page:Slot

在SQL Server 2008中,可以使用未记录且不受支持的%%physloc%%虚拟列进行查看。这将返回一个binary(8)值,其中页面ID的前四个字节,然后是文件ID的2个字节,然后是页面上插槽位置的2个字节。

标量函数sys.fn_PhysLocFormattersys.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

示例输出

+--------------------+----------------+
|    %%physloc%%     | File:Page:Slot |
+--------------------+----------------+
| 0x2926020001000000 | (1:140841:0)   |
| 0x2926020001000100 | (1:140841:1)   |
+--------------------+----------------+

请注意,查询处理器未利用此功能。虽然可以WHERE子句中使用它

SELECT *
FROM T
WHERE %%physloc%% = 0x2926020001000100 

SQL Server将不会直接搜索到指定的行。相反,它将进行全表扫描,评估%%physloc%%每一行并返回匹配的行(如果有的话)。

为了反转由前面提到的两个功能执行的过程,并获得binary(8)与已知File,Page,Slot值相对应的值,可以使用以下内容。

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))

在SQL Server 2005上,可以改用未记录且不受支持的虚拟列%% LockRes %%
Henrik HolmgaardHøyer19年

绝对正确。%% LockRes %%不是“正确的方法”-仅当对2008
Henrik HolmgaardHøyer19年

11

我必须对具有很多列的非常大的表进行重复数据删除,并且速度很重要。因此,我使用适用于任何表的此方法:

delete T from 
(select Row_Number() Over(Partition By BINARY_CHECKSUM(*) order by %%physloc%% ) As RowNumber, * From MyTable) T
Where T.RowNumber > 1


9

如果要唯一标识表中的行而不是结果集,则需要使用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

会给你:

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

support.microsoft.com上还有关于动态编号行的文章。


我认为标识列可以唯一地标识表中的行,而不是数据库中的行。
tuinstoel

的确是这样,但这符合我在Oracle文档中看到的ROWID的定义:“外部数据类型ROWID标识数据库表中的特定行” ...但是我看到您这么说是因为我在最佳。:) 感谢您指出了这一点。
小夫

行“数字”不是ROWID。ROWID包含行的物理位置,它与唯一编号有所不同。特别是它在数据库中的所有表中都是唯一的(使用特殊存储技术时会有所例外)
a_horse_with_no_name 2011年

6

上面的几个答案将解决缺少对特定行的直接引用的问题,但是如果表中的其他行发生更改,则将无法工作。这是我的答案在技术上不足的标准。

Oracle ROWID的常见用法是提供一种(某种)稳定的方法来选择行,然后再返回到该行以对其进行处理(例如,对其进行更新)。查找行的方法(复杂的联接,全文搜索或逐行浏览并针对数据应用过程测试)可能不容易或安全地重新用于限定UPDATE语句。

SQL Server RID似乎提供了相同的功能,但没有提供相同的性能。这是我所看到的唯一问题,不幸的是,保留ROWID的目的是避免重复执行昂贵的操作以在一个很大的表中找到该行。尽管如此,许多情况下的性能还是可以接受的。如果Microsoft在将来的版本中调整优化器,则可以解决性能问题。

也可以简单地使用FOR UPDATE并在程序程序中保持CURSOR打开。但是,这可能在大型或复杂的批处理中很昂贵。

注意:例如,如果SELECT和UPDATE之间的DBA重建数据库,则Oracle的ROWID也将不稳定,因为它是物理行标识符。因此,ROWID设备应仅在范围明确的任务中使用。


3

如果您只想为一个小的数据集进行基本的行编号,那么这种方法怎么样?

SELECT row_number() OVER (order by getdate()) as ROWID, * FROM Employees

8
不是什么rowid。
Stephanie Page 2010年

但是它适用于某些观众会在不知道ROWID是什么的情况下快速添加的ID。
Graeme

3

来自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表中的每一行生成一个行号。为了使该查询生效,表必须具有唯一键。

    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
    
  • 使用临时表方法,将整个结果集以及该IDENTITY() 函数生成的行ID一起存储到临时表中。创建临时表的成本很高,尤其是在使用大型表时。如果表中没有唯一键,请采用这种方法。


3

如果要对表中的行进行永久编号,请不要对SQL Server使用RID解决方案。在旧的386上,它的性能将比Access差。对于SQL Server,只需创建一个IDENTITY列,然后将该列用作群集的主键。这将在表上放置一个永久的快速Integer B树,更重要的是,每个非聚集索引都将使用它来定位行。如果您尝试像使用Oracle一样在SQL Server中进行开发,则会创建性能不佳的数据库。您需要针对引擎进行优化,而不是假装它是不同的引擎。

同样,请不要使用NewID()来用GUID填充主键,这样会降低插入性能。如果必须使用GUID,请使用NewSequentialID()作为列的默认值。但是INT仍然会更快。

另一方面,如果您只想对查询结果行进行编号,请使用RowNumber Over()函数作为查询列之一。




1

请参阅http://msdn.microsoft.com/zh-cn/library/aa260631(v=SQL.80).aspx 在SQL Server中,时间戳记与DateTime列不同。它用于唯一地标识数据库中的一行,不仅是表,而且还标识整个数据库。这可以用于乐观并发。例如UPDATE [Job] SET [Name] = @ Name,[XCustomData] = @ XCustomData WHERE([ModifiedTimeStamp] = @ Original_ModifiedTimeStamp AND [GUID] = @ Original_GUID

ModifiedTimeStamp可确保您正在更新原始数据,并且如果对该行进行了另一次更新将失败。


0

我从MS SQL示例中获取了此示例,您可以看到@ID可以与integer或varchar或任何其他名称互换。这是我一直在寻找的相同解决方案,因此我将与大家分享。请享用!!

-- UPDATE statement with CTE references that are correctly matched.
DECLARE @x TABLE (ID int, Stad int, Value int, ison bit);
INSERT @x VALUES (1, 0, 10, 0), (2, 1, 20, 0), (6, 0, 40, 0), (4, 1, 50, 0), (5, 3, 60, 0), (9, 6, 20, 0), (7, 5, 10, 0), (8, 8, 220, 0);
DECLARE @Error int;
DECLARE @id int;

WITH cte AS (SELECT top 1 * FROM @x WHERE Stad=6)
UPDATE x -- cte is referenced by the alias.
SET ison=1, @id=x.ID
FROM cte AS x

SELECT *, @id as 'random' from @x
GO

0

您可以使用以下给出的方法获取ROWID:

1.创建一个新表并在其中添加自动递增字段

2,使用Row_Number分析函数根据需要获取序列

Sample:Row_Number()Over(按Deptno顺序按sal desc进行分区)

上面的示例将根据每个部门的最高薪水为您提供序列号.partition by是可选的,您可以根据需要将其删除

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.