Table Variable In a table Valued function - sql

I am getting an error when trying to get this table valued function up. I have an error when I try to modify it. It is
Incorrect syntax near the keyword 'Declare'.
However when I use this outside of the function it all works great. So I was just wondering is there something I am missing or how should I be doing this. Thanks.
ALTER FUNCTION [dbo].[XXX]( #i_contactkey int,
#v_scope varchar(15),
#i_entrykey int = null,
#i_staffcontactkey int = null,
#d_startdate datetime,
#d_today datetime)
RETURNS TABLE
AS begin
Declare #temp as table
(contactkey int, contactkey1 int, rolekey1 int,contactkey2 int, rolekey2 int, relationshipid int)
insert into #temp (contactkey , contactkey1 , rolekey1 ,contactkey2 , rolekey2 , relationshipid )
select contactkey , contactkey1 , rolekey1 ,contactkey2 , rolekey2 , relationshipid
from contact clcon
LEFT JOIN contactassociation ca on ca.contactkey2 = clcon.contactkey where ca.rolekey1 in (4,5,6)
and ca.relationshipid = 181
and ca.activeind = 1
and ca.associationkey = (select top 1 associationkey
from contactassociation
where contactkey2 = clcon.contactkey and activeind = 1
and relationshipid = 181
and rolekey1 in (4,5,6)
order by begindate desc)
SELECT
clcon.contactkey'ClientId',
clcon.Stat'ClientStatus',
ctacct.optiondesc'account',
ctlvl.optiondesc'levelid',
(clcon.lastname+', '+clcon.firstname)'ClientName',
clcon.firstname'ClientFirstName',
clcon.lastname'ClientLastName',
clcon.addressline1'address1',
clcon.addressline2'address2',
clcon.city'city',
dbo.getcnfgoption(81,clcon.stateid,'D')'state',
clcon.zipcode'zipcode',
clcon.mainphone'mainphone',
cgcon.contactkey'cgkey',(cgcon.firstname+' '+cgcon.lastname)'CGName',
cgcon.firstname'CGFirstName',
cgcon.lastname'CGLastName',
cgcon.addressline1'cgaddressline1',
cgcon.addressline2'cgaddressline2',
cgcon.city'cgcity',
dbo.getcnfgoption(81,cgcon.stateid,'D')'cgstate',
cgcon.zipcode'cgzipcode',
cgcon.mainphone'cgmainphone',
dbo.getClientAltCGKeys_JSON(clcon.contactkey,'J')'AltCGsJSON',
--dbo.getClientAltCGKeys(clcon.contactkey,'C')'AltCGNames',
--dbo.getClientAltCGKeys(clcon.contactkey,'L')'altcgnamekeyslast',
--dbo.getClientAltCGKeys(clcon.contactkey,'A')'altcgkeysaddress',
dbo.getClientEventCount(clcon.contactkey, 'M', #d_startdate, #d_today) 'MLOA',
dbo.getClientEventCount(clcon.contactkey, 'N', #d_startdate, #d_today) 'NMLOA',
dbo.getClientEventCount(clcon.contactkey, 'A', #d_startdate, #d_today) 'Alts',
dbo.getClientEventCount(clcon.contactkey, 'S', #d_startdate, #d_today ) 'Suspension',
dbo.getClientEventCountAnnual(clcon.contactkey, 'C') 'MissingNotes',
-- dbo.getContactVerificationStatus(clcon.contactkey, 'D')'clverification',
-- dbo.getContactVerificationStatus(cgcon.contactkey, 'D')'cgverification',
ed1.eventkey 'mdskey',
dbo.getCnfgTableOption(54,ed1.eventstatusid,'D')'mdsstatus',
ed1.ScheduledDate 'NextMDS',
ed2.eventkey 'pockey',
dbo.getCnfgTableOption(54,ed2.eventstatusid,'D')'pocstatus',
ed2.ScheduledDate 'NextPoC',
ed3.eventkey 'hvkey',
dbo.getCnfgTableOption(54,ed3.eventstatusid,'D')'hvsstatus',
ed3.ScheduledDate 'NextHV',
ed4.eventkey 'medlistkey',
dbo.getCnfgTableOption(54,ed4.eventstatusid,'D')'medstatus',
ed4.ScheduledDate 'NextMedList',
ed5.eventkey 'semikey',
dbo.getCnfgTableOption(54,ed5.eventstatusid,'D')'semistatus',
ed5.ScheduledDate 'NextSemi',
ed6.eventkey'placementkey',
ed6.startdate'placementstart',
ed6.enddate'placementend',
[dbo].[getClientCMName](clcon.contactkey)'cmname',
[dbo].[getClientRNName](clcon.contactkey)'rnname',
[dbo].[getClientCMKey](clcon.contactkey)'cmkey',
[dbo].[getClientRNKey](clcon.contactkey)'rnkey',
alertcount=(SELECT COUNT(eventalertkey) FROM veventalert WHERE alerttype='Alert' AND clientkey=clcon.contactkey AND viewedind=0 AND contactkey=COALESCE(#i_staffcontactkey,#i_contactkey)),
alertkey=(SELECT MAX(eventalertkey) FROM veventalert WHERE alerttype='Alert' AND clientkey=clcon.contactkey AND viewedind=0 AND contactkey=COALESCE(#i_staffcontactkey,#i_contactkey)),
msgcount=(SELECT COUNT(eventalertkey) FROM veventalert WHERE alerttype='Message' AND clientkey=clcon.contactkey AND viewedind=0 AND cgkey=cgcon.contactkey),
msgkey=(SELECT MAX(eventalertkey) FROM veventalert WHERE alerttype='Message' AND clientkey=clcon.contactkey AND viewedind=0 AND cgkey=cgcon.contactkey),
clcp.birthdate
FROM (select dbo.getcontactstatus(contactkey,'ts')'Stat',* from contact where dbo.getcontactstatus(contactkey,'ts') is not null ) clcon
INNER JOIN contactrole cr
ON (clcon.contactkey = cr.contactkey)
--Find caregiver contact info
LEFT JOIN contactassociation ca on ca.contactkey2 = clcon.contactkey and ca.rolekey1 in (4,5,6)
and ca.relationshipid = 181 and ca.activeind = 1
and ca.associationkey = (select max(associationkey)
from contactassociation
where contactkey2 = clcon.contactkey and activeind = 1
and relationshipid = 181
and rolekey1 in (4,5,6))
LEFT JOIN contact cgcon
ON cgcon.contactkey = ca.contactkey1 and cgcon.activeind = 1
LEFT JOIN contactbu cbu
ON (clcon.contactkey = cbu.contactkey)
/*Account/Lvl Information*/
LEFT JOIN contactprofile clcp
ON (clcon.contactkey=clcp.contactkey)
LEFT JOIN cnfgtableoption ctlvl
ON (clcp.svclevelid = ctlvl.tableoptionkey)
LEFT JOIN cnfgtableoption ctacct
ON (clcp.accountid = ctacct.tableoptionkey)
LEFT JOIN eventdefinition ed1 /* MDS */
ON (clcon.contactkey=ed1.contactkey AND ed1.eventkey=dbo.getContactEventByWftask(clcon.contactkey, 181, 'MINOPEN'))
LEFT JOIN eventdefinition ed2 /* POC */
ON (clcon.contactkey=ed2.contactkey AND ed2.eventkey=dbo.getContactEventByWftask(clcon.contactkey, 120, 'MINOPEN'))
LEFT JOIN eventdefinition ed3 /* HV */
ON (clcon.contactkey=ed3.contactkey AND ed3.eventkey=dbo.getContactEventByWftask(clcon.contactkey, 341, 'MINOPEN'))
LEFT JOIN eventdefinition ed4 /* MED */
ON (clcon.contactkey=ed4.contactkey AND ed4.eventkey=dbo.getContactEventByWftask(clcon.contactkey, 178, 'MINOPEN'))
LEFT JOIN eventdefinition ed5 /* SEMI */
ON (clcon.contactkey=ed5.contactkey AND ed5.eventkey=dbo.getContactEventByWftask(clcon.contactkey, 122, 'MINOPEN'))
LEFT JOIN eventdefinition ed6 /* Placement */
ON (clcon.contactkey=ed6.contactkey AND ed6.wftaskkey = 49
AND ed6.eventstatusid = 16
AND ed6.activeind = 1
AND ed6.enddate = (select MAX(enddate) from eventdefinition
where contactkey = clcon.contactkey and wftaskkey = 49
and activeind = 1
and eventstatusid = 16))
WHERE
--Contact info
cr.rolekey = 8
AND cbu.entrykey = #i_entrykey
--and dbo.getcontactstatus (clcon.contactkey,'TS') not in ('Closed','Discharged')
and clcon.Stat not in ('Closed')
and clcon.activeind=1
-- filter by branch entrykey (if param exists)
--order by clcon.lastname,clcon.firstname

When you're using multiple statements in a function and returning a table, i.e. a Table-Valued User-Defined Function, you need a certain syntax, something like:
CREATE FUNCTION dbo.MyFunction(#ID int)
RETURNS #Mytable TABLE
(
-- Columns returned by the function
ID int PRIMARY KEY NOT NULL,
-- (Other columns as required)
)
AS
BEGIN
--Various statements to populate #Mytable
RETURN; -- Returns #Mytable
END
See Table-Valued User-Defined Functions for more information.
If you have a function that just has RETURNS TABLE with no definition of the table being returned, this is an Inline User-Defined Function.
Inline user-defined functions are a subset of user-defined functions
that return a table data type. Inline functions can be used to achieve
the functionality of parameterized views.
See Inline User-Defined Functions.
The syntax for this is like:
CREATE FUNCTION dbo.MyFunction(#ID int)
RETURNS TABLE
AS
RETURN
(
SELECT *
FROM MyTable
WHERE ID = #ID
);
Here you don't define the table being returned and the body of the function can only be one SELECT statement.
At the moment your code is somewhere between the two; you need to get this to work as the first option, i.e. the Table-Valued User-Defined Function; start by defining the table being returned in the RETURNS clause and go from there.

You have invalid function declaration. When returning table you should specify variable name that will hold your table, so your header may look like:
ALTER FUNCTION [dbo].[XXX](.....)
RETURNS #temp TABLE (contactkey int, contactkey1 int, rolekey1 int,contactkey2 int, rolekey2 int, relationshipid int)
AS
begin
...

You need to define the table's columns in the RETURNS statement.

Related

Error while trying to return one column from SQL Server function

I have a function in SQL Server with a select statement and I want to return one row calculated inside the select statement - ORDER_VALUE_ADJUSTED.
CREATE FUNCTION order_value
(#date DATE,
#client VARCHAR(50),
#order_number VARCHAR(50))
RETURNS DECIMAL(13,2)
AS
BEGIN
SELECT
EKKO.EBELN,
SUM(CASE WHEN EKPO.NETWR = CDPOS.VALUE_NEW AND CDHDR.UDATE >= #date THEN CDPOS.VALUE_OLD ELSE EKPO.NETWR END) AS ORDER_VALUE_ADJUSTED
FROM
EKKO
INNER JOIN
EKPO_C AS EKPO ON EKKO.EBELN = EKPO.EBELN
AND EKKO.MANDT = EKPO.MANDT
LEFT JOIN
CDPOS_C AS CDPOS ON (EKPO.MANDT + EKPO.EBELN + EKPO.EBELP) = CDPOS.TABKEY
LEFT JOIN
CDHDR ON CDHDR.CHANGENR = CDPOS.CHANGENR
WHERE
EKKO.MANDT = #client
AND EKKO.EBELN = #order_number
GROUP BY
EKKO.EBELN
RETURN ORDER_VALUE_ADJUSTED
END;
I am getting these errors:
Msg 444, Level 16, State 2, Procedure order_value, Line 6 [Batch Start Line 0]
Select statements included within a function cannot return data to a client.
Msg 207, Level 16, State 1, Procedure order_value, Line 22 [Batch Start Line 0]
Invalid column name 'ORDER_VALUE_ADJUSTED'.
How can I solve this issue ? Do I need to rewrite it into a stored procedure ?
Your primary issues are that you are trying to SELECT straight out of the function, and you are not storing the data into variables to RETURN.
But it sounds like you actually need an inline Table Valued Function, rather than a Scalar Function, these are in any case much faster
CREATE OR ALTER FUNCTION dbo.order_value
(#date DATE,
#client VARCHAR(50),
#order_number VARCHAR(50))
RETURNS TABLE
AS RETURN
SELECT
EKKO.EBELN,
SUM(CASE WHEN EKPO.NETWR = CDPOS.VALUE_NEW AND CDHDR.UDATE >= #date THEN CDPOS.VALUE_OLD ELSE EKPO.NETWR END) AS ORDER_VALUE_ADJUSTED
FROM
EKKO
INNER JOIN
EKPO_C AS EKPO ON EKKO.EBELN = EKPO.EBELN
AND EKKO.MANDT = EKPO.MANDT
LEFT JOIN
CDPOS_C AS CDPOS ON (EKPO.MANDT + EKPO.EBELN + EKPO.EBELP) = CDPOS.TABKEY
LEFT JOIN
CDHDR ON CDHDR.CHANGENR = CDPOS.CHANGENR
WHERE
EKKO.MANDT = #client
AND EKKO.EBELN = #order_number
GROUP BY
EKKO.EBELN
;
An inline table function must be a single RETURN SELECT statement.
You use it like this
SELECT *
FROM dbo.order_value(GETDATE(), 'SomeClient', 'SomeOrder') ov;
Or
SELECT *
FROM dbo.Orders o
CROSS APPLY dbo.order_value(o.Date, o.Client, o.Number) ov;
A guess, can't verify the syntax at the moment:
CREATE FUNCTION order_value
(#date DATE,
#client VARCHAR(50),
#order_number VARCHAR(50))
RETURNS DECIMAL(13,2)
RETURN
SELECT SUM(CASE WHEN EKPO.NETWR = CDPOS.VALUE_NEW AND CDHDR.UDATE >= #date
THEN CDPOS.VALUE_OLD
ELSE EKPO.NETWR
END) AS ORDER_VALUE_ADJUSTED
FROM EKKO
INNER JOIN EKPO_C AS EKPO
ON EKKO.EBELN = EKPO.EBELN
AND EKKO.MANDT = EKPO.MANDT
LEFT JOIN CDPOS_C AS CDPOS
ON (EKPO.MANDT + EKPO.EBELN + EKPO.EBELP) = CDPOS.TABKEY
LEFT JOIN CDHDR
ON CDHDR.CHANGENR = CDPOS.CHANGENR
WHERE EKKO.MANDT = #client
AND EKKO.EBELN = #order_number;

Pass Table as parameter to function

I have a temp table which contains data needed in selection, but I can't join in to main select statement because it contains too many rows, and grouping is not good enough. So I decided to use values directly from my temp table in the selection, and it works fine. But since I need to add selection from temp table 50 times as sub queries in that main selection, I was considering to move it to function, as is nicer to call a function 50 times than sub queries.
My question is how to pass values from that table to function? Function will be also supplied with other parameters needed for extraction exact value from that table. I know I can't pass temp table as a parameter, but what about table variable? I don't care if I use temp table or a table variable..
Firstly I wrote a function containing the select statement same as for temp table, and it works but too slow. So if I could pass table results to a function, it will speed up the process..
My function now look like this:
ALTER FUNCTION [document].[GetPersonPremium]
(
-- Add the parameters for the function here
#DocumentId bigint,
#PersonId bigint,
#PeriodId int,
#PersonRole nvarchar(20)
)
RETURNS DECIMAL (18,2)
AS
BEGIN
-- Declare the return variable here
DECLARE #premiumSum decimal (18,2)
-- Add the T-SQL statements to compute the return value here
set #premiumSum =
(select top 1 pt.Premium from document.Document d
inner join document.Person p on p.DocumentCalculationLayerID = dcl.DocumentCalculationLayerID
inner join document.PersonTasks pt on pt.PersonId = p.PersonId
inner join document.PersonCalculationHelper pch on pch.PersonTaskId = pt.PersonTaskId
inner join document.PersonTaskCalculationHelper ptch on ptch.PersonId = p.PersonId
inner join document.PersonMarkTypes pmt on pmt.ConcernMarkTypeID = ptch.ConcernMarkTypeId
where dcl.DocumentID = #DocumentId and p.PersonId = #PersonId and pch.PeriodId = #PeriodId and pmt.Name = #PersonRole)
-- Return the result of the function
RETURN #premiumSum
END
And I would like to use it from stored procedure like this:
...
Engineer = Coalesce(document.GetPersonPremium(#DocumentId, p.PersonID, 65, 'Intern'), 0.00),
...
Any suggestion?
The answer is for those who come to this page with same or similar problem.
I created a table type:
CREATE TYPE PremiumTableType AS TABLE
(
PersonId bigint,
PeriodId int,
PersonRole nvarchar(20),
)
Included it in function:
ALTER FUNCTION [document].[GetPersonPremium]
(
-- Add the parameters for the function here
#DocumentId bigint,
#PersonId bigint,
#PeriodId int,
#PersonRole nvarchar(20),
#PremiumTableType PremiumTableType readonly
)
RETURNS DECIMAL (18,2)
AS
BEGIN
-- Declare the return variable here
DECLARE #premiumSum decimal (18,2)
-- Add the T-SQL statements to compute the return value here
set #premiumSum = (select p.Premium from #PremiumType p where p.PersonId = #PersonId and p.PeriodId = #PeriodId and p.PersonRole = #PersonRole)
-- Return the result of the function
RETURN #premiumSum
END
In SP declared table type variable
Declare #PremiumTableType PremiumTableType
Inserted data from my temp table
Insert into #PremiumTableType (PersonID, PeriodId, ConcernRole, PersonPremium)
Select p.PersonID, ...
And called function from SP like
document.GetPersonPremium(#DocumentID, p.PersonID, pt.PeriodID, 'Intern', #PremiumTableType)

Selecting a Value from Multiple Tables if they Exist

I have three tables I am working with.
Table A = Training_Requests (title varchar(20), intID INT)
Table B = Content_Requests (title varchar(20), intID INT)
Table C = Projects (intID INT, otherField varchar(20))
I am trying to write a SELECT statement where I can pass an intID and it tells me if there is a Training_Request and/or a Content_Request.
I am looking for one result set that could either be Content, Training or Content and Training
Example:
DECLARE #intID INT = '123
SELECT 'Training' as val,
(SELECT 'Content' as val
FROM Content_Requests as cr
WHERE cr.intID = #intID)
FROM Training_Requests as tr
WHERE tr.intID = #intID
The above would return a result with two columns which is not what I want.
There should be two separate records returned if both a Content_Request and a Training_Request exist.
The result should look like this, assuming that both a Training_Request and a Content_Request exist:
I cant create a temp table as this will be inside a view.
Update:
Using Giorgos Betsos's answer, I am getting the following error:
Subquery returned more than 1 value.
Use UNION:
SELECT 'Training' as val
FROM Training_Requests as tr
WHERE tr.intID = #intID
UNION
SELECT 'Content' as val
FROM Content_Requests as cr
WHERE cr.intID = #intID
DECLARE #intID int = 123;
SELECT CASE WHEN cr.intID IS NOT NULL AND tr.intID IS NOT NULL THEN 'Content and Training'
WHEN cr.intID IS NOT NULL THEN 'Content'
WHEN tr.intID IS NOT NULL THEN 'Training'
END
FROM Training_Requests tr
FULL OUTER JOIN Content_Requests cr
ON tr.intID = cr.intID
WHERE cr.intID = #intID OR tr.intID = #intID

SQL Server stored procedure WHERE clause

Need help with WHERE clause in this stored procedure.
How to write WHERE with this parameters and if any of these param contains specific value, then I need to get all values from that column ?
Sample if #post1 contains 1 then select values from that columns that are equals to 1.
But if #post1 contains 0 than select all values from that column. And that for all other parameters.
ALTER PROCEDURE [dbo].[spStavke]
#dat1 date,
#dat2 date,
#god int,
#post1 int,
#post2 int,
#post3 int
AS
BEGIN
SET NOCOUNT ON;
SELECT
[test1]
,[test2]
,[test3]
,[test3]
FROM
[PN].[dbo].[Stavke] AS stavke
LEFT JOIN
PN.dbo.Tip AS tip ON stavke.Vrsta = tip.id
LEFT JOIN
PN.dbo.Vrsta AS vrs ON stavke.Jedinica = vrs.id
END
SELECT
[test1]
,[test2]
,[test3]
,[test3]
FROM [PN].[dbo].[Stavke] as stavke
left join PN.dbo.Tip as tip on stavke.Vrsta=tip.id
left join PN.dbo.Vrsta as vrs on stavke.Jedinica = vrs.id
WHERE (#Post1 = 0 OR (#Post1 = 1 AND 1 IN( TEST1,TEST2,TEST3))
AND (#Post2 = 0 OR (#Post2 = 1 AND 1 IN( TEST1,TEST2,TEST3))

SQL WHERE ... IN clause with possibly null parameter

I am having some problems with my WHERE clause (using SQL 2008) . I have to create a stored procedure that returns a list of results based on 7 parameters, some of which may be null. The ones which are problematic are #elements, #categories and #edu_id. They can be a list of ids, or they can be null. You can see in my where clause that my particular code works if the parameters are not null. I'm not sure how to code the sql if they are null. The fields are INT in the database.
I hope my question is clear enough. Here is my query below.
BEGIN
DECLARE #elements nvarchar(30)
DECLARE #jobtype_id INT
DECLARE #edu_id nvarchar(30)
DECLARE #categories nvarchar(30)
DECLARE #full_part bit
DECLARE #in_demand bit
DECLARE #lang char(2)
SET #jobtype_id = null
SET #lang = 'en'
SET #full_part = null -- full = 1, part = 0
SET #elements = '1,2,3'
SET #categories = '1,2,3'
SET #edu_id = '3,4,5'
select
jobs.name_en,
parttime.fulltime_only,
jc.cat_id category,
je.element_id elem,
jt.name_en jobtype,
jobs.edu_id minEdu,
education.name_en edu
from jobs
left join job_categories jc
on (jobs.job_id = jc.job_id)
left join job_elements je
on (jobs.job_id = je.job_id)
left join job_type jt
on (jobs.jobtype_id = jt.jobtype_id)
left join education
on (jobs.edu_id = education.edu_id)
left join
(select job_id, case when (jobs.parttime_en IS NULL OR jobs.parttime_en = '') then 1 else 0 end fulltime_only from jobs) as parttime
on jobs.job_id = parttime.job_id
where [disabled] = 0
and jobs.jobtype_id = isnull(#jobtype_id,jobs.jobtype_id)
and fulltime_only = isnull(#full_part,fulltime_only)
-- each of the following clauses should be validated to see if the parameter is null
-- if it is, the clause should not be used, or the SELECT * FROM ListToInt... should be replaced by
-- the field evaluated: ie if #elements is null, je.element_id in (je.element_id)
and je.element_id IN (SELECT * FROM ListToInt(#elements,','))
and jc.cat_id IN (SELECT * FROM ListToInt(#categories,','))
and education.edu_id IN (SELECT * FROM ListToInt(#edu_id,','))
order by case when #lang='fr' then jobs.name_fr else jobs.name_en end;
END
Something like
and (#elements IS NULL OR je.element_id IN
(SELECT * FROM ListToInt(#elements,',')))
and (#categories IS NULL OR
jc.cat_id IN (SELECT * FROM ListToInt(#categories,',')))
....
should do the trick
je.element_id IN (SELECT * FROM ListToInt(#elements,',')) OR #elements IS NULL
that way for each one
Have you tried explicitly comparing to NULL?
and (#elements is null or je.element_id IN (SELECT * FROM ListToInt(#elements,','))
And so on.