7

Anyone here had luck with creating and using UDFs on Azure data warehouse database? I am in the middle of migrating an on-prem warehouse from SQL Server 2014 to Azure datawarehouse and I ran into an issue with UDFs.

CREATE FUNCTION dbo.fn_GetImpliedRate (@Multiple float, @term int)
RETURNS float
AS
BEGIN
    DECLARE @ImpInt float
    IF(@Term = 1)
        SET @ImpInt = (select [1] from  dbo.ImpliedRate where Multiple = @Multiple); 
    IF(@Term = 2)
        SET @ImpInt = (select [2] from  dbo.ImpliedRate where Multiple = @Multiple); 
    IF(@Term = 3)
        SET @ImpInt = (select [3] from  dbo.ImpliedRate where Multiple = @Multiple); 
    IF(@Term = 4)
        SET @ImpInt = (select [4] from  dbo.ImpliedRate where Multiple = @Multiple); 

RETURN @ImpInt

END;
GO

This UDF works perfectly on SQL Server 2014. When I create this on Azure data warehouse, it gets created but it doesn't work when I query it. It returns a NULL. I have verified obvious things like whether the target table exists etc. All check. I looked at CREATE FUNCTION documentation for Azure data warehouse and it has an example UDF that converts int to a decimal. This works flawlessly on Azure DW. The moment I write a simple function that has a select, it fails. Unfortunately Azure's documentation here is not really helpful and I was wondering if any of you ran into this issue. If yes, how did you resolve?

I just tested another use case and it also doesn't work:

CREATE function [dbo].[fn_GetNumberBusinessDays] 
(
    @StartDate datetime,
    @EndDate Datetime
)
returns int
as 
begin 

DECLARE @NDAYS INT = 0
SELECT @NDAYS = 
  ISNULL( (DATEDIFF(dd, @StartDate, @EndDate) + 1)
  -(DATEDIFF(wk, @StartDate, @EndDate) * 2)
  -(CASE WHEN DATENAME(dw, @StartDate) = 'Sunday' THEN 1 ELSE 0 END)
  -(CASE WHEN DATENAME(dw, @EndDate) = 'Saturday' THEN 1 ELSE 0 END) - 1 ,0) + 1


 SELECT @NDAYS = @NDAYS - COUNT(*)
 FROM dbo.FedHolidays
 WHERE DateOfHoliday BETWEEN @StartDate AND @EndDate


 RETURN @NDAYS
end
GO
6

Functions in Azure DW don't support select statements that access tables like in your use case, see CREATE FUNCTION (SQL Data Warehouse):

function_body
Specifies that a series of Transact-SQL statements, which do not reference database data (tables or views), define the value of the function.

Could you double check that function is created in DW?

6

Azure SQL Data Warehouse has limited support for UDFs. It does not yet support the syntax SELECT @var =. Instead you must use DECLARE @var int = or SET @var =. SQL DW UDFs also do not yet support queries on user tables. Please use our feedback page to vote for new features.

  • 8
    "Our" feedback page! Do you work for Microsoft? It would be nice if you declared your affiliation, here or in your profile. – Michael Green May 23 '16 at 3:01
  • Thanks @sony. The documentation page was not specific about this and I started with migration of a warehouse for a client promising Azure warehouse will require minimum changes compared to something like redshift. If you are from Microsoft, I suggest changes to documentation to make these things explicit. – RK Kuppala May 23 '16 at 5:25
2

There is a Data Warehouse Migration utility (available here) for Azure SQL Data Warehouse which picks up issues like incompatible datatypes, in-line functions, use of hints, use of RETURN statement, INSERT ... EXEC and many others:

Example Database Compatibility report

Unfortunately it does not pick up scalar functions which reference tables and really it should. However, for your particular function, it could just be a view (or even another table), eg

CREATE VIEW dbo.vw_ImpliedRates
AS
SELECT 1 term, [1] impliedRate, Multiple
FROM  dbo.ImpliedRate 
UNION ALL
SELECT 2 term, [2], Multiple
FROM  dbo.ImpliedRate 
UNION ALL
SELECT 3 term, [3], Multiple
FROM  dbo.ImpliedRate 
UNION ALL
SELECT 4 term, [4], Multiple
FROM  dbo.ImpliedRate;
GO

CREATE TABLE dbo.test
(
    Multiple    FLOAT NOT NULL,
    Term        INT NOT NULL
);
GO

INSERT INTO dbo.test ( Multiple, Term )
VALUES
    ( 0.001, 1 ), ( 0.001, 2 ), ( 0.001, 3 ), ( 0.001, 4 );
GO


SELECT impliedRate, v.Multiple
FROM dbo.test t
    INNER JOIN dbo.vw_ImpliedRates v
        ON t.Multiple = v.Multiple
       AND t.Term = v.Term;

SELECT *
FROM dbo.vw_ImpliedRates
WHERE Multiple = 0.001
  AND Term = 2

I tried this on my Azure SQL Data Warehouse and it worked perfectly well.

You should also know that scalar functions in SQL Server do not scale well when called against tables, and if you have the volume appropriate for Azure SQL Data Warehouse (ie billions of rows), then you will need to re-think use of scalar functions anyway. For example, using CTAS and writing more procedural code is a good approach that will allow you to make proper use of this immensely powerful platform.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.