• Bazy danych i SQL
  • CAST w SQL Serverze bez błędów - składnia, TRY_CAST i pułapki

CAST w SQL Serverze bez błędów - składnia, TRY_CAST i pułapki

SQL Server TRY_CONVERT w zapytaniu i planie wykonania, tworzenie indeksu dla optymalizacji.

Spis treści

Jedna kolumna przechowuje liczbę jako tekst, inna zwraca datę z godziną, a raport oczekuje wartości dziesiętnej. W takich sytuacjach funkcja CAST w SQL Serverze pozwala jawnie zmienić typ danych i uniknąć przypadkowych wyników konwersji niejawnej. Pokażę składnię, praktyczne przykłady, różnice między CAST i CONVERT, bezpieczne użycie TRY_CAST oraz pułapki związane z wydajnością i precyzją.

Najważniejsze zasady konwersji typów w SQL Serverze

  • CAST zmienia typ wyrażenia według prostej, czytelnej składni.
  • CONVERT przydaje się szczególnie przy formatowaniu dat dzięki parametrowi style.
  • TRY_CAST zwraca NULL, gdy poprawna konwersja wartości się nie powiedzie.
  • Rozmiar typu, na przykład varchar(10) lub decimal(10,2), wpływa na wynik.
  • Rzutowanie kolumny w filtrze lub połączeniu może ograniczyć użycie indeksu.

Jak działa CAST i jaka jest jego składnia

CAST przyjmuje wyrażenie oraz typ docelowy. Najprostszy wzór wygląda tak:

CAST(wyrażenie AS typ_danych)

Przykład konwersji liczby całkowitej na tekst:

SELECT CAST(123 AS varchar(10)) AS NumerTekstowy;

Wynikiem będzie tekst 123, a nie wartość typu int. To rozróżnienie ma znaczenie przy łączeniu wartości, porównywaniu kolumn i budowaniu komunikatów. W praktyce najczęściej jawnie określam typ wtedy, gdy chcę, aby kod nie zależał od automatycznych reguł SQL Servera.

Typ docelowy może zawierać parametr długości albo precyzji:

SELECT CAST(123.4567 AS decimal(10,2)) AS Cena;
SELECT CAST('Kursdotnet.pl' AS varchar(20)) AS Nazwa;

Pierwsze wyrażenie zwróci wartość 123.46, ponieważ typ decimal(10,2) przechowuje dwie cyfry po przecinku. Parametr scale, czyli liczba miejsc po przecinku, nie jest tylko opisem formatu. Wpływa na sposób zaokrąglenia i na to, czy wynik zmieści się w wybranym typie.

Konwersja liczb wymaga kontroli precyzji

Rzutowanie wartości dziesiętnej na int usuwa część ułamkową:

SELECT CAST(12.99 AS int) AS Wynik;

Wynik to 12, a nie 13. To częsta przyczyna błędów w raportach, szczególnie gdy ktoś traktuje konwersję typu jak zaokrąglanie. Jeśli potrzebuję zaokrąglenia, używam najpierw ROUND, a dopiero później zmieniam typ:

SELECT CAST(ROUND(12.99, 0) AS int) AS Wynik;

Przy dzieleniu również trzeba uważać na typy operandów. Dzielenie dwóch wartości całkowitych może zwrócić wynik całkowity:

SELECT 7 / 2 AS DzielenieCalkowite;
SELECT CAST(7 AS decimal(10,2)) / 2 AS DzielenieDziesietne;

W pierwszym przypadku otrzymamy 3, w drugim wynik dziesiętny. Samo dodanie jednego operandu dziesiętnego często wystarcza, aby zachować oczekiwaną część ułamkową.

Konwersja tekstu, dat i wartości liczbowych w praktyce

Najwięcej problemów pojawia się przy danych pochodzących z plików CSV, formularzy i starszych systemów. Tekst może wyglądać jak liczba lub data, ale dla SQL Servera nadal pozostaje zwykłym ciągiem znaków. Jawna konwersja porządkuje takie dane, pod warunkiem że wartość rzeczywiście ma poprawny format.

Tekst na liczbę

SELECT CAST('450' AS int) AS Liczba;
SELECT CAST('19.95' AS decimal(10,2)) AS Kwota;

Oba przykłady zadziałają, ponieważ tekst odpowiada typowi docelowemu. Problem pojawi się przy wartości takiej jak '19,95 zł' albo 'brak danych'. Funkcja CAST przerwie wtedy wykonanie zapytania błędem konwersji. Dlatego przy imporcie danych nie zakładam, że każda komórka jest poprawna.

Liczba na tekst

Przy konwersji na varchar lub nvarchar trzeba dobrać odpowiednią długość:

SELECT CAST(987654 AS varchar(20)) AS Numer;
SELECT CAST(N'Zażółć gęślą jaźń' AS nvarchar(30)) AS TekstUnicode;

Dla polskich znaków bezpieczniejszy jest nvarchar wraz z prefiksem N. Zbyt krótki typ tekstowy może obciąć wynik, a później trudno ustalić, czy problem powstał w zapytaniu, czy już podczas zapisu danych. W kodzie produkcyjnym nie dobieram długości „na oko”, tylko sprawdzam maksymalną wartość i sposób użycia kolumny.

Tekst na datę

Najbezpieczniej korzystać z jednoznacznego formatu ISO:

SELECT CAST('2026-09-16' AS date) AS Data;
SELECT CAST('2026-09-16T14:30:00' AS datetime2(0)) AS DataCzas;

Unikam zapisów typu '16/09/2026', jeśli zapytanie może działać na serwerze z innymi ustawieniami językowymi lub formatem daty. Sama wartość wygląda dla człowieka oczywiście, ale interpretacja tekstu może zależeć od ustawień sesji. Przy danych z zewnętrznego źródła jednoznaczny format wejściowy jest ważniejszy niż krótka składnia.

Gdy potrzebuję konkretnego formatu tekstowego, wybieram zwykle CONVERT:

SELECT CONVERT(varchar(10), CAST('2026-09-16' AS date), 23) AS DataTekstowa;

Styl 23 zwraca datę w postaci yyyy-mm-dd. W aplikacji .NET formatowanie dat zazwyczaj lepiej wykonać w warstwie prezentacji, a nie w zapytaniu. SQL powinien zwracać prawdziwy typ date lub datetime2, jeśli odbiorca nadal ma wykonywać na nim operacje.

CAST czy CONVERT i kiedy wybrać każdą funkcję

Obie funkcje służą do konwersji typów, ale mają nieco inne zastosowanie. CAST jest prostszy i zgodny ze standardem SQL, natomiast CONVERT jest charakterystyczny dla SQL Servera i oferuje dodatkowy parametr style przy datach oraz niektórych typach tekstowych.

Potrzeba Lepszy wybór Dlaczego
Zwykła zmiana typu CAST Składnia jest krótka i łatwa do przeniesienia między bazami.
Formatowanie daty jako tekstu CONVERT Parametr style pozwala wskazać format, na przykład 23 lub 112.
Czytelność zapytania CAST Wyraźnie pokazuje, że chodzi o rzutowanie wyrażenia.
Kod ściśle zależny od SQL Servera CONVERT Udostępnia możliwości specyficzne dla tego silnika.

Przykład użycia stylu 112:

SELECT CONVERT(char(8), CAST('2026-09-16' AS date), 112) AS DataYYYYMMDD;

Wynik to 20260916. Taki zapis bywa przydatny w kluczach technicznych i eksportach, ale nie powinien zastępować typu daty w tabeli. Zapisanie daty jako tekstu odbiera bazie możliwość wygodnego sortowania, filtrowania i korzystania z funkcji daty.

Moja praktyczna zasada jest prosta. Używam CAST do zwykłego rzutowania, a po CONVERT sięgam wtedy, gdy naprawdę potrzebuję kontrolować styl albo świadomie korzystam z funkcji specyficznych dla SQL Servera.

TRY_CAST pomaga przy brudnych danych

Jeśli dane pochodzą od użytkownika lub z importu, zwykły CAST może zatrzymać całe zapytanie przez jedną niepoprawną wartość. TRY_CAST próbuje wykonać tę samą operację, ale w przypadku błędu zwraca NULL:

SELECT TRY_CAST('123' AS int) AS PoprawnaWartosc;
SELECT TRY_CAST('abc' AS int) AS NiepoprawnaWartosc;

Drugi wynik będzie pusty, czyli równy NULL. Dzięki temu można wykryć wadliwe rekordy bez przerywania całego procesu:

SELECT Id, WartoscTekstowa
FROM ImportDanych
WHERE TRY_CAST(WartoscTekstowa AS decimal(12,2)) IS NULL
  AND WartoscTekstowa IS NOT NULL;

Ten wzorzec dobrze sprawdza się podczas walidacji plików i tabel tymczasowych. Nie rozwiązuje jednak problemu biznesowego sam z siebie. Po znalezieniu błędnych danych trzeba zdecydować, czy rekord odrzucić, poprawić, skierować do tabeli wyjątków czy oznaczyć do ręcznej weryfikacji.

TRY_CAST nie oznacza, że każda konwersja stanie się dozwolona. Jeśli SQL Server nie obsługuje jawnego przejścia między dwoma typami, funkcja nadal może zgłosić błąd. Działa przede wszystkim wtedy, gdy typy są kompatybilne, lecz konkretna wartość ma nieprawidłową postać.

Do danych zależnych od kultury można rozważyć TRY_PARSE, ale korzystam z niego ostrożnie. Opiera się na mechanizmach .NET i bywa cięższy wydajnościowo, dlatego do typowych konwersji w SQL Serverze preferuję CAST, CONVERT albo TRY_CAST.

Konwersja może zmienić wydajność zapytania

Najprostszy zapis nie zawsze jest najlepszy dla indeksów. Jeżeli rzutujemy kolumnę w warunku WHERE, silnik może nie wykorzystać indeksu tak efektywnie, jak przy bezpośrednim porównaniu:

SELECT *
FROM Zamowienia
WHERE CAST(DataZamowienia AS date) = '2026-09-16';

Przy dużej tabeli lepszy bywa przedział bez konwersji kolumny:

SELECT *
FROM Zamowienia
WHERE DataZamowienia >= '2026-09-16'
  AND DataZamowienia < '2026-09-17';

Drugie zapytanie zachowuje warunek SARGable, czyli taki, który pozwala optymalizatorowi skuteczniej użyć indeksu. Różnica może być niewielka przy kilku tysiącach rekordów, ale przy milionach wierszy przekłada się na odczyt stron, czas wykonania i obciążenie serwera.

Podobny problem występuje przy łączeniu kolumn o różnych typach. Jeśli jedna tabela przechowuje identyfikator jako int, a druga jako varchar, SQL Server może wykonać niejawną konwersję po jednej ze stron. To nie tylko komplikuje plan wykonania, ale również może spowodować błąd, gdy tekst zawiera wartość nienumeryczną.

SELECT *
FROM Klienci AS k
JOIN ImportKlientow AS i
  ON k.Id = TRY_CAST(i.IdTekst AS int);

Taki zapis może być użyteczny podczas jednorazowego czyszczenia importu, ale nie powinien maskować złego modelu danych. Docelowo powiązane kolumny powinny mieć ten sam typ i zgodną semantykę. Konwersja w każdym zapytaniu jest zwykle sygnałem, że warto uporządkować schemat albo etap ładowania danych.

Pułapki, które najczęściej psują wynik

Nieodpowiednia długość tekstu

Rzutowanie do varchar(5) nie zachowa wartości dłuższej niż pięć znaków. Nawet jeśli zapytanie wykona się poprawnie, rezultat może być obcięty. Zawsze sprawdzam długość danych przed konwersją, szczególnie podczas przygotowywania eksportu.

Za mała precyzja typu decimal

W zapisie decimal(10,2) pierwsza liczba oznacza całkowitą liczbę cyfr, a druga liczbę cyfr po przecinku. Oznacza to maksymalnie osiem cyfr przed przecinkiem. Kwota lub wynik obliczenia, który przekroczy ten zakres, może doprowadzić do błędu przepełnienia.

Konwersja NULL

CAST(NULL AS int) zwróci NULL. Sama funkcja nie zamienia pustej wartości na zero ani na pusty tekst. Jeśli aplikacja wymaga wartości zastępczej, używam jawnie COALESCE:

SELECT COALESCE(CAST(NULL AS decimal(10,2)), 0.00) AS Kwota;

Przeczytaj również: YYYY-MM-DD w SQL - formatowanie dat w 5 bazach

Format prezentacyjny zamiast typu danych

Data zapisana jako varchar może wyglądać atrakcyjnie w raporcie, ale przestaje być datą. Nie da się wtedy bez dodatkowej konwersji wygodnie obliczyć różnicy dni ani poprawnie sortować według chronologii. Formatowanie zostawiam na końcowy etap, a w tabelach przechowuję właściwy typ.

Zanim zostawisz CAST w produkcyjnym zapytaniu

Najpierw sprawdź, czy konwersja jest naprawdę potrzebna i czy odbywa się po właściwej stronie wyrażenia. Do prostego rzutowania wybierz CAST, do formatowania dat użyj świadomie CONVERT, a przy niepewnych danych zastosuj TRY_CAST.

Jeśli wynik ma znaczenie finansowe lub wpływa na filtrowanie, przetestuj precyzję, wartości graniczne, NULL oraz niepoprawne rekordy. Tych kilku testów nie widać w krótkim przykładzie, ale to one decydują, czy konwersja będzie bezpieczna także po wdrożeniu.

Dobrze użyty CAST porządkuje zapytanie i jasno pokazuje intencję autora. Źle użyty potrafi ukryć problem z modelem danych, obciąć wartości albo odebrać indeksowi szansę na szybkie działanie.

FAQ - Najczęstsze pytania

CAST sprawdza się przy zwykłej, czytelnej zmianie typu i jest zgodny ze standardem SQL. CONVERT warto wybrać, gdy trzeba kontrolować format daty lub tekstu za pomocą parametru style, na przykład 23 dla formatu yyyy-mm-dd albo 112 dla zapisu yyyyMMdd.

TRY_CAST zwraca NULL zamiast przerywać zapytanie błędem, gdy wartość nie pasuje do typu docelowego. Możesz użyć go w warunku WHERE, aby znaleźć rekordy, dla których konwersja do decimal lub int się nie powiodła, pomijając wartości już równe NULL.

Konwersja CAST(12.99 AS int) usuwa część ułamkową i zwraca 12, a nie 13. Jeśli potrzebujesz zaokrąglenia, najpierw zastosuj ROUND, a dopiero potem rzutuj wynik na int.

Rzutowanie kolumny w warunku WHERE może ograniczyć skuteczne użycie indeksu. Zamiast CAST(DataZamowienia AS date) = '2026-09-16' lepiej zastosować przedział od '2026-09-16' włącznie do '2026-09-17' wyłącznie, ponieważ zachowuje on warunek SARGable.

W decimal(10,2) liczba 10 oznacza całkowitą liczbę cyfr, a 2 liczbę cyfr po przecinku, więc zbyt duża wartość może spowodować przepełnienie. Przy varchar trzeba dobrać długość do danych, ponieważ zbyt krótki typ może obciąć wynik, a dla polskich znaków bezpieczniejszy jest nvarchar z prefiksem N.

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi

cast
convert
typy danych
indeksy
try_cast
Autor Przemysław Kwiatkowski
Przemysław Kwiatkowski
Jestem Przemysław i od 15 lat zajmuję się programowaniem .NET, chmurą Azure oraz sztuczną inteligencją. Moja przygoda z tymi technologiami zaczęła się od fascynacji możliwościami, jakie dają, a z czasem przerodziła się w pasję do tworzenia rozwiązań, które realnie wpływają na pracę i życie ludzi. Na kursdotnet.pl staram się dzielić się swoją wiedzą w sposób przystępny, tłumacząc złożone zagadnienia i pomagając zrozumieć, jak te dynamicznie rozwijające się obszary IT mogą być wykorzystane w praktyce. Dokładam wszelkich starań, aby prezentowane przeze mnie materiały były rzetelne, aktualne i oparte na sprawdzonych źródłach, a także aby uporządkować wiedzę w sposób ułatwiający jej przyswojenie.

Udostępnij artykuł

Napisz komentarz