Mathematische Funktionen in Natively Compiled Stored Procedures

Dass der SQL Server 2014 mit seinen InMemory-Fähigkeiten einen erheblichen Performance-Boost in vielen praktischen Einsatzmöglichkeiten ermöglicht, haben wir bereits ausführlich dokumentiert.

Einer der Faktoren, die eine erhebliche Beschleunigung von InMemory-Abfragen ermöglichen, sind nativ kompilierte Stored Procedures (NCSPs). Diese bieten zwar eine erhebliche Beschleunigung, erlauben aber nur einen eingeschränkten Zugriff auf die reichhaltigen Funktionen von T-SQL. Wir möchten hier einen typischen Workaround für eine solche Einschränkung vorstellen. 

Oftmals steht der Entwickler beim Schreiben von NCSPs vor der Fehlermeldung

The function ‚…‘ is not supported with natively compiled stored procedures. 

 

Für viele mathematische Funktionen gibt es dabei elegante Workarounds. Zum Beispiel ist abs(@arg) = sqrt(power(@arg,2)).

Etwas komplizierter wird es bei Funktionen wie Ceil und Floor oder auch Sign, die abhängig vom Vorzeichen sind.

Da T-SQL keine CASE-Konstrukte in NCSPs erlaubt, müssen wir für das korrekte Verhalten an dieser Stelle mit TRY/CATCH-Konstrukten arbeiten. Die drei Funktionen Ceil, Floor und Sign lassen sich dann wie folgt umsetzen:

if OBJECT_ID('dbo.Mathtest') is not null
 drop procedure dbo.MathTest
go
create procedure dbo.MathTest(@arg numeric(10,2))
with native_compilation, schemabinding, execute as owner
as 
begin atomic with (transaction isolation level = snapshot, language = N'English')
 declare @sign int
 declare @dump numeric(10,2)
 begin try 
 select @sign = cast(@arg/SQRT(POWER(@arg,2)) as numeric(10,0))
 end try
 begin catch -- division by zero means, @arg is zero
 select @sign = 0
 end catch
 begin try 
 select @dump = 1/(@sign*(@arg%1)) -- is our number integer or zero? 
 -- nmber is not zero
 select cast(@arg-(@arg % 1) + (-1+@sign)*0.5 as int) as [floor], cast(@arg-(@arg%1) + (1+@sign)*0.5 as int) as [ceil], @sign as [sign]
 end try
 begin catch -- @arg is integer or zero!
 select @arg as [floor], @arg as [ceil], @sign as [sign]
 end catch 
end
go

Die Tests zeigen das erwartete Verhalten der Funktionen:

declare @arg numeric(10,2)
set @arg = -3.14
exec dbo.MathTest @arg
	
select floor(@arg), ceiling(@arg)

liefert als Werte für Floor und Ceiling übereinstimmend -4 bzw. -3 und für @arg = 3.14 erhalten wir wie erwartet 3 und 4.

Welche Funktionen vermissen Sie, welche Workarounds haben Sie gefunden? Bitte teilen Sie Ihre Erfahrungen mit uns in den Kommentaren zu diesem Beitrag.

Schreibe einen Kommentar