Does SQL Server deferred name resolution work for functions? - sql-server

SQL Server has Deferred Name Resolution feature, read here for details:
https://msdn.microsoft.com/en-us/library/ms190686(v=sql.105).aspx
In that page, all it's talking is stored procedure so it seems Deferred Name Resolution only works for stored procedures and not for functions and I did some testing.
create or alter function f2(#i int)
returns table
as
return (select fff from xxx)
go
Note the table xxx does not exist. When I execute the above CREATE statement, I got the following message:
Msg 208, Level 16, State 1, Procedure f2, Line 4 [Batch Start Line 22]
Invalid object name 'xxx'.
It seems that SQL Server instantly found the non-existent table xxx and it proved Deferred Name Resolution doesn't work for functions. However when I slightly change it as follows:
create or alter function f1(#i int)
returns int
as
begin
declare #x int;
select #x = fff from xxx;
return #x
end
go
I can successfully execute it:
Commands completed successfully.
When executing the following statement:
select dbo.f1(3)
I got this error:
Msg 208, Level 16, State 1, Line 34
Invalid object name 'xxx'.
So here it seems the resolution of the table xxx was deferred. The most important differences between these two cases is the return type. However I can't explain when Deferred Name Resolution will work for functions and when not. Can anyone help me to understand this? Thanks in advance.

It feels like you were looking for understanding of why your particular example didn't work. Quassnoi's answer was correct but didn't offer a reason so I went searching and found this MSDN Social answer by Erland Sommarskog. The interesting part:
However, it does not extend to views and inline-table functions. For
stored procedures and scalar functions, all SQL Server stores in the
database is the text of the module. But for views and inline-table
functions (which are parameterised view by another name) SQL Server
stores metadata about the columns etc. And that is not possible if the
table is missing.
Hope that helps with understanding why :-)
EDIT:
I did take some time to confirm Quassnoi's comment that sys.columns as well as several other tables did contain some metadata about the inline function so I am unsure if there is other metadata not written. However I thought I would add a few other notes I was able to find that may help explain in conjunction.
First a quote from Wayne Sheffield's blog:
In the MTVF, you see only an operation called “Table Valued Function”. Everything that it is doing is essentially a black box – something is happening, and data gets returned. For MTVFs, SQL can’t “see” what it is that the MTVF is doing since it is being run in a separate context. What this means is that SQL has to run the MTVF as it is written, without being able to make any optimizations in the query plan to optimize it.
Then from the SQL Server 2016 Exam 70-761 by Itzik Ben-Gan (Skill 3.1):
The reason that it's called an inline function is because SQL Server inlines, or expands, the inner query definition, and constructs an internal query directly against the underlying tables.
So it seems the inline function essentially returns a query and is able to optimize it with the outer query, not allowing the black-box approach and thus not allowing deferred name resolution.

What you have in your first example is an inline function (it does not have BEGIN/END).
Inline functions can only be table-valued.
If you used a multi-statement table-valued function for you first example, like this:
CREATE OR ALTER FUNCTION
fn_test(#a INT)
RETURNS #ret TABLE
(
a INT
)
AS
BEGIN
INSERT
INTO #ret
SELECT a
FROM xxx
RETURN
END
, it would compile alright and fail at runtime (if xxx would not exist), same as a stored procedure or a scalar UDF would.
So yes, DNR does work for all multi-statement functions (those with BEGIN/END), regardless of their return type.

Related

Function returning a results set and an integer in SQL Server

I'm facing a quite annoying barrier enforced by SQL Server and would like to check if there is an elegant solution for this.
I have a sequence of procedures' invocations (meaning, A calls B which calls C). The procedures are due to return different results sets, where (for instance) "A" generates its result using a set of records returned by "B".
Now, SQL Server does not allow to have nested INSERT INTO ... EXEC <stored procedure> so, to cope with this limitation, I converted the lowest procedure into a function that returns a table and hence INSERT INTO ... SELECT * FROM <function call>.
Now, there are situations in which the FUNCTION cannot return a result due to conditions of the data, and I would like the function to return a sort of code indicating the result of the execution (e.g. 0 would mean success, 1 would mean "missing input data").
Since SQL Server does not allow functions with OUTPUT parameters, I can't think of any elegant way of conveying these two outputs.
Can anyone suggest an elegant alternative?
there are situations in which the FUNCTION cannot return a result due
to conditions of the data, and I would like the function to return a
sort of code indicating the result of the execution
You really should use THROW to indicate the result of execution, which also precludes using a table-valued function.
So you need to use a stored procedure. To avoid the restriction on nested INSERT .. SELECT you can use temporary tables to pass data back to the calling procedure. EG
create or alter procedure foo
as
begin
if object_id('tempdb..#foo_results') is null
begin
print 'create table #foo_results(id int primary key, a int);';
THROW 51000, 'The results table #foo_results does not exist. Before calling this procedure create it. ', 1;
end
insert into #foo_results(id,a)
values (1,1);
end;
Can anyone suggest an ELEGANT alternative?
I'm not sure any of the alternatives is elegant.

Create wrapper with unknown data types

I'm trying to create a wrapper in T-SQL for a procedure where I'm not sure what the data types are. I can run the wrapper without an INSERT INTO statement and I get the data just fine, but I need to have it in a table.
Whenever I use the INSERT INTO I get an error:
Column name or number of supplied values does not match table definition
I've parsed back through my code and can't see where any column names don't match up, so I'm thinking that it has to be a data type. I've looked through the procedure I'm wrapping to see if I can find what the data types are, but some aren't defined there; I've referenced the tables they pull some data from to find the definitions; I've run SQL_VARIANT_PROPERTY on all of the data to see what data type it is (although some of them come up null).
Is there some better way for me to track down exactly where the error is?
I think you can find out your stored procedure result schema, using sp_describe_first_result_set (available from SQL2012) and FMTONLY. Something like this:
EXEC sp_describe_first_result_set
#tsql = N'SET FMTONLY OFF; EXEC yourProcedure <params are embedded here>'
More details can be found here.
However, if I remember correctly, this works only if your procedure used deterministic schemas (no SELECT INTO #tempTable or similar things).
One trick to find out the schema of your result is to actually materialize the result into ad-hoc created table. However, this is not easy since SELECT INTO does not work with EXEC procedure. One work-around is this:
1) Define a linked-server to the instance itself. E.g. loopback
2) Execute your procedure like this (for SQL 2008R2):
SELECT * INTO tempTableToHoldDataAndStructure
FROM OPENQUERY(' + #LoopBackServerName + ', ''set fmtonly off exec ' + #ProcedureFullName + ' ' + #ParamsStr
where
#LoopBackServerName = 'loopback'
#ProcedureFullName = loopback.database.schema.procedure_name
#ParamsStr = embedded parameters
For SQL2012 I think the execution might fail if RESULT SETS are not provided (i.e. schema definition of the expected result, which is kind of a chicken-egg problem in this case):
' WITH RESULT SETS (( ' + #ResultSetStr + '))'');
Okay, I have a solution to my problem. It's tedious, but tedious I can do. Randomly guessing is what drives me crazy. The procedure I'm wrapping dumps 51 columns. I already know I can get it to work without putting anything into a table. So I decided to comment out part of the select statement in the procedure I'm wrapping so it's only selecting 1 column. (First I made a copy of that procedure so I don't screw up the original; then I referenced the copy from my wrapper). Saved both, ran it, and it worked. So far so good. I could have done it line by line, but I'm more of a binary kind of guy, so I went about halfway down--now I'm including about 25 columns in both the select statement and my table--and it's still working. Repeat procedure until it doesn't work any more, then backtrack until it does again. My error was in identifying one of the data types followed by "IDENTITY". I'm not sure what will happen when I leave that out, but at least my wrapper works.

Create SQL user-defined function in ColdFusion with MS SQL Server

I'm doing queries in which I want to extract the left-most n characters from a string that has been stripped of all leading and following spaces. An example is:
Select SUBSTRING(LTRIM(RTRIM(somefield)), 0, #n) AS mydata
FROM sometable
It's the only way I can figure to do it on a SQL Server.
I've never written a UDF before, but I think if I was just working on a SQL Server, I could create a user-defined function such as:
CREATE FUNCTION udfLeftTrimmed
(
#inputString nvarchar(50),
#n int
)
RETURNS nvarchar(#n)
AS
BEGIN
RETURN SUBSTRING(LTRIM(RTRIM(#inputString)), 0, #n);
END
I could then do something like:
Select udfLeftTrimmed(somefield,6) AS mydata
FROM sometable
which is at least a little easier to read and understand.
The question is, how do I create the UDF in ColdFusion? All my searches for SQL user-defined function in ColdFusion just gave me how to create ColdFusion functions.
Since there is nothing special or "dynamic" about your UDF you really don't need to create it in CF. You should just create it using MSSQL Manager. UDFs in SQL are like stored procedures. Once created they are a part of the DB/Schema. so create once, use as many times as you like (as #leigh has mentioned).
Keep in mind that using a SQL udf in SQL usually requires the user prepend as in:
<cfquery...>
Select dbo.udfLeftTrimmed(somefield,6) AS mydata
FROM sometable
</cfquery>
Note the "dbo.udf..." that dbo is important and may be why your subsequent try is failing - besides getting a duplicate UDF error by now. :)
NOTE:
To follow up on your comments and Leighs, you can create your UDF in a DB accessible to your user then access it as dbo.dbname.function ... as inthe following code:
<cfquery...>
Select dbo.myspecialDatabase.udfLeftTrimmed(somefield,6) AS mydata
FROM sometable
</cfquery>
Then you need only create it one time.

Why are table valued parameters to SQL Server stored procedures required to be input READONLY?

Can anyone explain the design decision behind preventing table valued parameters from being specified as output parameters to stored procedures?
I can't count the number of times I've started building out a data model hoping to completely lock down my tables to external access (you know...implementation details), grant applications access to the database through stored procedures only (you know... the data interface) and communicate back and forth with TVPs only to have SSMS call me naughty for having the audacity to think that I can use a user-defined table type as the transfer object between my data service and my application.
So someone please provide me a good reason why TVPs were designed to be readonly input parameters.
In the presentation on Optimizing Microsoft SQL Server 2008 Applications Using Table Valued Parameters, XML, and MERGE by Michael Rys he says. (at 32:52)
Note that in SQL Server 2008 table valued parameters are read only.
But as you notice we actually require you to write READONLY. So that
actually then means that at some point in the future maybe if you say
please, please please often enough we might be able to actually make
them writable as well at some point. But at the moment they are read
only.
Here is the connect item you should use to add your "please". Relax restriction that table parameters must be readonly when SPs call each other.
Srini Acharya made a comment on the connect item.
Allowing table valued parameters to be read/write involves quite a bit
of work on the SQL Engine side as well as client protocols. Due to
time/resource constraints as well as other priorirites, we will not be
able to take up this work as part of SQL Server 2008 release. However,
we have investigated this issue and have this firmly in our radar to
address as part of the next release of SQL Server.
Table-valued parameters have the following restrictions(source MSDN):
SQL Server does not maintain statistics on columns of table-valued
parameters.
Table-valued parameters must be passed as input READONLY
parameters to Transact-SQL routines. You cannot perform DML
operations such as UPDATE, DELETE, or INSERT on a table-valued
parameter in the body of a routine.
You cannot use a table-valued parameter as target of a SELECT INTO
or INSERT EXEC statement. A table-valued parameter can be in the
FROM clause of SELECT INTO or in the INSERT EXEC string or stored
procedure.
there are few options to over come this restriction one is
CREATE TYPE RTableType AS TABLE(id INT, NAME VARCHAR )
go
CREATE PROCEDURE Rproc #Rtable RTABLETYPE READONLY,
#id INT
AS
BEGIN
SELECT *
FROM #Rtable
WHERE ID = #id
END
go
DECLARE #Rtable RTABLETYPE
DECLARE #Otable RTABLETYPE
INSERT INTO #Rtable
VALUES (1,'a'),
(2,'b')
INSERT #Otable
EXEC Rproc
#Rtable,
2
SELECT *
FROM #Otable
through this you can get the table values out
With respect to (emphasis added):
So someone please provide me a good reason why TVPs were designed to be readonly input parameters.
I just posted a more detailed answer to this on DBA.StackExchange here:
READONLY parameters and TVP restrictions
But the summary of it goes like this:
According to this blog post ( TSQL Basics II - Parameter Passing Semantics ), a design goal of Stored Procedure OUTPUT parameters is that they merely mimic "by reference" behavior when the Stored Procedure completes successfully! But when there is an error that causes the Stored Procedure to abort, then any changes made to any OUTPUT parameters would not be reflected in the current value of those variables upon control returning to the calling process.
But when TVPs were introduced, they implemented them as truly passing by reference since continuing the "by value" model -- in which a copy of it is made to ensure that changes are lost if the Stored Procedure does not complete successfully -- would not be efficient / scalable, especially if a lot of data is being passed in through TVP.
So there is only one instance of the Table Variable that is the TVP, and any changes made to it within any Stored Procedure (if they were not restricted to being READONLY) would be immediately persisted and would remain, even if the Stored Procedure encountered an error. This violates the design goal stated at the beginning of this summary. And, there is no option for somehow tying changes made to a TVP to a transaction (even something handled automatically, behind the scenes) since table variables are not bound by transactions.
Hence, marking them as READONLY is the only way (at the moment) to maintain the design goal of Stored Procedure parameters such that they do not reflect changes made within the Stored Procedure unless: the parameter is declared as OUTPUT and the Stored Procedure complete successfully.
Be Forewarned. This code will not work. That is the problem
Note that all code was entered directly into post from memory. I may have a type wrong in the example or some similar error. It is just to demonstrate the technique that this would facilitate, which won't work with any version of SQL Server released at the time of this writing. So it doesn't really matter if it currently compiles or not.
I know this question is old by now, but perhaps someone coming across my post here might benefit from understanding why it's a big deal that TVPs can't be directly manipulated by a stored proc and read as output parameters by the calling client.
"How do you..." questions regarding OUTPUT TVPs have littered SQL Server forums for more than half a decade now. Nearly every one of them involves someone attempting some supposed workaround that completely misses the point of the question in the first place.
It is entirely non sequitur that you can "get a result set that matches a table type" by creating a Table Typed variable, Inserting into it and then returning a read from it. When you do that, the result set is still not a message. It is an ad hoc ResultSet that contains arbitrary columns that "just happen to match" a UDTT. What is needed is the ability for the following:
create database [Test]
create schema [Request]
create schema [Response]
create schema [Resources]
create schema [Services]
create schema [Metadata]
create table [Resources].[Foo] ( [Value] [varchar](max) NOT NULL, [CreatedBy] [varchar](max) NOT NULL) ON [PRIMARY]
insert into [Resources].[Foo] values("Bar", "kalanbates");
create type [Request].[Message] AS TABLE([Value] [varchar](max) NOT NULL)
create type [Response].[Message] AS TABLE([Resource] [varchar](max) NOT NULL, [Creator] [varchar](max) NOT NULL, [LastAccessedOn] [datetime] NOT NULL)
create PROCEDURE [Services].[GetResources]
(#request [Request].[Message] READONLY, #response [response].[Message] OUTPUT)
AS
insert into #response
select [Resource].[Value] [Resource]
,[Resource].[CreatedBy] [Creator]
,GETDATE() [LastAccessedOn]
inner join #request as [Request] on [Resource].[Value] = [Request].[Value]
GO
and have an ADO.NET client be able to say:
public IEnumerable<Resource> GetResources(IEnumerable<string> request)
{
using(SqlConnection connection = new SqlConnection("Server=blahdeblah;database=Test;notGoingToFillOutRestOfConnString")
{
connection.Open();
using(SqlCommand command = connection.CreateCommand())
{
command.CommandText = "[Services].[GetResources]"
command.CommandType = CommandType.StoredProcedure;
SqlParameter _request;
_request = command.Parameters.Add(new SqlParameter("#request","[request].[Message]");
_request.Value = CreateRequest(request,_request.TypeName);
_request.SqlDbType = SqlDbType.Structured;
SqlParameter response = new SqlParameter("#response", "[response].[Message]"){Direction = ParameterDirection.Output};
command.Parameters.Add(response);
command.ExecuteNonQuery();
return Materializer.Create<List<ResourceEntity>>(response).AsEnumerable(); //or something to that effect.
//The point is, messages are sent to and received from the database.
//The "result set" contained within response is not dynamic. It has a structure that can be *reliably* anticipated.
}
}
}
private static IEnumerable<SqlDataRecord> CreateRequest(IEnumerable<string> values, string typeName)
{
//Optimally,
//1)Call database stored procedure that executes a select against the information_schema to retrieve type metadata for "typeName", or something similar
//2)Build out SqlDataRecord from returned MetaData
//Suboptimally, hard code "[request].[Message]" metadata into a SqlMetaData collection
//for example purposes.
SqlMetaData[] metaData = new SqlMetaData[1];
metaData[0] = new SqlMetaData("Value", SqlDbType.Varchar);
SqlDataRecord record = new SqlDataRecord(metaData);
foreach(string value in values)
{
record.SetString(0,value);
yield return record;
}
}
The point here is that with this structure, the Database defines [Response].[Message],[Request].[Message], and [Services].[GetResource] as its Service Interface. Calling clients interact with "GetResource" by sending a pre-determined message type and receive their response in a pre-determined message type. Of course it can be approximated with an XML output parameter, you can somewhat infer a pre-determined message type by instituting tribal requirements that retrieval stored procedures must insert its response into a local [Response].[Message] Table Typed variable and then select directly out of it to return its results. But none of those techniques are nearly as elegant as a structure where a stored procedure fills a response "envelope" provided by the client with its payload and sends it back.
Still in 2020, SQL version "Microsoft SQL Azure (RTM) - 12.0.2000.8", I am not able to edit the Table value parameter within the Stored Procedure. So I did the work around by moving the data into Temp table and edited it.
ALTER PROCEDURE [dbo].[SP_APPLY_CHANGESET_MDTST09_MSG_LANG]
#CHANGESET AS [dbo].[MDTSTYPE09_MSG_LANG] READONLY
AS
BEGIN
SELECT * INTO #TCHANGESET FROM #CHANGESET
UPDATE #TCHANGESET SET DTST08_MSG_K = 0 WHERE ....
...............

Issue with parameters in SQL Server stored procedures

I remember reading a while back that randomly SQL Server can slow down and / or take a stupidly long time to execute a stored procedure when it is written like:
CREATE PROCEDURE spMyExampleProc
(
#myParameterINT
)
AS
BEGIN
SELECT something FROM myTable WHERE myColumn = #myParameter
END
The way to fix this error is to do this:
CREATE PROCEDURE spMyExampleProc
(
#myParameterINT
)
AS
BEGIN
DECLARE #newParameter INT
SET #newParameter = #myParameter
SELECT something FROM myTable WHERE myColumn = #newParameter
END
Now my question is firstly is it bad practice to follow the second example for all my stored procedures? This seems like a bug that could be easily prevented with little work, but would there be any drawbacks to doing this and if so why?
When I read about this the problem was that the same proc would take varying times to execute depending on the value in the parameter, if anyone can tell me what this problem is called / why it occurs I would be really grateful, I cant seem to find the link to the post anywhere and it seems like a problem that could occur for our company.
The problem is "parameter sniffing" (SO Search)
The pattern with #newParameter is called "parameter masking" (also SO Search)
You could always use the this masking pattern but it isn't always needed. For example, a simple select by unique key, with no child tables or other filters should behave as expected every time.
Since SQL Server 2008, you can also use the OPTIMISE FOR UNKNOWN (SO). Also see Alternative to using local variables in a where clause and Experience with when to use OPTIMIZE FOR UNKNOWN

Resources