Еквівалент RowID Oracle у SQL Server


84

Що еквівалентно RowID Oracle у SQL Server?


Стефані: припущення полягає в тому, що в даних є унікальний ключ, який передбачає, що дані нормалізовані, що іноді є неправильним припущенням. Таким чином, що еквівалентно RowID Oracle на SQL сервері.
Крістофер Махан,

Відповіді:


117

З документів Oracle

ПУЗОВА псевдоколонка

Для кожного рядка бази даних псевдостовбець ROWID повертає адресу рядка. Значення рядків Oracle Database містять інформацію, необхідну для пошуку рядка:

  • Номер об'єкта даних об'єкта
  • Блок даних у файлі даних, у якому знаходиться рядок
  • Положення рядка в блоці даних (перший рядок - 0)
  • Файл даних, у якому знаходиться рядок (перший файл - 1). Номер файлу відносно табличної області.

Найближчим еквівалентом цього в SQL Server є той, ridякий має три компоненти File:Page:Slot.

У SQL Server 2008 можна використовувати недокументований та непідтримуваний %%physloc%%віртуальний стовпець, щоб побачити це. Це повертає binary(8)значення з ідентифікатором сторінки в перших чотирьох байтах, потім 2 байти для ідентифікатора файлу, а потім 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

Приклад результату

+--------------------+----------------+
|    %%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 %%
Хенрік Холмгаард Хойер,

абсолютно правильний. %% LockRes %% - це не "правильний шлях" - використовуйте лише якщо для qucik і брудних виправлень даних на старих версіях SQL-серверів до 2008 р.
Henrik Holmgaard Høyer

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. Див. "Властивість IDENTITY" у довідці SQL Server. SQL Server не генерує автоматично ідентифікатор для кожного рядка в таблиці, як це робить Oracle, тому вам доведеться зіткнутися з проблемою створення власного стовпця ідентифікатора та явно отримати його у своєму запиті.

EDIT: для динамічної нумерації рядків набору результатів дивіться нижче, але це, мабуть, було б еквівалентом для ROWNUM Oracle, і я припускаю з усіх коментарів на сторінці, що ви хочете, щоб матеріали вище. Для 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

Це правда, але це відповідає визначенню ROWID, яке я бачу в документації Oracle: "Зовнішній тип даних ROWID ідентифікує певний рядок у таблиці бази даних" ... але я бачу, що ви говорите це через мою помилку в зверху. :) Дякую, що вказали на це.
Сяофу

Рядок "число" не є ROWID. ROWID містить фізичне розташування рядка, це щось інше, ніж унікальний номер. Особливо це унікально для всіх таблиць бази даних (за деякими винятками, коли використовуються спеціальні техніки зберігання)
a_horse_with_no_name

6

Деякі з наведених вище відповідей допоможуть усунути відсутність прямого посилання на певний рядок, але не спрацюють, якщо в інших рядках таблиці відбудуться зміни. Це мої критерії, щодо яких відповіді технічно короткі.

Типовим використанням ROWID Oracle є надання (дещо) стабільного методу виділення рядків і пізніше повернення до рядка для його обробки (наприклад, для оновлення). Метод пошуку рядка (складні об'єднання, повнотекстовий пошук або перегляд рядків за рядком та застосування процедурних перевірок щодо даних) може бути не легко або безпечно повторно використаний для кваліфікації оператора UPDATE.

Здається, RID SQL Server надає однакову функціональність, але не забезпечує однакову продуктивність. Це єдине питання, яке я бачу, і, на жаль, метою збереження ROWID є уникнення повторення дорогої операції пошуку рядка, скажімо, у дуже великій таблиці. Тим не менше, продуктивність у багатьох випадках є прийнятною. Якщо Microsoft налаштує оптимізатор у майбутньому випуску, може бути вирішено проблему продуктивності.

Також можна просто використовувати FOR UPDATE і тримати КУРСОР відкритим у процедурній програмі. Однак це може виявитися дорогим при великій або складній партійній обробці.

Застереження: Навіть ROWID Oracle не був би стабільним, якби DBA, наприклад, між SELECT та UPDATE, перебудовував базу даних, оскільки це фізичний ідентифікатор рядка. Отже, пристрій ROWID слід використовувати лише в рамках широкомасштабного завдання.


3

якщо ви просто хочете базову нумерацію рядків для невеликого набору даних, як щодо такого?

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

Але це працює для швидко доданого ідентифікатора, який деякі глядачі шукатимуть, не знаючи, що таке ROWID.
Graeme

3

З http://vyaskn.tripod.com/programming_faq.htm#q17 :

Oracle має доступ до рядків таблиці, використовуючи номер рядка або ідентифікатор рядка. Чи існує якийсь еквівалент для цього в SQL Server? Або як генерувати вихідні дані з номером рядка в SQL Server?

У SQL Server немає прямого еквівалента ідентифікатора рядка або рядка Oracle. Строго кажучи, у реляційній базі даних рядки в таблиці не впорядковані, а ідентифікатор рядка не має сенсу. Але якщо вам потрібна ця функціональність, розгляньте наступні три варіанти:

  • Додайте IDENTITYстовпець до таблиці.

  • Використовуйте наступний запит, щоб створити номер рядка для кожного рядка. Наступний запит генерує номер рядка для кожного рядка в таблиці авторів бази даних пабів. Щоб цей запит працював, таблиця повинна мати унікальний ключ.

    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() функцією. Створення тимчасової таблиці буде дорогим, особливо коли ви працюєте з великими таблицями. Подивіться на цей підхід, якщо у вас немає унікального ключа у вашій таблиці.


3

Якщо ви хочете постійно нумерувати рядки в таблиці, не використовуйте рішення RID для SQL Server. Він буде працювати гірше, ніж Access на старому 386. Для SQL Server просто створіть стовпець IDENTITY і використовуйте цей стовпець як кластерний первинний ключ. Це помістить постійне, швидке Integer B-Tree на стіл, і що більш важливо, кожен некластеризований індекс використовуватиме його для пошуку рядків. Якщо ви спробуєте розвиватися в SQL Server так, ніби це Oracle, ви створите неефективну базу даних. Вам потрібно оптимізувати для двигуна, а не робити вигляд, що це інший двигун.

також, будь ласка, не використовуйте NewID () для заповнення первинного ключа GUID-і, ви вб'єте продуктивність вставки. Якщо вам потрібно використовувати GUID, використовуйте NewSequentialID () як стовпець за замовчуванням. Але INT все одно буде швидшим.

Якщо, з іншого боку, ви просто хочете пронумерувати рядки, які є результатом запиту, використовуйте функцію RowNumber Over () як один із стовпців запиту.



1

ROWID - це прихований стовпець у таблицях Oracle, тому для SQL Server створіть свій власний. Додайте стовпець ROWID зі значенням за замовчуванням NEWID().

Як це зробити: Додайте стовпець із значенням за замовчуванням до існуючої таблиці в SQL Server


1
Хіба це не більше коментаря?
The Unfun Cat

1

Будь ласка , дивіться http://msdn.microsoft.com/en-us/library/aa260631(v=SQL.80).aspx У сервері SQL мітка часу не те ж саме як стовпець DateTime. Це використовується для однозначної ідентифікації рядка в базі даних не тільки таблиці, але і всієї бази даних. Це можна використовувати для оптимістичного паралелізму. наприклад UPDATE [Job] SET [Name] = @ Name, [XCustomData] = @ XCustomData WHERE ([ModifiedTimeStamp] = @ Original_ModifiedTimeStamp AND [GUID] = @ Original_GUID

ModifiedTimeStamp гарантує, що ви оновлюєте вихідні дані, і не вдасться виконати інше оновлення рядка.


0

Я взяв цей приклад із прикладу 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

0

Ви можете отримати ROWID, використовуючи методи, наведені нижче:

1. Створіть нову таблицю з полем автоматичного збільшення

2. Використовуйте аналітичну функцію Row_Number, щоб отримати послідовність на основі ваших вимог. Я би віддав перевагу цьому, оскільки це допомагає у ситуаціях, коли ви хочете, щоб row_id був за зростанням або спаданням конкретного поля або комбінації полів

Зразок: Row_Number () Over (Розділення за Дептно замовлення за Sal desc)

Наведений вище зразок дасть вам порядковий номер на основі найвищої заробітної плати кожного відділу. Розділ за не є обов’язковим, і ви можете видалити його відповідно до ваших вимог

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.