Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Nowe zmiany w kolumnach zawierających tylko metadane w SQL Server 2016

ALTER TABLE ... ALTER COLUMN polecenie jest bardzo potężne. Możesz go użyć do zmiany typu danych, długości, precyzji, skali, wartości null, sortowania kolumny… i wielu innych rzeczy.

Jest to z pewnością wygodniejsze niż alternatywa:tworzenie nowej tabeli i migracja danych za każdym razem, gdy konieczna jest zmiana. Niemniej jednak niewiele można zrobić, aby ukryć leżącą u podstaw złożoność. Wraz z dużą liczbą ograniczeń dotyczących tego, co jest możliwe dzięki temu poleceniu, zawsze pojawia się kwestia wydajności.

Ostatecznie tabele są przechowywane jako sekwencja bajtów z pewnymi metadanymi w innym miejscu systemu, aby opisać, co każdy z tych bajtów oznacza i jak odnoszą się do poszczególnych kolumn tabeli. Kiedy prosimy SQL Server o zmianę jakiegoś aspektu definicji kolumny, musi sprawdzić, czy istniejące dane są zgodne z nową definicją. Musi również określić, czy obecny układ fizyczny musi się zmienić.

W zależności od typu zmiany i konfiguracji bazy danych, ALTER COLUMN polecenie będzie musiało wykonać jedną z następujących czynności:

  1. Zmień metadane tylko w tabelach systemowych.
  2. Sprawdź wszystkie istniejące dane pod kątem zgodności, a następnie zmień metadane.
  3. Przepisz niektóre lub wszystkie zapisane dane, aby pasowały do ​​nowej definicji.

Opcja 1 reprezentuje idealny przypadek z punktu widzenia wydajności. Wymaga tylko kilku zmian w tabelach systemowych i minimalnej ilości rejestrowania. Operacja nadal będzie wymagała restrykcyjnej modyfikacji schematu Sch-M blokada, ale zmiany metadanych zakończą się bardzo szybko, niezależnie od rozmiaru tabeli.

Zmiany tylko dla metadanych

Istnieje wiele specjalnych przypadków, na które należy zwrócić uwagę, ale ogólnie rzecz biorąc, następujące działania wymagają jedynie zmiany metadanych:

  • Przechodzę z NOT NULL na NULL dla tego samego typu danych.
  • Zwiększanie maksymalnego rozmiaru varchar , nvarchar lub varbinary kolumna (z wyjątkiem max ).

Ulepszenia w SQL Server 2016

Tematem tego posta są dodatkowe zmiany, które są włączone tylko dla metadanych od SQL Server 2016 i nowszych . Nie są potrzebne żadne zmiany składni i nie trzeba modyfikować ustawień konfiguracyjnych. Otrzymujesz te nieudokumentowane ulepszenia za darmo.

Nowe możliwości są ukierunkowane na podzbiór stałej długości typy danych. Nowe zdolności mają zastosowanie do tabel magazynu wierszy w następujących okolicznościach:

  • Kompresja musi być włączona:
    • Na wszystkich indeksach i partycjach , w tym stertę bazową lub indeks klastrowy.
    • Albo ROW lub PAGE kompresja.
    • Indeksy i partycje mogą używać mieszaniny tych poziomów kompresji. Ważną rzeczą jest to, że nie ma nieskompresowanych indeksów ani partycji.
  • Zmiana z NULL na NOT NULL jest niedozwolone .
  • Następujące zmiany typu liczb całkowitych są obsługiwane:
    • smallint na integer lub bigint .
    • integer do bigint .
    • smallmoney na money (używa wewnętrznie reprezentacji liczb całkowitych).
  • Następujące zmiany typu łańcuchowego i binarnego są obsługiwane:
    • char(n) do char(m) lub varchar(m)
    • nchar(n) do nchar(m) lub nvarchar(m)
    • binary(n) do binary(m) lub varbinary(m)
    • Wszystkie powyższe tylko dla n < m i m != max
    • Zmiany sortowania są niedozwolone

Te zmiany mogą dotyczyć tylko metadanych, ponieważ podstawowy układ danych binarnych nie zmienia się, gdy Deskryptor kolumny używany jest format wiersza (stąd potrzeba kompresji). Bez kompresji magazyn wierszy używa oryginalnej FixedVar reprezentacja, która nie może pomieścić tych zmian typu danych o stałej długości bez przepisania fizycznego układu.

Możesz zauważyć, że tinyint jest pomijany na liście typów liczb całkowitych. Dzieje się tak, ponieważ nie ma znaku, a wszystkie inne typy liczb całkowitych są ze znakiem, więc zmiana tylko metadanych nie jest możliwa. Na przykład wartość 255 może zmieścić się w jednym bajcie dla tinyint , ale wymaga dwóch bajtów w dowolnym z podpisanych formatów. Podpisane formaty mogą pomieścić od -128 do +127 w jednym bajcie po kompresji.

Przykład liczby całkowitej

Jednym z bardzo przydatnych zastosowań tego ulepszenia jest zmiana typu danych kolumny za pomocą IDENTITY właściwość.

Załóżmy, że mamy następującą tabelę sterty używającą kompresji wierszy (kompresja strony również zadziała):

DROP TABLE IF EXISTS dbo.Test;
GO
CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL,
    some_value integer NOT NULL
)
WITH (DATA_COMPRESSION = ROW);

Dodajmy 5 milionów wierszy danych. To wystarczy, aby było oczywiste (z punktu widzenia wydajności), czy zmiana typu danych kolumny jest operacją tylko na metadanych, czy nie:

WITH Numbers AS
(
    SELECT 
        n = ROW_NUMBER() OVER (ORDER BY @@SPID) 
    FROM sys.all_columns AS AC1
    CROSS JOIN sys.all_columns AS AC2
    ORDER BY n
    OFFSET 0 ROWS
    FETCH FIRST 5 * 1000 * 1000 ROWS ONLY
)
INSERT dbo.Test
    WITH (TABLOCKX)
(
    some_value
)
SELECT
    N.n
FROM Numbers AS N;

Następnie ponownie wstawimy IDENTITY aby wyglądało na to, że prawie kończy się nam wartość, która zmieści się w integer :

DBCC CHECKIDENT
(
    N'dbo.Test',
    RESEED,
    2147483646
);

Możemy pomyślnie dodać jeszcze jeden wiersz:

INSERT dbo.Test
    (some_value)
VALUES
    (123456);

Ale próbując dodać kolejny wiersz:

INSERT dbo.Test
    (some_value)
VALUES
    (7890);

Powoduje wyświetlenie komunikatu o błędzie:

Msg 8115, Poziom 16, Stan 1, Wiersz 1
Błąd przepełnienia arytmetycznego podczas konwersji IDENTITY na typ danych int.

Możemy to naprawić, konwertując kolumnę na bigint :

ALTER TABLE dbo.Test
ALTER COLUMN id bigint NOT NULL;

Dzięki ulepszeniom w SQL Server 2016 to polecenie zmienia tylko metadane i kończy się natychmiast. Poprzednia INSERT instrukcja (ta, która spowodowała błąd przepełnienia arytmetycznego) teraz kończy się pomyślnie.

Ta nowa możliwość nie rozwiązuje wszystkich problemów związanych ze zmianą typu kolumny za pomocą IDENTITY własność. Nadal będziemy musieli usunąć i odtworzyć wszystkie indeksy w kolumnie, odtworzyć wszelkie odwołujące się klucze obce i tak dalej. To trochę wykracza poza zakres tego postu (choć Aaron Bertrand pisał o tym wcześniej). Możliwość zmiany typu jako operacji tylko na metadanych z pewnością nie zaszkodzi. Dzięki starannemu planowaniu pozostałe wymagane kroki można wykonać tak wydajnie, jak to tylko możliwe, na przykład przy użyciu minimalnie rejestrowanych lub ONLINE operacje.

Uważaj na składnię

Pamiętaj, aby zawsze określ NULL lub NOT NULL podczas zmiany typów danych za pomocą ALTER COLUMN . Powiedzmy na przykład, że chcieliśmy również zmienić typ danych some_value kolumna w naszej tabeli testowej z integer NOT NULL na bigint NOT NULL .

Kiedy piszemy polecenie, pomijamy NULL lub NOT NULL kwalifikator:

ALTER TABLE dbo.Test
ALTER COLUMN some_value bigint;

To polecenie kończy się pomyślnie jako zmiana tylko metadanych, ale usuwa również NOT NULL ograniczenie. Kolumna jest teraz bigint NULL , co nie było naszym zamiarem. To zachowanie jest udokumentowane, ale łatwo je przeoczyć.

Możemy spróbować naprawić nasz błąd za pomocą:

ALTER TABLE dbo.Test
ALTER COLUMN some_value bigint NOT NULL;

To nie zmiana obejmująca tylko metadane. Nie możemy zmienić z NULL na NOT NULL (odnieś się do wcześniejszej tabeli, jeśli potrzebujesz odświeżenia warunków). SQL Server będzie musiał sprawdzić wszystkie istniejące wartości, aby upewnić się, że nie ma wartości null. Następnie fizycznie przepisze każdy wiersz tabeli. Oprócz tego, że same w sobie są powolne, działania te generują dużo dzienników transakcji, co może mieć efekt domina.

Na marginesie, ten sam błąd nie jest możliwy w przypadku kolumn z IDENTITY własność. Jeśli napiszemy ALTER COLUMN instrukcja bez NULL lub NOT NULL w takim przypadku silnik założy, że chodziło nam o NOT NULL ponieważ właściwość tożsamości nie jest dozwolona w kolumnach dopuszczających wartość null. Nadal dobrym pomysłem jest nie poleganie na takim zachowaniu.

Zawsze określaj NULL lub NOT NULL z ALTER COLUMN .

Składanie

Szczególna ostrożność jest wymagana podczas zmiany kolumny ciągu znaków, która ma sortowanie niezgodne z wartością domyślną dla bazy danych.

Załóżmy na przykład, że mamy tabelę z sortowaniem uwzględniającym wielkość liter i akcent (załóżmy, że domyślna baza danych jest inna):

DROP TABLE IF EXISTS dbo.Test2;
GO
CREATE TABLE dbo.Test2
(
    id integer IDENTITY NOT NULL,
    some_string char(8) COLLATE Latin1_General_100_CS_AS NOT NULL
)
WITH (DATA_COMPRESSION = ROW);

Dodaj 5 milionów wierszy danych:

WITH Numbers AS
(
    SELECT 
        n = ROW_NUMBER() OVER (ORDER BY @@SPID) 
    FROM sys.all_columns AS AC1
    CROSS JOIN sys.all_columns AS AC2
    ORDER BY n
    OFFSET 0 ROWS
    FETCH FIRST 5 * 1000 * 1000 ROWS ONLY
)
INSERT dbo.Test2
    WITH (TABLOCKX)
(
    some_string
)
SELECT
    CONVERT(char(8), N.n) COLLATE Latin1_General_100_CS_AS
FROM Numbers AS N;

Podwój długość kolumny ciągu za pomocą następującego polecenia:

ALTER TABLE dbo.Test2
ALTER COLUMN some_string char(16) NOT NULL;

Pamiętaliśmy o określeniu NOT NULL , ale zapomniałem o sortowaniu innym niż domyślny. SQL Server zakłada, że ​​zamierzaliśmy zmienić sortowanie na domyślne wartości bazy danych (Latin1_General_CI_AS dla mojej testowej bazy danych). Zmiana sortowania sprawia, że ​​operacja nie jest oparta tylko na metadanych, więc operacja działa przez kilka minut, generując mnóstwo dzienników.

Odtwórz tabelę i dane za pomocą poprzedniego skryptu, a następnie wypróbuj ALTER COLUMN ponownie, ale podając istniejące, niedomyślne sortowanie jako część polecenia:

ALTER TABLE dbo.Test2
ALTER COLUMN some_string 
    char(16) COLLATE Latin1_General_100_CS_AS NOT NULL;

Zmiana jest teraz wykonywana natychmiast, jako operacja tylko na metadanych. Podobnie jak w przypadku NULL i NOT NULL składni, opłaca się być jawnym, aby uniknąć wypadków. Ogólnie jest to dobra rada, nie tylko w przypadku ALTER COLUMN .

Kompresja

Należy pamiętać, że kompresja musi być wyraźnie określona dla każdego indeksu i osobno dla tabeli bazowej, jeśli jest to sterta. To kolejny przykład, w którym użycie skróconej składni lub skrótów może zapobiec pożądanemu wynikowi.

Na przykład poniższa tabela nie określa jawnej kompresji ani klucza podstawowego, ani definicji indeksu wbudowanego:

CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL PRIMARY KEY,
    some_value integer NOT NULL
        INDEX [IX dbo.Test some_value]
)
WITH (DATA_COMPRESSION = PAGE);

PRIMARY KEY będzie miał przypisaną nazwę, domyślnie CLUSTERED i być PAGE sprężony. Wbudowany indeks będzie NONCLUSTERED i wcale nie skompresowane. Ta tabela nie zostanie włączona dla żadnej z nowych optymalizacji, ponieważ nie wszystkie indeksy i partycje są skompresowane.

O wiele lepsza i bardziej precyzyjna definicja tabeli to:

CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL
        CONSTRAINT [PK dbo.Test id]
        PRIMARY KEY CLUSTERED
        WITH (DATA_COMPRESSION = PAGE),
    some_value integer NOT NULL
        INDEX [IX dbo.Test some_value]
        NONCLUSTERED
        WITH (DATA_COMPRESSION = ROW)        
);

Ta tabela kwalifikuje się do nowych optymalizacji, ponieważ wszystkie indeksy i partycje są skompresowane. Jak wspomniano wcześniej, mieszanie typów kompresji jest w porządku.

Istnieje wiele sposobów na napisanie tego CREATE TABLE oświadczenie w sposób jednoznaczny, więc istnieje element osobistych preferencji. Ważnym punktem na wynos jest zawsze być jednoznacznym o tym, czego chcesz. Dotyczy to oddzielnego CREATE INDEX również oświadczenia.

Rozszerzone zdarzenia i flaga śledzenia

Istnieje rozszerzone zdarzenie przeznaczone specjalnie dla nowej kolumny ALTER COLUMN zawierającej tylko metadane operacje obsługiwane w SQL Server 2016 i nowsze.

Rozszerzone zdarzenie to compressed_alter_column_is_md_only w Debugowaniu kanał. Jego pola zdarzeń to object_id , column_id i is_md_only (prawda/fałsz).

To zdarzenie wskazuje tylko, czy operacja obejmuje tylko metadane ze względu na nowe możliwości SQL Server 2016. Zmiany kolumn, które przed 2016 r. były tylko metadane, pokażą is_md_only = false pomimo tego, że nadal zawiera tylko metadane.

Inne rozszerzone zdarzenia przydatne do śledzenia ALTER COLUMN operacje obejmują metadata_ddl_alter_column i alter_column_event , oba w sekcji Analiza kanał.

Jeśli musisz wyłączyć nowe możliwości programu SQL Server 2016 z dowolnego powodu można użyć nieudokumentowanej globalnej (lub startowej) flagi 3618 śledzenia. Ta flaga śledzenia nie jest skuteczna, gdy jest używana na poziomie sesji. Nie ma możliwości określenia flagi śledzenia na poziomie zapytania za pomocą ALTER COLUMN polecenie.

Końcowe myśli

Możliwość zmiany niektórych typów danych całkowitych o stałej długości za pomocą zmiany samych metadanych jest bardzo pożądanym ulepszeniem produktu. Wymaga to, aby tabela była już w pełni skompresowana, ale i tak staje się to coraz bardziej powszechne. Jest to szczególnie ważne, ponieważ kompresja była włączona we wszystkich edycjach, począwszy od dodatku Service Pack 1 dla SQL Server 2016.

Kolumny typu string o stałej długości są prawdopodobnie znacznie mniej popularne. Niektóre z nich mogą wynikać z nieco nieaktualnych kwestii, takich jak wykorzystanie przestrzeni. Po skompresowaniu kolumny ciągów o stałej długości nie przechowują końcowych spacji, dzięki czemu są tak samo wydajne, jak kolumny ciągów o zmiennej długości z punktu widzenia przechowywania. Przycinanie przestrzeni do manipulacji lub wyświetlania może być denerwujące, ale jeśli dane zwykle zajmują większość maksymalnej długości, typy o stałej długości mogą mieć ważne zalety, nie tylko w odniesieniu do przyznawania pamięci na takie rzeczy, jak sortowanie i mieszanie.

To nie wszystkie dobre wieści z włączoną kompresją. Wspomniałem wcześniej, że SQL Server może czasami dokonać zmiany tylko w metadanych po sprawdzeniu, czy wszystkie istniejące wartości zostaną pomyślnie przekonwertowane na nowy typ. Tak jest w przypadku użycia ALTER COLUMN zmienić z integer na smallint na przykład. Niestety, te operacje nie dotyczą obecnie tylko metadanych dla skompresowanych obiektów.

Podziękowania

Specjalne podziękowania dla Panagiotis Antonopoulos (główny inżynier oprogramowania) i Mirek Sztajno (Senior Program Manager) z zespołu ds. produktu SQL Server za pomoc i wskazówki podczas badania i pisania tego artykułu.

Żadne szczegóły podane w tej pracy nie powinny być traktowane jako oficjalna dokumentacja firmy Microsoft lub oświadczenia dotyczące produktu.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Zachowaj relacje rodzic-dziecko podczas kopiowania danych hierarchicznych

  2. Jak mogę określić w SQL Server, czy zakres dat i godzin nakłada się na inny?

  3. Wdrażanie obciążenia przyrostowego za pomocą przechwytywania zmian danych w SQL Server

  4. 13 najlepszych praktyk w zakresie bezpieczeństwa SQL Server

  5. Nie można powiązać wieloczęściowego identyfikatora