Czy w ogóle istnieje jakakolwiek różnica między NUMERIC a DECIMAL?

47

Wiem, że typy danych NUMERIC i DECIMAL w SQL Server działają tak samo: składnia ich tworzenia jest taka sama, zakresy wartości, które można w nich przechowywać, są takie same itp.

Dokumentacja MSDN opisuje jednak związek między nimi w następujący sposób:

numeryczny jest funkcjonalnie równoważny dziesiętnemu.

Normalnie, kiedy widzę kwalifikator „ funkcjonalnie równoważny”, oznacza to, że dwie rzeczy nie są dokładnie takie same, ale że są to dwa różne typy, które są nie do odróżnienia od zewnątrz .

Czy ta implikacja jest prawdziwa? Czy istnieją różnice między NUMERYCZNYM a DECIMALNYM, które akurat zachowują się tak samo dla obserwatora zewnętrznego? A może faktycznie są one równoważne, np. Czy NUMERIC jest po prostu synonimem DECIMAL?

KutuluMike
źródło
1
Możesz znaleźć różnice we wsparciu poza SQL Server, na przykład właśnie uświadomiłem sobie tę dziwną różnicę w SSIS .
Aaron Bertrand

Odpowiedzi:

56

W rzeczywistości są one równoważne, ale są niezależnymi typami, a nie technicznie synonimami, takimi jak ROWVERSIONi TIMESTAMP- chociaż mogły być jednocześnie określane w dokumentacji jako synonimy . Jest to nieco inne znaczenie synonimu (np. Są nierozróżnialne, z wyjątkiem nazwy, żaden nie jest pseudonimem dla drugiego). Ironiczne, prawda?

To, co interpretuję ze sformułowania w MSDN, to w rzeczywistości:

Te typy są identyczne, mają tylko różne nazwy.

Poza type_idwartościami wszystko tutaj jest identyczne:

SELECT * FROM sys.types WHERE name IN (N'numeric', N'decimal');

Nie mam absolutnie żadnej wiedzy na temat różnic w zachowaniu między nimi, a wracając do SQL Server 6.5, zawsze traktowałem je jako w 100% wymienne.

dla DECIMAL (18,2) i NUMERIC (18,2)? Przypisywanie sobie nawzajem jest technicznie „konwersją”?

Tylko jeśli zrobisz to wyraźnie. Możesz to łatwo udowodnić, tworząc tabelę, a następnie sprawdzając plan zapytań pod kątem zapytań, które wykonują jawne lub - można się spodziewać - niejawne konwersje. Oto prosty stół:

CREATE TABLE [dbo].[NumDec]
(
    [num] [numeric](18, 0) NULL,
    [dec] [decimal](18, 0) NULL
);

Teraz uruchom te zapytania i zrób plan:

DECLARE @num NUMERIC(18,0);
DECLARE @dec DECIMAL(18,0);

SELECT 
  CONVERT(DECIMAL(18,0), [num]), -- conversion
  CONVERT(NUMERIC(18,0), [dec])  -- conversion
FROM dbo.NumDec
UNION ALL SELECT [num],[dec] 
  FROM dbo.NumDec WHERE [num] = @dec  -- no conversion
UNION ALL SELECT [num],[dec] 
  FROM dbo.NumDec WHERE [dec] = @num; -- no conversion

Jak pokazano w SQL Sentry Plan Explorer *, plan nie jest tak naprawdę interesujący:

wprowadź opis zdjęcia tutaj

Ale zakładka Wyrażenia jest z pewnością:

wprowadź opis zdjęcia tutaj

Jak skomentowałem powyżej, mamy wyraźne konwersje tam, gdzie o nie poprosiliśmy, ale żadnych wyraźnych konwersji, w których moglibyśmy się ich spodziewać. Wydaje się, że optymalizator również traktuje je jako wymienne.

Śmiało i wypróbuj również ten test (dane i indeksy).

CREATE TABLE [dbo].[NumDec2]
(
    [num] [numeric](18, 2) NULL,
    [dec] [decimal](18, 2) NULL
);

INSERT dbo.NumDec2([num],[dec])
SELECT [object_id] + 0.12, [object_id] + 0.12
  FROM sys.all_columns;

CREATE INDEX [ix_num] ON dbo.NumDec2([num]);
CREATE INDEX [ix_dec] ON dbo.NumDec2([dec]);

Teraz uruchom to zapytanie:

DECLARE @num NUMERIC(18,2) = -1291334356.88,
        @dec NUMERIC(18,2) = -1291334356.88;

SELECT [dec] FROM dbo.NumDec2 WHERE [dec] = @num
UNION ALL
SELECT [num] FROM dbo.NumDec2 WHERE [num] = @dec;

Plan nie zawiera żadnych konwersji (w rzeczywistości karta Wyrażenia jest pusta):

wprowadź opis zdjęcia tutaj

Nawet te nie prowadzą do nieoczekiwanych konwersji. Oczywiście widzisz to na RHS w predykacie, ale w żadnym wypadku nie musiała wystąpić żadna konwersja w stosunku do danych kolumny, aby ułatwić wyszukiwanie (znacznie mniej wymuszając skanowanie).

SELECT [dec] FROM dbo.NumDec2 WHERE [dec] = CONVERT(DECIMAL(18,2), @num)
UNION ALL
SELECT [dec] FROM dbo.NumDec2 WHERE [dec] = CONVERT(DECIMAL(18,2), @dec)
UNION ALL
SELECT [num] FROM dbo.NumDec2 WHERE [num] = CONVERT(NUMERIC(18,2), @num)
UNION ALL
SELECT [num] FROM dbo.NumDec2 WHERE [num] = CONVERT(NUMERIC(18,2), @dec)
UNION ALL
SELECT [num] FROM dbo.NumDec2 WHERE [num] = CONVERT(DECIMAL(18,2), @num)
UNION ALL
SELECT [num] FROM dbo.NumDec2 WHERE [num] = CONVERT(DECIMAL(18,2), @dec)
UNION ALL
SELECT [dec] FROM dbo.NumDec2 WHERE [dec] = CONVERT(NUMERIC(18,2), @num)
UNION ALL
SELECT [dec] FROM dbo.NumDec2 WHERE [dec] = CONVERT(NUMERIC(18,2), @dec);

Osobiście wolę używać tego terminu DECIMALtylko dlatego, że jest on o wiele bardziej dokładny i opisowy. BITjest również „numeryczny”.

* Disclaimer: I work for SQL Sentry.
Aaron Bertrand
źródło
Wiem, że SQL traktuje np. DECIMAL (18,2) i DECIMAL (18,0) jako „różne typy”; czy to oznacza to samo dla DECIMAL (18,2) i NUMERIC (18,2)? Przypisywanie sobie nawzajem jest technicznie „konwersją”?
KutuluMike
@MichaelEdenfield Zaktualizowałem moją odpowiedź na nowe pytanie.
Aaron Bertrand
1
Wiem, że to stara odpowiedź, ale właśnie znalazłem przypadek, w którym myślenie, że numeryczne i dziesiętne są synonimami, spowodowało błąd w programie SQL Server. Pomyślałem, że powinienem tu zostawić komentarz na wypadek, gdyby ktoś inny mógł z niego skorzystać.
Zohar Peled,
@AaronBertrand Głosowałem za twoją odpowiedzią, ponieważ jest pomocna. Oba typy wyglądają na identyczne, więc jeśli tak, to dlaczego mają inne nazwy? czy jest jakiś historyczny powód ?
Ivanzinho
@Ivanzinho Nie jestem pewien, czy twoje pytanie jest retoryczne, istnieje kilka teorii (zarówno na tej stronie, jak i odpowiedzi, do której linkujesz), ale jeśli chcesz ostatecznej odpowiedzi, prawdopodobnie nie będziesz miał szczęścia. Trzeba będzie wyśledzić oryginalnych inżynierów, którzy jako pierwsi wdrożyli oba typy w SQL Server, i polegać na ich pamięci zarówno na tej implementacji, jak i na interpretacji standardu w tym czasie.
Aaron Bertrand
15

Są jednak identyczne w praktyce (ze standardu SQL 2003):

21) NUMERIC określa dokładny typ danych z dokładnością dziesiętną i skalą określoną przez precisioni scale.

22) DECIMAL określa dokładny numeryczny typ danych, przy czym skala dziesiętna jest określona przez scalei precyzja dziesiętna zdefiniowana w implementacji jest równa lub większa niż wartość określonej precision.

Neil McGuigan
źródło
5
Podstawowe pytanie (i wierzę, że odpowiedź brzmi nie), czy SQL Server implementował precyzję DECIMAL inaczej niż NUMERIC. To, co mówi standard, że można zrobić, jest znacznie mniej istotne niż to, co faktycznie zrobiono.
Aaron Bertrand
5
Dzięki. To doskonała odpowiedź na kolejne pytanie, dlaczego istnieją dwa typy o różnych nazwach, ale identycznym zachowaniu.
Gabe