Question

I have recently run into this issue and wanted to know whether my understanding was correct. I am by DNA a backend developer (not DBA) but has regular interaction with SQL. I created two identical functions (from result data viewpoints) and I am putting them in order of creation scripts:

CREATE FUNCTION dbo.get_names_udf (@user_id nvarchar(24))
RETURNS NVARCHAR(500) 
AS
BEGIN
DECLARE
RETURN SELECT dbo.usernames(@user_id))
END
GO

CREATE FUNCTION dbo.get_names_itvf (@user_id nvarchar(24))
RETURNS TABLE 
AS
RETURN
(SELECT name = SELECT dbo.usernames(@user_id))
GO

-- dbo.usernames is a simple scalar function which is defined after both the functions above    

This is how I create all the functions for my DB:

  1. I drop all the functions from sysobjects

  2. I recreate all the functions again.

  3. "the usernames() function is created after both of the above".

When I try to execute all the function creation script - it seems that my iTVF creation fails because it reports that usernames() function isn't there (which is expected because the objects are dropped, as I mentioned above).

Msg 4121, Level 16, State 1, Server XXXXXXXXX, Procedure get_names_itvf, Line 5
Cannot find either column "dbo" or the user-defined function or aggregate "dbo.usernames", or the name is ambiguous.

But for the UDF function it doesn't run into this error.

Is this because how the inline TVF sees the internal results? Or is it how the dependencies are resolved for UDF and iTVF?

I am not sure why this is happening so it would be good to get some guidance/suggestion.

KR,

Was it helpful?

Solution

When you`re creating a scalar function you define the return type

CREATE FUNCTION dbo.get_names_udf (@user_id nvarchar(24))
RETURNS NVARCHAR(500) 
AS
BEGIN
RETURN (SELECT dbo.usernames(@user_id))
END
GO

Which was NVARCHAR(500) in this case and SQL server does not need to check the underlying structure of referenced functions/tables as long as knows the return type.

It will fail to execute tho, if usernames function (in this case) would return something other than NVARCHAR(500), which is expected

On the other hand

CREATE FUNCTION dbo.get_names_itvf (@user_id nvarchar(24))
RETURNS TABLE 
AS
RETURN
(SELECT dbo.usernames(@user_id) as username)
GO

As specified ,returns table therefore SQL server checks the underlying structure of the table because it needs to know what kind of table is begin returned. How many columns it has,if any of the columns are without names(in aggregate cases) and inform you about it. You can test this by running the query above without referencing the column name( 'as user name' ) in case username table does not have a specified column name.

Note: I had to tweak this two functions a little bit, to make them work.

Hope this clarifies what is going under the hood for you

OTHER TIPS

If this is a problem, you could write your script to create dummy or place-holder versions of some of your objects, such as procedures without any working parts inside. Then, at a later stage in the process, replace the dummy object with the working one.

If I expect to take several attempts at defining an object to do what I want, in test or production environment, I may script it like this:

IF ( OBJECT_ID(N'MyProcedure') IS NULL ) EXEC (N'CREATE MyProcedure AS')
GO
ALTER PROCEDURE MyProcedure
          @parameters varchar(99)
AS
DECLARE
          @variable1  varchar(15)
...
-- procedure body
...
GO
EXEC MyProcedure 'test_parameter'

Noting S4V1N's advice, the scripting for your situation probably should specify the format of inputs and outputs of objects, the first time.

Licensed under: CC-BY-SA with attribution
Not affiliated with dba.stackexchange
scroll top