SQL Server 中 Oracle 的 RowID 等价物
Posted
技术标签:
【中文标题】SQL Server 中 Oracle 的 RowID 等价物【英文标题】:Equivalent of Oracle's RowID in SQL Server 【发布时间】:2010-10-28 21:24:58 【问题描述】:什么是 SQL Server 中 Oracle 的 RowID 的等价物?
【问题讨论】:
Stephanie:假设是数据中有一个唯一的键,假设数据是标准化的,这有时是不正确的假设。因此,SQL server 中的 Oracle 的 RowID 等价于什么。 【参考方案1】:请尝试
select NEWID()
来源:https://docs.microsoft.com/en-us/sql/t-sql/data-types/uniqueidentifier-transact-sql
【讨论】:
ROWID
是服务器中现有记录的唯一标识符; NEWID()
是“为我创建一个随机 guid”——两者都很有用,但完全不同的概念,甚至没有一点重叠【参考方案2】:
您可以使用下面给出的方法获取ROWID:
1.创建一个包含自增字段的新表
2.使用 Row_Number 分析函数根据您的要求获取序列。我更喜欢这个,因为它有助于您希望 row_id 以特定字段或字段组合的升序或降序方式
Sample:Row_Number() Over(Partition by Deptno order by sal desc)
上面的样本会根据每个部门的最高薪水给你序列号。分区是可选的,你可以根据你的要求删除它
【讨论】:
ROWID
是服务器中现有记录的唯一标识符; ROW_NUMBER()
给出查询中记录的位置(按某些指定的顺序) - 两者都很有用,但完全不同的概念,甚至没有一点重叠【参考方案3】:
我必须对包含许多列的非常大的表进行重复数据删除,并且速度很重要。因此,我使用适用于任何表的这种方法:
delete T from
(select Row_Number() Over(Partition By BINARY_CHECKSUM(*) order by %%physloc%% ) As RowNumber, * From MyTable) T
Where T.RowNumber > 1
【讨论】:
【参考方案4】:如果您想对表中的行进行永久编号,请不要使用 SQL Server 的 RID 解决方案。它在旧的 386 上的性能将比 Access 更差。对于 SQL Server,只需创建一个 IDENTITY 列,并将该列用作聚集的主键。这将在表上放置一个永久的、快速的 Integer B-Tree,更重要的是,每个非聚集索引都将使用它来定位行。如果您尝试在 SQL Server 中进行开发,就好像它是 Oracle 一样,您将创建一个性能不佳的数据库。您需要针对引擎进行优化,而不是假装它是不同的引擎。
另外,请不要使用 NewID() 使用 GUID 填充主键,否则会影响插入性能。如果必须使用 GUID,请使用 NewSequentialID() 作为列默认值。但是INT仍然会更快。
另一方面,如果您只想对查询产生的行进行编号,请使用 RowNumber Over() 函数作为查询列之一。
【讨论】:
【参考方案5】:From the Oracle docs
ROWID 伪列
对于数据库中的每一行,ROWID 伪列返回 行的地址。 Oracle 数据库 rowid 值包含信息 需要定位一行:
对象的数据对象编号 行所在的数据文件中的数据块 行在数据块中的位置(第一行为0) 行所在的数据文件(第一个文件是 1)。文件 number 是相对于表空间的。
在 SQL Server 中与此最接近的是 rid
,它具有三个组件 File:Page:Slot
。
在 SQL Server 2008 中,可以使用未记录且不受支持的 %%physloc%%
虚拟列来查看此内容。这将返回一个 binary(8)
值,前四个字节中包含页面 ID,然后是文件 ID 的 2 个字节,然后是页面上的插槽位置的 2 个字节。
标量函数sys.fn_PhysLocFormatter
或sys.fn_PhysLocCracker
TVF 可用于将其转换为更易读的形式
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%%
并返回匹配的行(如果有的话)。
要反转前面提到的 2 个函数执行的过程,并获取与已知 File、Page、Slot 值对应的 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))
【讨论】:
在 SQL Server 2005 上,您可以改用未记录和不受支持的虚拟列 %%LockRes%% 绝对正确。 %%LockRes%% 不是“正确方法”——仅用于 2008 年之前旧版本 sql server 上数据的 qucik 和脏修复【参考方案6】:请看http://msdn.microsoft.com/en-us/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 确保您正在更新原始数据,如果该行发生另一次更新,则会失败。
【讨论】:
【参考方案7】:来自http://vyaskn.tripod.com/programming_faq.htm#q17:
Oracle 有一个 rownum 来使用行号或行 ID 访问表的行。 SQL Server 中是否有任何等价物?或者如何生成 在 SQL Server 中输出行号?
SQL 中没有直接等价于 Oracle 的 rownum 或 row id 服务器。严格来说,在关系数据库中, 表没有排序,行 ID 没有意义。但是如果你 需要该功能,请考虑以下三种选择:
将
IDENTITY
列添加到您的表中。使用以下查询为每一行生成一个行号。以下查询为作者中的每一行生成一个行号 pubs 数据库表。要使此查询起作用,该表必须具有 唯一键。
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 一起存储到临时表中 功能。创建临时表的成本很高,尤其是在 您正在使用大桌子。如果您不这样做,请采用这种方法 在你的表中有一个唯一的键。
【讨论】:
【参考方案8】:ROWID 是 Oracle 表上的隐藏列,因此,对于 SQL Server,构建您自己的列。添加一个名为 ROWID 的列,其默认值为 NEWID()
。
怎么做:Add column, with default value, to existing table in SQL Server
【讨论】:
这不是评论吗?【参考方案9】:上面的几个答案将解决缺少对特定行的直接引用,但如果表中的其他行发生更改将不起作用 .这是我的标准,我的答案在技术上是不足的。
Oracle 的 ROWID 的一个常见用途是提供一种(稍微)稳定的方法来选择行,然后返回该行进行处理(例如,更新它)。查找行的方法(复杂的连接、全文搜索或逐行浏览并对数据应用程序测试)可能无法轻松或安全地重新用于限定 UPDATE 语句。
SQL Server RID 似乎提供了相同的功能,但没有提供相同的性能。这是我看到的唯一问题,不幸的是,保留 ROWID 的目的是避免重复昂贵的操作来查找例如非常大的表中的行。尽管如此,许多情况下的性能是可以接受的。如果微软在未来的版本中调整优化器,性能问题可能会得到解决。
也可以简单地使用 FOR UPDATE 并在程序程序中保持 CURSOR 打开。但是,这在大型或复杂的批处理中可能会很昂贵。
警告:如果 DBA 在 SELECT 和 UPDATE 之间重建数据库,即使是 Oracle 的 ROWID 也不稳定,因为它是物理行标识符。因此,ROWID 设备只应在范围良好的任务中使用。
【讨论】:
【参考方案10】:我从 MS SQL 示例中获取了这个示例,您可以看到 @ID 可以与整数或 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
【讨论】:
【参考方案11】:如果你只是想要一个小数据集的基本行编号,像这样呢?
SELECT row_number() OVER (order by getdate()) as ROWID, * FROM Employees
【讨论】:
但它适用于一些观众会寻找的快速添加的 ID,不知道 ROWID 是什么。【参考方案12】:如果您想唯一标识表中的一行而不是您的结果集,那么您需要考虑使用 IDENTITY 列之类的东西。请参阅 SQL Server 帮助中的“IDENTITY 属性”。 SQL Server 不会像 Oracle 那样为表中的每一行自动生成一个 ID,因此您必须自己创建 ID 列并在查询中显式获取它。
编辑: 对于结果集行的动态编号,请参见下文,但这可能与 Oracle 的 ROWNUM 等效,我从页面上的所有 cmets 中假设您想要上面的内容。 对于 SQL Server 2005 及更高版本,您可以使用新的Ranking Functions 函数来实现行的动态编号。
例如,我对我的查询执行此操作:
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 上还有一篇关于动态编号行的文章。
【讨论】:
我认为标识列唯一标识了表中的一行,而不是数据库中的行。 这是真的,但这符合我在 Oracle 文档中看到的 ROWID 的定义:“外部数据类型 ROWID 标识数据库表中的特定行”......但我看到你是之所以这么说,是因为我在顶部的错字。 :) 感谢您指出这一点。 行“编号”不是 ROWID。 ROWID 包含与唯一编号不同的行的物理位置。特别是它在数据库中的所有表中都是唯一的(使用特殊存储技术时会有一些例外)【参考方案13】:查看新的ROW_NUMBER 函数。它的工作原理是这样的:
SELECT ROW_NUMBER() OVER (ORDER BY EMPID ASC) AS ROWID, * FROM EMPLOYEE
【讨论】:
我认为这是替代 rownum 而不是 rowid。以上是关于SQL Server 中 Oracle 的 RowID 等价物的主要内容,如果未能解决你的问题,请参考以下文章