Returning a table from a function with parameters in SQL - sql

As far as I know, we can return a table as a result of a db function:
CREATE FUNCTION MyFunction(#Value varchar(100))
RETURNS table
AS RETURN (select * from MyTable where ColumnName = '#Value')
in this example we can make the column name as a parameter for the function. My question is, can we write the column name and table name as a parameter for the function? hence we can write a more generic function something like:
CREATE FUNCTION MyGenericSearchFunction(#TableName varchar(100), #ColumnName varchar(100), #Value varchar(100))
RETURNS table
AS RETURN (select * from #TableName where #ColumnName = '#Value')

No, you can't.
This would then be a dynamic query.
For dynamic queries in SQL Server, one has to use exec() or sp_executesql() functions, which are not allowed in functions.

Related

SQL query help - declaration of variables within a function

I'm trying to write a SQL function but an having problems with declaring the variables I need for use in the WHERE clause.
Here's the code:
CREATE FUNCTION fn_getEmployeePolicies(#employeeid smallint)
RETURNS TABLE
AS
DECLARE #empLoc varchar
DECLARE #empBusA varchar
DECLARE #empType varchar
#empLoc = SELECT Location FROM fn_getEmployeeDetails(#employeeid)
#empBusA = SELECT BusinessArea FROM fn_getEmployeeDetails(#employeeid)
#empType = SELECT Type FROM fn_getEmployeeDetails(#employeeid)
RETURN select PolicyId, PolicyGroupBusinessArea.BusinessArea, policysignoff.PolicyGroupLocation.Location, policysignoff.PolicyGroupEmployeeType.EmployeeType
from policysignoff.PolicyGroupPolicy
LEFT JOIN policysignoff.PolicyGroupBusinessArea on policysignoff.PolicyGroupBusinessArea.PolicyGroupId=policysignoff.PolicyGroupPolicy.PolicyGroupId
LEFT JOIN policysignoff.PolicyGroupLocation on policysignoff.PolicyGroupLocation.PolicyGroupId=policysignoff.PolicyGroupPolicy.PolicyGroupId
LEFT JOIN policysignoff.PolicyGroupEmployeeType on policysignoff.PolicyGroupEmployeeType.PolicyGroupId=policysignoff.PolicyGroupPolicy.PolicyGroupId
where BusinessArea = #empBusA
AND EmployeeType = #empType
AND Location = #empLoc
GO
The logic I am trying to build in is:
'given an employeeId, return all "applicable" policies'
An "Applicable" policy is one where the Business Area, Location and EmployeeType match that of the user.
I am trying to use another function (fn_getEmployeeDetails) to return the BusArea, Loc & EmpType for the given user.
Then with the results of that (stored as variables) I can run my select statement to return the policies.
The problem i am having is trying to get the variables declared correctly within the function.
Any help or tips would be appreciated.
Thanks in advance!
Without knowing what your error actually is, I can only say that you're properly not after using varchar as datatype without specifying length.
DECLARE #empLoc varchar will declare a varchar with length 1.
Chances are it should be something like varchar(255) or similar.
Second to set variables you'll either need to use SET and use paranthisis for selects or set it into the statement:
SET #empLoc = (SELECT Location FROM fn_getEmployeeDetails(#employeeid))
or
SELECT #empLoc = Location FROM fn_getEmployeeDetails(#employeeid)
There are subtle differences between these two methods, but for your purpose right now I don't think it's important.
EDIT:
Based on your comment you lack a BEGIN after AS, and an END before GO.
Basically - your function syntax is mixing up "inline" table function with "multi-statement" function.
Such a function "template" should look something like this:
CREATE FUNCTION <Table_Function_Name, sysname, FunctionName>
(
-- Add the parameters for the function here
<#param1, sysname, #p1> <data_type_for_param1, , int>,
<#param2, sysname, #p2> <data_type_for_param2, , char>
)
RETURNS
<#Table_Variable_Name, sysname, #Table_Var> TABLE
(
-- Add the column definitions for the TABLE variable here
<Column_1, sysname, c1> <Data_Type_For_Column1, , int>,
<Column_2, sysname, c2> <Data_Type_For_Column2, , int>
)
AS
BEGIN
-- Fill the table variable with the rows for your result set
RETURN
END
GO
(script taken from sql server management studio)

Use LIKE for strings in sql server 2008

I want to make a function that search in a table and returns rows that contain a certain word that I Insert like below. But when I use LIKE it give me an error: Incorrect syntax near '#perberesi'
CREATE FUNCTION perberesit7
(#perberesi varchar(100))
RETURNS #menu_rest TABLE
(emri_hotelit varchar(50),
emri_menuse varchar(50),
perberesit varchar(255))
AS
Begin
insert into #menu_rest
Select dbo.RESTORANTET.Emri_Rest, dbo.MENU.Emri_Pjatës, dbo.MENU.Pershkrimi
From RESTORANTET, MENU
Where dbo.MENU.Rest_ID=dbo.RESTORANTET.ID_Rest and
dbo.MENU.Pershkrimi LIKE %#perberesi%
return
End
Pleae help me...How can I use LIKE in this case
try using:
'%' + #perberesi + '%'
instead of:
%#perberesi%
Some Examples
Ok, I just realized that you are creating a function, which means that you can't use INSERT. You should also really take Gordon's advice and use explicit joins and table aliases.
CREATE FUNCTION perberesit7(#perberesi varchar(100))
RETURNS #menu_rest TABLE ( emri_hotelit varchar(50),
emri_menuse varchar(50),
perberesit varchar(255))
AS
Begin
return(
Select R.Emri_Rest, M.Emri_Pjatës, M.Pershkrimi
From RESTORANTET R
INNER JOIN MENU M
ON M.Rest_ID = R.ID_Rest
Where M.Pershkrimi LIKE '%' + #perberesi + '%')
End
Why do you have to define the return table?
The following is a inline table variable function that performs better than a multi-line table. I wrote one to return columns that have the two letters 'id'. Just modify for your own case.
See article from Wayne Sheffield.
http://blog.waynesheffield.com/wayne/archive/2012/02/comparing-inline-and-multistatement-table-valued-functions/
-- Use tempdb
use tempdb;
go
-- Simple ITVF
create function search_columns (#name varchar(128))
returns TABLE
return
(
select * from sys.columns where name like '%' + #name + '%'
)
go
-- Call the function
select * from search_columns('id');
go
However, since you have a '%' in the like clause at the front of the expression, a full table or index scan is likely. You might want to look at full text indexing if you data is large.
http://craftydba.com/?p=1629

Multiple values in a single parameter of a scalar function

Is there a way to input multiple values in a single parameter of a scalar-valued function in SQL Server 2008 R2 and have it filter data by that parameter using both values?
For example I would like to do the following
SET #Salesperson='BILL' OR 'MOSES'
SELECT Sum(SalesDollars)
FROM Invoices
WHERE Invoices.Salesperson = #Salesperson
I attempted to use the following as the WHERE clause, but this didnt work either.
SET #Salesperson='BILL','MOSES'
SELECT Sum(SalesDollars)
FROM Invoices
WHERE Invoices.Salesperson IN (#Salesperson)
Would it be easier if i were dealing with integers as opposed to varchar values?
Any help would be absolutely appreciated!
You need to use table-valued parameters. Look it up on technet or msdn
Best part of it that your table-valued parameters can have multiple columns.
Note however that you have to define TVP parameter as readonly. So if you want to return similar set from your function you will need to create another variable inside your function.
Example:
CREATE TYPE Names AS TABLE
( Name VARCHAR(50));
GO
/* Create a procedure to receive data for the table-valued parameter. */
CREATE PROCEDURE dbo.mySP
#n Names READONLY
AS
SELECT Sum(SalesDollars)
FROM
WHERE Invoices.Salesperson in (select Name from #n)
GO
CREATE FUNCTION dbo.myFun(#n Names READONLY) returns int
AS
SELECT Sum(SalesDollars)
FROM
WHERE Invoices.Salesperson in (select Name from #n)
GO
/* Declare a variable that references the type. */
DECLARE #names AS Names;
/* Add data to the table variable. */
INSERT INTO #names (Name)
VALUES ('BILL'),('MOSES')
-- using stored procedure with TVP
EXEC dbo.mySP #names
-- using function with TVP
select dbo.myFun(#names)
GO
This could be done this way:
SET #Salesperson='BILL,MOSES'
SELECT *
FROM YourTable
WHERE Invoices.Salesperson IN (SELECT * FROM dbo.split(#Salesperson,','))
This is how you split the values.
I would typically do this using a user defined table type: SQL Fiddle Example.
CREATE TYPE <schema>.SalespersonList AS TABLE
(
Name varchar(32)
)
You may have to grant execute permissions on the type:
GRANT EXECUTE ON TYPE::<schema>.SalespersonList TO <user>
Then you can create a function to use it:
CREATE FUNCTION <schema>.fnGetTotalSales
(
#nameList <schema>.SalespersonList READONLY
)
RETURNS INT
AS
BEGIN
DECLARE #ret INT
SELECT #ret = Sum(SalesDollars)
FROM Invoices i
INNER JOIN #nameList nl ON nl.Name = i.Salesperson
RETURN #ret
END
Then you would just insert your list into the type and call the function:
DECLARE #salesPersonList <schema>.SalespersonList
INSERT INTO #salesPersonList (Name)
SELECT 'Bill'
UNION
SELECT 'Moses'
SELECT <schema>.fnGetTotalSales(#salesPersonList)

SQL - create function that will take a table variable as an input

I'm having trouble writing a function that will take a table variable as an input and return the total number of rows in that table.
Here is my try:
CREATE FUNCTION fTableRows( #table TABLE )
RETURNS INT AS
BEGIN
RETURN( SELECT COUNT(*) FROM #table )
END
If you do this in SQL server 2008 + you have use user defined data type - table.
Good explanation can be found here: Pass table as parameter into SQL Udf
CREATE FUNCTION getTableRows
(
#TableName VARCHAR(30)
)
RETURNS INT AS
BEGIN
RETURN( SELECT COUNT(*) FROM #TableName)
END

How do I make a function in SQL Server that accepts a column of data?

I made the following function in SQL Server 2008 earlier this week that takes two parameters and uses them to select a column of "detail" records and returns them as a single varchar list of comma separated values. Now that I get to thinking about it, I would like to take this table and application-specific function and make it more generic.
I am not well-versed in defining SQL functions, as this is my first. How can I change this function to accept a single "column" worth of data, so that I can use it in a more generic way?
Instead of calling:
SELECT ejc_concatFormDetails(formuid, categoryName)
I would like to make it work like:
SELECT concatColumnValues(SELECT someColumn FROM SomeTable)
Here is my function definition:
FUNCTION [DNet].[ejc_concatFormDetails](#formuid AS int, #category as VARCHAR(75))
RETURNS VARCHAR(1000) AS
BEGIN
DECLARE #returnData VARCHAR(1000)
DECLARE #currentData VARCHAR(75)
DECLARE dataCursor CURSOR FAST_FORWARD FOR
SELECT data FROM DNet.ejc_FormDetails WHERE formuid = #formuid AND category = #category
SET #returnData = ''
OPEN dataCursor
FETCH NEXT FROM dataCursor INTO #currentData
WHILE (##FETCH_STATUS = 0)
BEGIN
SET #returnData = #returnData + ', ' + #currentData
FETCH NEXT FROM dataCursor INTO #currentData
END
CLOSE dataCursor
DEALLOCATE dataCursor
RETURN SUBSTRING(#returnData,3,1000)
END
As you can see, I am selecting the column data within my function and then looping over the results with a cursor to build my comma separated varchar.
How can I alter this to accept a single parameter that is a result set and then access that result set with a cursor?
Others have answered your main question - but let me point out another problem with your function - the terrible use of a CURSOR!
You can easily rewrite this function to use no cursor, no WHILE loop - nothing like that. It'll be tons faster, and a lot easier, too - much less code:
FUNCTION DNet.ejc_concatFormDetails
(#formuid AS int, #category as VARCHAR(75))
RETURNS VARCHAR(1000)
AS
RETURN
SUBSTRING(
(SELECT ', ' + data
FROM DNet.ejc_FormDetails
WHERE formuid = #formuid AND category = #category
FOR XML PATH('')
), 3, 1000)
The trick is to use the FOR XML PATH('') - this returns a concatenated list of your data columns and your fixed ', ' delimiters. Add a SUBSTRING() on that and you're done! As easy as that..... no dogged-slow CURSOR, no messie concatenation and all that gooey code - just one statement and that's all there is.
You can use table-valued parameters:
CREATE FUNCTION MyFunction(
#Data AS TABLE (
Column1 int,
Column2 nvarchar(50),
Column3 datetime
)
)
RETURNS NVARCHAR(MAX)
AS BEGIN
/* here you can do what you want */
END
You can use Table Valued Parameters as of SQL Server 2008, which would allow you to pass a TABLE variable in as a parameter. The limitations and examples for this are all in that linked article.
However, I'd also point out that using a cursor could well be painful for performance.
You don't need to use a cursor, as you can do it all in 1 SELECT statement:
SELECT #MyCSVString = COALESCE(#MyCSVString + ', ', '') + data
FROM DNet.ejc_FormDetails
WHERE formuid = #formuid AND category = #category
No need for a cursor
Your question is a bit unclear. In your first SQL statement it looks like you're trying to pass columns to the function, but there is no WHERE clause. In the second SQL statement you're passing a collection of rows (results from a SELECT). Can you supply some sample data and expected outcome?
Without fully understanding your goal, you could look into changing the parameter to be a table variable. Fill a table variable local to the calling code and pass that into the function. You could do that as a stored procedure though and wouldn't need a function.