Jedną z ciekawiej zapowiadających się nowości w języku Transact-SQL (T-SQL) w SQL Server 2012 była funkcja FORMAT. Wspomniał o niej choćby Marcin Nowakowski opisując nowe funkcje skalarne w najnowszej odsłonie systemu SQL Server.

FORMAT daje spore możliwości tworzenia ciągów znakowych sformatowanych według zadanego wzorca zgodnego z wybraną notacją narodową (np. wybór formatowania “pieniężnego” dla narodowości pl-PL, da nam ciąg znaków przedstawiających liczbę z walutą zł). Przykłady poniżej:

DECLARE
  @date date = '20120629',
  @money money = 1234567.89,
  @number int = 1,
  @percent numeric(2,1) = 0.3;
 
-- 29 czerwca 2012
SELECT FORMAT(@date, 'D', 'pl-PL');
 
-- piątek, 29 czerwca 2012
SELECT FORMAT(@date, 'dddd, dd MMMM yyyy', 'pl-PL');
 
-- Pt, 29.06.2012
SELECT FORMAT(@date, 'ddd, dd.MM.yyyy', 'pl-PL');
 
-- 1 234 567,89 zł
SELECT FORMAT(@money, 'C2', 'pl-PL');
 
-- 000001
SELECT FORMAT(@number, 'D6', 'pl-PL');
 
-- 30,00%
SELECT FORMAT(@percent, 'P2', 'pl-PL');

Wygląda ciekawie? Tak samo pomyślałem i pewnie będą sytuacje, w których ta funkcja się znajdzie zastosowanie. Warto zatem zapoznać się z dokumentacją funkcji oraz opisem szablonów formatujących, które są żywcem wzięte z technologii .NET.

Z dokumentacji funkcji dowiadujemy się m.in., że:

FORMAT relies on the presence of .the .NET Framework Common Language Runtime (CLR).

Czyli jest tam gdzieś pod spodem używana technologia .NET. Cudów nie ma. Postanowiłem sprawdzić, jaki to może mieć wpływ na wydajność.

Do testów na instancji SQL Server 2012 + CU2 (build 11.0.2325) zainstalowanej na laptopie (8 GB RAM, z czego 4 GB RAM dla SQL Servera, Intel Core i7) użyłem bazy AdventureWorksDW2012. Wykonywałem operacje prowadzące do produkowania tych samych wyników metodami znanymi z poprzednich wersji systemu SQL Server oraz za pomocą funkcji FORMAT. Skrypt do testów:

USE AdventureWorksDW2012;
GO
 
-- Test 1: padding
 
SET STATISTICS TIME ON;
GO
 
SELECT RIGHT('000000' + CONVERT(varchar(10), SalesOrderLineNumber), 6)
FROM dbo.FactInternetSales;
SELECT FORMAT(SalesOrderLineNumber, 'D6')
FROM dbo.FactInternetSales;
GO
 
SET STATISTICS TIME OFF;
GO
 
-- Test 2: numbers
 
SET STATISTICS TIME ON;
GO
 
SELECT CONVERT(varchar(20), SalesAmount, 1)
FROM dbo.FactInternetSales;
SELECT FORMAT(SalesAmount, 'N2')
FROM dbo.FactInternetSales;
GO
 
SET STATISTICS TIME OFF;
GO

Wyniki:

  • Test 1 – padding:
    • metoda klasyczna: CPU time = 0 ms,  elapsed time = 311 ms
    • metoda wykorzystująca funkcję FORMAT: CPU time = 1919 ms,  elapsed time = 2259 ms
  • Test 2 – numbers:
    • metoda klasyczna: CPU time = 15 ms,  elapsed time = 282 ms
    • metoda wykorzystująca funkcję FORMAT: CPU time = 2075 ms,  elapsed time = 2450 ms

Wniosek – funkcja FORMAT oferuje ciekawe możliwości, ale należy unikać stosowania jej tam, gdzie jesteśmy w stanie osiągnąć analogiczny wynik funkcjami “czysto T-SQL-owymi” (np. CONVERT). I raczej wypada stosować tę funkcję do produkowania pojedynczych napisów niż dużych zbiorów wartości. Oczywiście, może się okazać, że kryterium wydajności nie będzie dla nas najważniejsze i górę wezmą przyzwyczajenia z programowania w językach .NET lub po prostu wygoda. Podobnie, dla jednorazowo wykonywanych operacji, w których czas wykonania zadania nie jest priorytetem, funkcja ta może być bardzo przydatna. I wreszcie, jeśli w grę wchodzi generowanie wartości “nacjonalizowanych”, zastosowanie funkcji FORMAT wydaje się być najlepszym rozwiązaniem.