Эквивалент Oracle RowID в SQL Server


84

Что эквивалентно Oracle RowID в SQL Server?


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

Ответы:


117

Из документов Oracle

Псевдоколонка ROWID

Для каждой строки в базе данных псевдостолбец ROWID возвращает адрес строки. Значения 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 г.
Хенрик Холмгаард Хёйер

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, поэтому вам придется создать собственный столбец идентификатора и явно получить его в своем запросе.

РЕДАКТИРОВАТЬ: для динамической нумерации строк набора результатов см. Ниже, но это, вероятно, было бы эквивалентом Oracle ROWNUM, и я предполагаю, что из всех комментариев на странице вы хотите, чтобы материал был выше. Для 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 также есть статья о динамической нумерации строк.


Я думаю, что столбец идентификаторов однозначно определяет строку в таблице, но не в базе данных.
tuinstoel

Это правда, но это соответствует определению ROWID, которое я вижу в документации Oracle: «Внешний тип данных ROWID идентифицирует конкретную строку в таблице базы данных» ... но я вижу, что вы говорите это из-за моей опечатки в Топ. :) Спасибо что подметил это.
Сяофу

Строка «номер» не является ROWID. ROWID содержит физическое расположение строки, отличное от уникального номера. Особенно он уникален для всех таблиц в базе данных (за некоторыми исключениями, когда используются специальные методы хранения)
a_horse_with_no_name

6

Некоторые из приведенных выше ответов будут работать с отсутствием прямой ссылки на конкретную строку, но не будут работать, если изменения произойдут в других строках в таблице. Это мои критерии, ответы на которые технически недостаточны.

Обычно ROWID используется для предоставления (в некоторой степени) стабильного метода выбора строк и последующего возврата к строке для ее обработки (например, для ее ОБНОВЛЕНИЯ). Метод поиска строки (сложные объединения, полнотекстовый поиск или просмотр строка за строкой и применение процедурных тестов к данным) может быть нелегко или безопасно повторно использовать для квалификации оператора UPDATE.

Кажется, что RID SQL Server предоставляет те же функции, но не обеспечивает такой же производительности. Это единственная проблема, которую я вижу, и, к сожалению, цель сохранения ROWID - избежать повторения дорогостоящей операции по поиску строки, скажем, в очень большой таблице. Тем не менее, производительность во многих случаях приемлема. Если Microsoft настроит оптимизатор в будущем выпуске, проблема производительности может быть решена.

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

Предостережение: даже ROWID Oracle не был бы стабильным, если бы администратор базы данных, например, между 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 имеет rownum для доступа к строкам таблицы, используя номер строки или идентификатор строки. Есть ли для этого эквивалент в SQL Server? Или как сгенерировать вывод с номером строки в SQL Server?

В SQL Server нет прямого эквивалента rownum или идентификатора строки Oracle. Строго говоря, в реляционной базе данных строки в таблице не упорядочены, и идентификатор строки не имеет смысла. Но если вам нужна эта функция, рассмотрите следующие три альтернативы:

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


3

Если вы хотите постоянно нумеровать строки в таблице, пожалуйста, не используйте решение RID для SQL Server. Он будет работать хуже, чем Access на старом 386. Для SQL Server просто создайте столбец IDENTITY и используйте этот столбец в качестве кластеризованного первичного ключа. Это поместит в таблицу постоянное быстрое целочисленное B-дерево, и, что более важно, каждый некластеризованный индекс будет использовать его для поиска строк. Если вы попытаетесь разработать 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 И [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 (Разделение по порядку Deptno по sal desc)

В приведенном выше примере будет указан порядковый номер, основанный на наивысшей заработной плате каждого отдела. Разделение по необязательно, и вы можете удалить его в соответствии с вашими требованиями.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.