sql user defined function - sql-server

for a table valued function in sql why cant we write sql statements inside begin and end tags like-
create function dbo.emptable()
returns Table
as
BEGIN --it throws an error
return (select id, name, salary from employee)
END
go
while in scalar valued function we can use these tags like
create function dbo.countemp()
returns int
as
begin
return (select count(*) from employee)
end
go
is there any specific rule where we should use BEGIN & END tags

The multi-statement table-valued
function is slightly more complicated
than the other two types of functions
because it uses multiple statements to
build the table that is returned to
the calling statement. Unlike the
inline table-valued function, a table
variable must be explicitly declared
and defined. The following example
shows how to implement a
multi-statement table-valued function
that populates and returns a table
variable.
USE Northwind
go
CREATE FUNCTION fx_OrdersByDateRangeAndCount
( #OrderDateStart smalldatetime,
#OrderDateEnd smalldatetime,
#OrderCount smallint )
RETURNS #OrdersByDateRange TABLE
( CustomerID nchar(5),
CompanyName nvarchar(40),
OrderCount smallint,
Ranking char(1) )
AS
BEGIN
// statements that does some processing ....
END
From the above, I guess BEGIN and END denotes the intent/use of multiple statements & hence it requires the table variable to be defined as shown in the code above.
from http://www.sqlteam.com/article/intro-to-user-defined-functions-updated

In an INLINE TVF (like your first example), the BEGIN and END would try to force it to be procedural code, and therefore it would no longer be an Inline TVF. Scalar functions are only available in a procedural form (which is lousy). Therefore, Scalar functions should generally be avoided in the current versions of SQL Server.

Related

SQL Server matching input and output types of a tabled value function with table valued parameters

I'm new to SQL, and I wanted to create functions that do some sort of things on tables.
After a lot of searching, I found out about table valued functions and table valued parameters.
Here's my example:
CREATE TYPE MyType AS TABLE(ID int);
CREATE FUNCTION dbo.MyFunc1(#Input MyType READONLY)
RETURNS #Result TABLE(ID int)
BEGIN
INSERT INTO #Result
VALUES(1); --Just a simple example
RETURN;
END;
Everything goes fine with this example, but what if I want to use output of this function for input of another function (such as MyFunc2) and do some other things on it?
Surely I can declare a temporary MyType, insert values from MyFunc1 into it and call MyFunc2 with that temporary variable as input, but what I'm looking for is some sort of pipelining (like streams api in java 8) and I want to call chain of functions, without boilerplate codes between, such as
SELECT *
FROM (MyFunc1(MyFunc2(MyFunc3(#FirstInput, #SecondInput))));
How is this possible?

Getting Error Msg: Must declare the scalar variable

I used this code in SQL Server:
CREATE TYPE ExampleType AS TABLE (Number INT)
GO
CREATE FUNCTION GetExampleTableType(#InputNumber INT)
RETURNS ExampleType
AS
BEGIN
DECLARE #OutputTable ExampleType;
INSERT INTO OutputTable
VALUES (#InputNumber);
RETURN(#OutputTable);
END;
GO
But I got an error:
Must declare the scalar variable "#OutputTable"
I have declared #OutputTable but it cannot be a scalar value, it must be a table.
What is wrong?
You are trying to return table variable from scalar valued User defined function. You need to convert the function as given below to store the values into the table valued parameter.
Also, read this StackOverFlow Post, where you cannot return UDT from table valued function.
CREATE TYPE ExampleType AS TABLE (Number int)
GO
CREATE Function GetExampleTableType(#InputNumber int)
RETURNS Table
AS
RETURN
(SELECT #InputNumber AS int);
DECLARE #OutputTable ExampleType
INSERT INTO #OutputTable
SELECT * FROM dbo.GetExampleTableType (1);
Actually you don't need to create a user-defined table type. You can directly give table instead of that.
Query
CREATE Function GetExampleTableType(#InputNumber int)
RETURNS #OutputTable TABLE
(
Number int
)
AS
BEGIN
INSERT INTO #OutputTable VALUES (#InputNumber);
RETURN;
END;
As per my understanding we can use tabletype variables to pass as arguments to functions and stored procedures but not used in multistatemnet table valued functions like you mentioned in the question. But in your example you try to access tabletype variable in returns clause which is syntactically incorrect.
This is what i have as sample code here to use tabletype variables in functions context.. please let me know if any thing i missed to understand your question...
CREATE TYPE TableType
AS TABLE (LocationName VARCHAR(50))
GO
CREATE FUNCTION Example( #TableName TableType READONLY)
RETURNS VARCHAR(50)
AS
BEGIN
DECLARE #name VARCHAR(50)
SELECT #name = LocationName FROM #TableName
RETURN #name
END
DECLARE #myTable TableType
INSERT INTO #myTable(LocationName) VALUES('aaa')
SELECT dbo.Example(#myTable)
Table-valued parameters have the following restrictions:
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.
Reference: https://learn.microsoft.com/en-us/sql/relational-databases/tables/use-table-valued-parameters-database-engine?view=sql-server-2017

Better way to use SQL Server function?

Hey guys I want to use SQL Server function,
I never use function in SQL Server and I only know that function must return a value so I have two stored procedure (1 for insert and 1 for select)
My stored procedures look like this
For insert:
create proc INS_tblteststud
#stdtid int=null,
#name varchar(50) =null,
#fullname varchar(50)=null,
#address varchar(50)=null,
#city varchar(50)=null,
#country varchar(50)=null,
#contno varchar(50)=null
as
begin
insert into tbl_student_test(name,fullname,address,city,country,contno)
values
(#name,#fullname,#address,#city,#country,#contno)
end
And for select:
Create proc SEL_tblteststud
as
begin
select * from tbl_student_test
end
Now I want to know, how can I convert these statements(Insert, Select) into functions? And which is better to use stored procedure or function?
You are mixing procedure and function...
A procedure is meant for doing something
A function is meant for reading only
It is not allowed to place data changing commands within a function.
What you are trying is just not possible...
UPDATE Some insight about functions and procedures
There are three types of functions
scalar functions: Return one scalar value and are very bad performing
multi statement table valued functions (with BEGIN ... END): Return a resultset, but are bad performing too
inline table valued functions: They are great, but restricted. Think of them like of a VIEW with pre-compiled parameters
A procedure has a return value too, which is of tpye INT and is not used to return the SP's result, but kind of execution status.
A SP might just do something and return no procedure result at all. If you want to return procedural results you must either use
output parameters
or you must call a SELECT within your SP. It is possible to grab the result of an internal SELECT from outside, but this is very cumbersome...

Creating a stored function in SQL Server to generate id from sequence

I have a created sequence in SQL Server like this:
CREATE SEQUENCE dbo.cust_acc_seq
START WITH 1771
INCREMENT BY 1 ;
GO
I'm trying to create a stored function to return next value from the sequence, below is the code for the same.
CREATE FUNCTION dbo.GetSeq_Cust_Acc()
RETURNS VARCHAR(250)
AS BEGIN
DECLARE #Work VARCHAR(250)
SET #Work = NEXT VALUE FOR dbo.cust_add_seq
RETURN #work
END
But while executing this I'm getting this error:
NEXT VALUE FOR function is not allowed in check constraints, default objects, computed columns, views, user-defined functions, user-defined aggregates, user-defined table types, sub-queries, common table expressions, or derived tables.
Please let me know if I'm missing something while defining the stored function.

Preserve PostgreSQL schema identifier in dependent function calls

I have a stored procedure that RETURNS record:
CREATE OR REPLACE FUNCTION r_rpt_prov_summary()
RETURNS record
AS $$
DECLARE
_fields record;
BEGIN
SELECT INTO _fields
0::integer AS customers,
0::integer AS customers_active;
SELECT INTO _fields.customers count(*) FROM customer;
SELECT INTO _fields.customers_active count(*) FROM customer WHERE active = 't';
RETURN _fields;
END
$$ LANGUAGE 'plpgsql';
However, in order to query it, I would have to explicitly enumerate the returned columns and types:
SELECT * FROM r_rpt_prov_summary() AS (a integer, b integer);
In order to make this palatable to an MVC framework, which by its nature wants to query tables, I wrap it in an SQL function that RETURNS TABLE:
CREATE OR REPLACE FUNCTION rpt_prov_summary()
RETURNS TABLE (
customers integer,
customers_active integer
) AS $$
SELECT * FROM r_rpt_prov_summary() AS (customers integer, customers_active integer);
$$ LANGUAGE 'sql';
This works very well as long as both functions reside in the same schema or search_path space. However, in this case, they live in a nonstandard schema of their own, so I have to query the outer function as myschema.rpt_prov_summary(), i.e.
SELECT * FROM myschema.rpt_prov_summary();
This doesn't work if my schema and search path are set to public:
test=> SELECT * FROM myschema.rpt_prov_summary();
ERROR: function r_rpt_prov_summary() does not exist
LINE 2: SELECT * FROM r_rpt_prov_summary() AS (customers integer, ...
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
Naturally, your thoughts would turn to calling SET SCHEMA 'myschema' or SET search_path TO myschema prior to query execution. So did mine. The problem is it's just not going to work for the environment from which I'm calling. The MVC framework is using prepared statements to construct the query, and PostgreSQL frowns upon something like:
SET search_path TO myschema;SELECT * FROM rpt_prov_summary();
Which is to say:
< 2016-03-14 20:50:46.410 EDT >ERROR: cannot insert multiple commands into a prepared statement
So, that's not going to work. Providing the schema as an argument to the outer function isn't going to work, either; I just don't have that flexibility within the constraints of the MVC framework, which wants to be querying plain old tables or things that act like tables.
current_schema identifies the current schema of the client session, so that's not going to help. Already tried this:
CREATE OR REPLACE FUNCTION rpt_prov_summary()
RETURNS TABLE (
customers integer,
customers_active integer
) AS $$
BEGIN
RAISE NOTICE 'Current schema: %', current_schema;
RETURN QUERY EXECUTE 'SELECT * FROM ' || current_schema || '.r_rpt_prov_summary() AS (customers integer, customers_active integer)';
END
$$ LANGUAGE 'plpgsql';
No dice - Current schema: public, as expected.
Is there any way to somehow capture the schema space of the outer call and propagate that into the encapsulated query?
1.
I have a stored procedure ...
No, you don't. You have a function, which is almost but not quite the same. Postgres doesn't currently support stored procedures.
2.
Radically simplify. Instead of your two nested functions, use one simple SQL function with OUT parameters:
CREATE OR REPLACE FUNCTION myschema.r_rpt_prov_summary( -- schema-qualify!
OUT _customers integer
, OUT _customers_active integer) AS
$func$
SELECT count(*)::int
, count(*) FILTER (WHERE active)::int -- requires Postgres 9.4+
FROM myschema.customer; -- schema-qualify!
$func$ LANGUAGE sql; -- don't quote the language name
Call:
SELECT * FROM myschema.rpt_prov_summary();
Works independently of your current search_path setting.
Note a subtle difference between RETURNS TABLE() and RETURNS record (my version is also RETURNS record, but I didn't specify explicitly after declaring OUT parameters!): The later always returns exactly 1 row (and Postgres knows that and can rely on it), while the former can return 0 - n rows.
3.
You could set the search_path in many different ways, even as local setting to the function itself - if necessary:
How does the search_path influence identifier resolution and the "current schema"
To answer your actual question:
Is there any way to somehow capture the schema space of the outer call
and propagate that into the encapsulated query?
CREATE OR REPLACE FUNCTION myschema.r_rpt_prov_summary(OUT _customers integer
, OUT _customers_active integer) AS
$func$
BEGIN
SELECT INTO _customers, _customers_active
count(*)::int, count(*) FILTER (WHERE active)::int
FROM customer; -- no schema-qualification
END
$func$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION myschema.rpt_prov_summary()
RETURNS TABLE (customers int, customers_active int) AS
$func$
SELECT * FROM myschema.r_rpt_prov_summary();
$func$ LANGUAGE sql SET search_path = myschema; -- set the search_path here
The search_path is propagated to the nested function - just what you were looking for.
Or just schema-qualify identifiers everywhere (including function names) to be unambiguous.

Resources