Czy jest jakiś sposób na zmianę typu danych kolumny jako operacji tylko na metadanych?
Nie sądzę, tak właśnie działa ten produkt. Istnieje kilka naprawdę dobrych obejść tego ograniczenia zaproponowanego w odpowiedzi Joe .
... powoduje, że SQL Server przepisuje całą tabelę (i używa 2x rozmiaru tabeli w obszarze dziennika)
Odpowiem na dwie części tego oświadczenia osobno.
Przepisywanie tabeli
Jak wspomniałem wcześniej, tak naprawdę nie da się tego uniknąć. Wydaje się, że taka jest sytuacja, nawet jeśli nie ma to pełnego sensu z naszej perspektywy jako klientów.
Spojrzenie na DBCC PAGE
przed i po zmianie kolumny z 4000 na 260 pokazuje, że wszystkie dane są powielone na stronie danych (moja tabela testowa miała 'A'
260 razy w rzędzie):
W tym momencie na stronie znajdują się dwie kopie dokładnie takich samych danych. „Stara” kolumna jest zasadniczo usuwana (identyfikator jest zmieniany z id = 2 na id = 67108865), a „nowa” wersja kolumny jest aktualizowana, aby wskazywała nowe przesunięcie danych na stronie:
Używanie 2x rozmiaru tabeli w obszarze dziennika
Dodanie WITH (ONLINE = ON)
na końcu ALTER
instrukcji zmniejsza aktywność rejestrowania o około połowę , więc jest to jedno ulepszenie, które można wprowadzić, aby zmniejszyć ilość potrzebnych zapisów na dysku / dysku.
Użyłem tej uprzęży testowej, aby ją wypróbować:
USE [master];
GO
DROP DATABASE IF EXISTS [248749];
GO
CREATE DATABASE [248749]
ON PRIMARY
(
NAME = N'248749',
FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017\MSSQL\DATA\248749.mdf',
SIZE = 2048000KB,
FILEGROWTH = 65536KB
)
LOG ON
(
NAME = N'248749_log',
FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017\MSSQL\DATA\248749_log.ldf',
SIZE = 2048000KB,
FILEGROWTH = 65536KB
);
GO
USE [248749];
GO
CREATE TABLE dbo.[table]
(
id int IDENTITY(1,1) NOT NULL,
[col] nvarchar (4000) NULL,
CONSTRAINT [PK_test] PRIMARY KEY CLUSTERED (id ASC)
);
INSERT INTO dbo.[table]
SELECT TOP (1000000)
REPLICATE(N'A', 260)
FROM master.dbo.spt_values v1
CROSS JOIN master.dbo.spt_values v2
CROSS JOIN master.dbo.spt_values v3;
GO
Sprawdziłem sys.dm_io_virtual_file_stats(DB_ID(N'248749'), DEFAULT)
przed i po uruchomieniu ALTER
instrukcji, a oto różnice:
Domyślnie (offline) ALTER
- Plik danych zapisuje / zapisuje bajty: 34 809/2 193,801,216
- Zapis pliku dziennika / zapisane bajty: 40 953/1 484 910 080
online ALTER
- Zapis / zapis danych w pliku danych: 36 874/1 693 745 152 (spadek o 22,8%)
- Zapis pliku dziennika / zapisane bajty: 24.680 / 866,166,272 (spadek o 41%)
Jak widać, nastąpił niewielki spadek zapisów pliku danych i znaczny spadek zapisów pliku dziennika.
update table set new_col = old_col where new_col <> old_col;
przed upuszczeniemold_col
.where new_col <> old_col
nie spowodują żadne inne klauzule filtrujące, możesz dodać wyzwalacz, aby przenieść te zmiany w miarę ich występowania i usunąć je na końcu procesu. Wciąż potencjalne uderzenie wydajności, ale wiele małych ilości w ciągu całego procesu zamiast jednego wielkiego trafienia na końcu, prawdopodobnie (w zależności od wzorca aktualizacji aplikacji dla tabeli) w sumie znacznie mniej niż w przypadku tego jednego wielkiego trafienia .Istnieje alternatywa zależna od dostępnego miejsca w bazie danych.
Utwórz dokładną kopię tabeli (np.
new_table
), Z wyjątkiem kolumny, w której będziesz skracał zNVARCHAR(4000)
doNVARCHAR(260)
:W oknie konserwacji skopiuj dane z tabeli „zepsutej” (
table
) do tabeli „naprawionej” (new_table
) za pomocą prostegoINSERT ... INTO ... SELECT ....
:Zmień nazwę tabeli „zepsuty”
table
na coś innego:Zmień nazwę tabeli „stałej”
new_table
natable
:Jeśli wszystko jest w porządku, upuść tabelę o zmienionej nazwie: „zepsuta”:
Proszę bardzo.
Odpowiadając na twoje pytania
Nie. Obecnie nie jest możliwe
Nie.
( Zobacz moje rozwiązanie i inne. )
źródło