Odpowiednik Oracle RowID w SQL Server

84

Jaki jest odpowiednik Oracle RowID w SQL Server?

John Saunders
źródło
Stephanie: założenie jest takie, że w danych znajduje się unikalny klucz, który zakłada, że ​​dane są znormalizowane, co czasami jest założeniem niepoprawnym. Zatem, jaki jest odpowiednik RowID Oracle w SQL Server.
Christopher Mahan,

Odpowiedzi:

117

Z dokumentów Oracle

Pseudokolumna ROWID

Dla każdego wiersza w bazie danych pseudokolumna ROWID zwraca adres wiersza. Wartości identyfikatorów wiersza w bazie danych Oracle zawierają informacje niezbędne do zlokalizowania wiersza:

  • Numer obiektu danych obiektu
  • Blok danych w pliku danych, w którym znajduje się wiersz
  • Pozycja wiersza w bloku danych (pierwszy wiersz to 0)
  • Plik danych, w którym znajduje się wiersz (pierwszy plik to 1). Numer pliku jest określany względem obszaru tabel.

Najbliższym odpowiednikiem tego w SQL Server jest ten, ridktóry ma trzy składniki File:Page:Slot.

W SQL Server 2008 można to zobaczyć przy użyciu nieudokumentowanej i nieobsługiwanej %%physloc%%kolumny wirtualnej. Zwraca binary(8)wartość z identyfikatorem strony w pierwszych czterech bajtach, następnie 2 bajty dla identyfikatora pliku, a następnie 2 bajty dla lokalizacji boksu na stronie.

Aby przekształcić to w bardziej czytelną formę, można użyć funkcji skalarnej sys.fn_PhysLocFormatterlub 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

Przykładowe dane wyjściowe

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

Należy zauważyć, że nie jest to wykorzystywane przez procesor zapytań. Chociaż możliwe jest użycie tego w WHEREklauzuli

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

SQL Server nie będzie bezpośrednio szukać określonego wiersza. Zamiast tego wykona pełne skanowanie tabeli, oceni %%physloc%%dla każdego wiersza i zwróci ten, który pasuje (jeśli w ogóle).

Aby odwrócić proces wykonywany przez 2 wcześniej wspomniane funkcje i uzyskać binary(8)wartość odpowiadającą znanym wartościom Plik, Strona, Slot, można użyć poniższych.

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))
Martin Smith
źródło
W SQL Server 2005 można zamiast tego użyć nieudokumentowanych i nieobsługiwanych kolumn wirtualnych %% LockRes %%
Henrik Holmgaard Høyer
absolutnie poprawne. %% LockRes %% nie jest „właściwą drogą” - używaj tylko w przypadku szybkich i brudnych poprawek danych na starych wersjach serwerów sql sprzed 2008 roku
Henrik Holmgaard Høyer
11

Muszę usunąć bardzo dużą tabelę z wieloma kolumnami, a szybkość jest ważna. Dlatego używam tej metody, która działa dla każdej tabeli:

delete T from 
(select Row_Number() Over(Partition By BINARY_CHECKSUM(*) order by %%physloc%% ) As RowNumber, * From MyTable) T
Where T.RowNumber > 1
S Wright
źródło
9

Wypróbuj nową funkcję ROW_NUMBER . Działa to tak:

SELECT ROW_NUMBER() OVER (ORDER BY EMPID ASC) AS ROWID, * FROM EMPLOYEE
Daren Thomas
źródło
15
Myślę, że to zamiennik rownum a nie rowid.
tuinstoel
9

Jeśli chcesz jednoznacznie zidentyfikować wiersz w tabeli, a nie zestaw wyników, musisz przyjrzeć się użyciu czegoś w rodzaju kolumny IDENTITY. Zobacz „Właściwość IDENTITY” w pomocy programu SQL Server. SQL Server nie generuje automatycznie identyfikatora dla każdego wiersza w tabeli, tak jak robi to Oracle, więc musisz zadać sobie trud tworzenia własnej kolumny ID i jawnie pobrać ją w zapytaniu.

EDYCJA: dynamiczne numerowanie wierszy zestawu wyników patrz poniżej, ale prawdopodobnie byłby to odpowiednik ROWNUM Oracle i na podstawie wszystkich komentarzy na stronie zakładam, że chcesz rzeczy powyżej. W przypadku SQL Server 2005 i nowszych można użyć nowej funkcji Ranking Functions w celu uzyskania dynamicznej numeracji wierszy.

Na przykład robię to na moje zapytanie:

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

Da tobie:

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

W witrynie support.microsoft.com znajduje się również artykuł dotyczący dynamicznego numerowania wierszy.

Xiaofu
źródło
Myślę, że kolumna tożsamości jednoznacznie identyfikuje wiersz w tabeli, ale nie w bazie danych.
tuinstoel
To prawda, ale pasuje to do definicji ROWID, którą widzę w dokumentach Oracle: „Zewnętrzny typ danych ROWID identyfikuje określony wiersz w tabeli bazy danych” ... ale widzę, że mówisz to z powodu mojej literówki w Top. :) Dziękuję za zwrócenie uwagi.
Xiaofu
„Numer” wiersza nie jest ROWID. ROWID zawiera fizyczne położenie wiersza i różni się od unikalnej liczby. W szczególności jest wyjątkowy we wszystkich tabelach w bazie danych (z pewnymi wyjątkami, gdy używane są specjalne techniki przechowywania)
a_horse_with_no_name
6

Kilka z odpowiedzi powyżej będzie obejść brak bezpośredniego odniesienia do określonego wiersza, ale nie będzie działać , jeśli zmiany zachodzą do innych wierszy w tabeli. To są moje kryteria, dla których odpowiedzi są technicznie krótkie.

Powszechnym zastosowaniem Oracle ROWID jest zapewnienie (w pewnym stopniu) stabilnej metody wybierania wierszy i późniejszego powrotu do wiersza w celu jego przetworzenia (np. W celu ZAKTUALIZOWANIA). Metoda znajdowania wiersza (łączenia złożone, wyszukiwanie pełnotekstowe lub przeglądanie wiersza po wierszu i stosowanie testów proceduralnych do danych) może nie być łatwo lub bezpiecznie ponownie wykorzystana do zakwalifikowania instrukcji UPDATE.

Wydaje się, że identyfikator RID programu SQL Server zapewnia tę samą funkcjonalność, ale nie zapewnia takiej samej wydajności. To jedyny problem, jaki widzę, i niestety celem zachowania ROWID jest uniknięcie powtarzania kosztownej operacji w celu znalezienia wiersza, powiedzmy, bardzo dużej tabeli. Niemniej jednak wydajność w wielu przypadkach jest akceptowalna. Jeśli firma Microsoft dostosuje optymalizator w przyszłej wersji, można rozwiązać problem z wydajnością.

Można również po prostu użyć FOR UPDATE i pozostawić CURSOR otwarty w programie proceduralnym. Może to jednak okazać się kosztowne przy przetwarzaniu dużych lub złożonych partii.

Ostrzeżenie: Nawet ROWID Oracle nie byłby stabilny, gdyby DBA, na przykład, między SELECT i UPDATE, odbudował bazę danych, ponieważ jest to fizyczny identyfikator wiersza. Dlatego urządzenie ROWID powinno być używane tylko w ramach dobrze określonego zadania.

Vincent
źródło
3

jeśli potrzebujesz tylko podstawowej numeracji wierszy dla małego zbioru danych, co powiesz na coś takiego?

SELECT row_number() OVER (order by getdate()) as ROWID, * FROM Employees
Adriano Carneiro
źródło
Ale działa w przypadku szybkiego dodawania identyfikatora, którego niektórzy widzowie będą szukać, nie wiedząc, co to jest ROWID.
Graeme,
3

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

Oracle ma rownum dostępu do wierszy tabeli przy użyciu numeru wiersza lub identyfikatora wiersza. Czy istnieje odpowiednik tego w SQL Server? Albo jak wygenerować dane wyjściowe z numerem wiersza w programie SQL Server?

Nie ma bezpośredniego odpowiednika numeru wiersza lub identyfikatora wiersza Oracle w SQL Server. Ściśle mówiąc, w relacyjnej bazie danych wiersze w tabeli nie są uporządkowane, a identyfikator wiersza nie ma sensu. Ale jeśli potrzebujesz tej funkcji, rozważ następujące trzy alternatywy:

  • Dodaj IDENTITYkolumnę do tabeli.

  • Użyj następującego zapytania, aby wygenerować numer wiersza dla każdego wiersza. Następujące zapytanie generuje numer wiersza dla każdego wiersza w tabeli autorów bazy danych pubs. Aby to zapytanie działało, tabela musi mieć unikalny klucz.

    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
    
  • Użyj metody tabeli tymczasowej, aby zapisać cały zestaw wyników w tabeli tymczasowej, wraz z identyfikatorem wiersza wygenerowanym przez IDENTITY() funkcję. Utworzenie tabeli tymczasowej będzie kosztowne, zwłaszcza podczas pracy z dużymi tabelami. Wybierz to podejście, jeśli nie masz unikalnego klucza w swojej tabeli.

cjs
źródło
3

Jeśli chcesz trwale ponumerować wiersze w tabeli, nie używaj rozwiązania RID dla SQL Server. Będzie działać gorzej niż Access na starym 386. W przypadku SQL Server po prostu utwórz kolumnę IDENTITY i użyj tej kolumny jako klastrowego klucza podstawowego. Spowoduje to umieszczenie w tabeli trwałego, szybkiego drzewa typu Integer B-Tree, a co ważniejsze, każdy indeks nieklastrowy użyje go do zlokalizowania wierszy. Jeśli spróbujesz programować w SQL Server tak, jakby to był Oracle, utworzysz słabo działającą bazę danych. Musisz zoptymalizować silnik, a nie udawać, że to inny silnik.

również nie używaj NewID () do wypełniania klucza podstawowego identyfikatorami GUID, zabijesz wydajność wstawiania. Jeśli musisz użyć identyfikatorów GUID, użyj wartości NewSequentialID () jako wartości domyślnej kolumny. Ale INT nadal będzie szybszy.

Jeśli z drugiej strony chcesz po prostu ponumerować wiersze, które wynikają z zapytania, użyj funkcji RowNumber Over () jako jednej z kolumn zapytania.

user2793105
źródło
1

Zobacz http://msdn.microsoft.com/en-us/library/aa260631(v=SQL.80).aspx W serwerze SQL sygnatura czasowa nie jest tym samym, co kolumna DateTime. Służy do jednoznacznej identyfikacji wiersza w bazie danych, a nie tylko tabeli, ale całej bazy danych. Może to służyć do optymistycznej współbieżności. na przykład UPDATE [Job] SET [Name] = @ Name, [XCustomData] = @ XCustomData WHERE ([ModifiedTimeStamp] = @ Original_ModifiedTimeStamp AND [GUID] = @ Original_GUID

ModifiedTimeStamp gwarantuje, że aktualizujesz oryginalne dane i zakończy się niepowodzeniem, jeśli kolejna aktualizacja wystąpi w wierszu.

Jim
źródło
0

Wziąłem ten przykład z przykładu MS SQL i widać, że @ID można zamienić z integer lub varchar lub czymkolwiek. To było to samo rozwiązanie, którego szukałem, więc się nim udostępniam. Cieszyć się!!

-- 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
Barry Austra
źródło
0

Możesz uzyskać ROWID, korzystając z poniższych metod:

1. Utwórz nową tabelę z polem automatycznego przyrostu

2. użyj funkcji analitycznej Row_Number, aby uzyskać sekwencję w oparciu o twoje wymagania. Wolałbym to, ponieważ pomaga w sytuacjach, w których chcesz, aby row_id był rosnący lub malejący określonego pola lub kombinacji pól

Przykład: Row_Number () Over (Partition by Deptno order by sal desc)

Powyższa próbka poda numer porządkowy oparty na najwyższym wynagrodzeniu każdego działu. Podział według jest opcjonalny i można go usunąć zgodnie z własnymi wymaganiami

Saurabh Gautam
źródło