SQL UDF Group By Parameter Issue - sql

I'm having some issues with a group by clause in SQL. I have the following basic function:
CREATE FUNCTION dbo.fn_GetWinsYear (#Year int)
RETURNS int
AS
BEGIN
declare #W int
select #W = count(1)
from tblGames
where WinLossForfeit = 'W' and datepart(yyyy,Date) = #Year
return #W
END
I'm trying to run the following basic query:
select dbo.fn_GetWinsYear(datepart(yyyy,date))
from tblGames
group by datepart(yyyy,date)
However, I'm encountering the following error message: Column 'tblGames.Date' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Any ideas why this is occurring? FYI, I know I can remove the function and combine into one call but I'd like to keep the function in place if possible.

I think you should be calling your function like this.
select dbo.fn_GetWinsYear(datepart(yyyy,getdate()))
OR
select dbo.fn_GetWinsYear('2010')
Essentially you are just passing a year to your function and the function is returning the number of wins for that year.
If you don't know the year, your function could look something like this...
CREATE FUNCTION dbo.fn_GetWinsYear ()
RETURNS #tblResults TABLE
( W INT, Y INT )
AS
BEGIN
INSERT #tblResults
SELECT count(1), datepart(yyyy,[Date])
FROM tblGames
WHERE WinLossForfeit = 'W'
GROUP BY datepart(yyyy,[Date])
RETURN
END
SELECT * FROM dbo.fn_GetWinsYear()

Related

Error Number:141,State:1,Class:15 A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations

I am trying to write a function to get count of appointments with matching doctorId, here is function :
CREATE FUNCTION dbo.GetSumOfAppointments (#doctorId int)
RETURNS int
AS
BEGIN
DECLARE #count int;
SET #count = (SELECT
COUNT(*)
FROM dbo.Appointments
WHERE DoctorId = #doctorId);
RETURN ISNULL(#count, 0)
END
but its giving me error :
Error Number:141,State:1,Class:15 A SELECT statement that assigns a
value to a variable must not be combined with data-retrieval
operations.
what exactly is the problem with above?
CREATE FUNCTION dbo.GetSumOfAppointments (#doctorId int)
RETURNS int
AS
BEGIN
DECLARE #count int;
select #count = COUNT(*)
FROM dbo.Appointments
WHERE DoctorId = #doctorId;
RETURN ISNULL(#count, 0)
END
No need to declare a variable and applying Isnull function since count(*) always returns number of records in that table suppose if the table is empty then you would get zero. Try like this make it simple.
CREATE FUNCTION dbo.GetSumOfAppointments (#doctorId INT)
RETURNS INT
AS
BEGIN
RETURN (
SELECT COUNT(*)
FROM dbo.Appointments
WHERE DoctorId = #doctorId
);
END

Error using Common Table Expression in SQL User Defined Function

CREATE FUNCTION [dbo].[udfGetNextEntityID]
()
RETURNS INT
AS
BEGIN
;WITH allIDs AS
(
SELECT entity_id FROM Entity
UNION SELECT entity_id FROM Reserved_Entity
)
RETURN (SELECT (MAX(entity_id) FROM allIDs )
END
GO
SQL isn't my strong point, but I can't work out what I'm doing wrong here. I want the function to return the largest entity_id from a union of 2 tables. Running the script gives the error:
Incorrect syntax near the keyword 'RETURN'.
I looked to see if there was some restriction on using CTEs in functions but couldn't find anything relevant. How do I correct this?
CREATE FUNCTION [dbo].[udfGetNextEntityID]()
RETURNS INT
AS
BEGIN
DECLARE #result INT;
WITH allIDs AS
(
SELECT entity_id FROM Entity
UNION SELECT entity_id FROM Reserved_Entity
)
SELECT #result = MAX(entity_id) FROM allIDs;
RETURN #result;
END
GO
While you can do it, why do you need a CTE here?
RETURN
(
SELECT MAX(entity_id) FROM
(
SELECT entity_id FROM dbo.Entity
UNION ALL
SELECT entity_id FROM dbo.Reserved_Entity
) AS allIDs
);
Also there is no reason to use UNION instead of UNION ALL since this will almost always introduce an expensive distinct sort operation. And please always use the schema prefix when creating / referencing any object.
You can not return the way your are doing from the function.
Make use of a local variable and return the same.
CREATE FUNCTION [dbo].[udfGetNextEntityID]()
RETURNS INT
AS
BEGIN
DECLARE #MaxEntityId INT;
WITH allIDs AS
(
SELECT entity_id FROM Entity
UNION SELECT entity_id FROM Reserved_Entity
)
SELECT #MaxEntityId = MAX(entity_id) FROM allIDs;
RETURN #MaxEntityId ;
END
GO
create function tvfFormatstring (#string varchar(100))
returns #fn_table table
(id int identity(1,1),
item int)
as
begin
insert into #fn_table(item)
declare #result int
set #string = #string+'-'
;with cte (start,number)
as
(
select 1 as start , CHARINDEX('-',#string,1) as number
union all
select number+1 as start , CHARINDEX('-',#string,number+1) as number from cte
where number <= LEN(#string)
)
select #result = SUBSTRING(#string,start,number-start) from cte ;
return #result;
end
select * from tvfFormatstring ('12321-13542-15634')

how to make an outer join with the calling of other procedure

I have a stored procedure :
procedure qr_get_dep_boss(...,...)
takes two params(year, main_code) and returning one record the boss_num and his name .
I want to create another procedure and make a loop on the previous procedure calling , making an outer join with the result of another query
SELECT
year,
main_code,
name,
CASE
WHEN father_code in(-1) THEN NULL
ELSE father_code
END AS father_code,main_code || '_' || year AS main_id,
(SELECT COUNT(*)
FROM sm_r_build
WHERE father_code = sc.main_code
AND year = (SELECT MAX(year)
FROM st_quit_info)) childcount,
main_code||'_'|| father_code AS serial
FROM sm_r_build sc
WHERE year=(SELECT MAX(year)FROM st_quit_info)
So I want the result:
year,main_code,name,father_code,main_id,childcount,serial,boss_num,boss_name
I try this :
create procedure new_get_alldepwithboss()
returning int as year , int as main_code ,nvarchar(100) as name,int as father_code,nvarchar(255) as main_id,int as childcount,int as ll_boss_num,nvarchar(100) as ll_boss_name
define ll_year int;
define ll_main_code int;
define ll_name nvarchar(100);
define ll_father_code int;
define ll_main_id nvarchar(255) ;
define ll_childcount int;
define ll_boss_num int;
define ll_boss_name nvarchar(100);
foreach
SELECT year,main_code,name,CASE WHEN father_code in(-1) THEN NULL ELSE father_code END AS father_code,main_code || '_' || year AS main_id, (SELECT COUNT(*)
FROM sm_r_build
WHERE father_code=sc.main_code AND year= (SELECT MAX(year)
FROM st_quit_info)) childcount ,a.emp_num,a.emp_name
INTO ll_year ,ll_main_code,ll_name ,ll_father_code ,ll_main_id ,ll_childcount ,ll_boss_num,ll_boss_name
FROM sm_r_build sc , TABLE(FUNCTION qr_get_dep_boss(ll_main_code, ll_year))AS a(emp_num,emp_name)
WHERE year=(SELECT MAX(year)FROM st_quit_info)
return ll_year , ll_main_code, ll_name,ll_father_code,ll_main_id, ll_childcount , ll_boss_num,ll_boss_name with resume;
end foreach
end procedure
but in vain !
In case of SQL server I would recommend using rather function than store procedure. Header of function should look:
CREATE FUNCTION qr_get_dep_boss( #year DATE, #main_code INT)
RETURN #t TABLE ( boss_num INT, boss_name VARCHAR(50))
AS
... ( here boss_num and boss_name should be set )
In your main query you can use this function by:
SELECT year, main_code, ... , t.bos_num , t.boss_name
FROM ...
CROSS APPLY
qr_get_dep_boss(year, main_code) t
But I am not sure if it is possible in Informix, in case not you can always define two functions that returns single value. GL!
If I understand right, you don't need the second procedure.
What you need to do is use the RETURN xxx WITH RESUME into the procedure qr_get_dep_boss()
A example copied from the manual .
CREATE FUNCTION series (limit INT, backwards INT) RETURNING INT;
DEFINE i INT;
FOR i IN (1 TO limit)
RETURN i WITH RESUME;
END FOR;
IF backwards = 0 THEN
RETURN;
END IF;
FOR i IN (limit TO 1 STEP -1)
RETURN i WITH RESUME;
END FOR;
END FUNCTION; -- series

Creating a function out of a select statement in SQL

I have built a select statement to pick the date the participant was first recorded in the database. However I want to create a function out of this select statement to use it. I am fairly new to SQL and have never built a function before. My select statement looks like this:
Select DATEDIFF(day, (select min(startdatetime)
from GamePlay
), enddatetime)
from GamePlay
where ParticipantID = '200'
My attempted function looks like this:
CREATE FUNCTION daysPlayed (#ParticipantID int)
RETURNS DateTime
AS
BEGIN
Return DATEDIFF(day, (select min(startdatetime)
from GamePlay
), enddatetime)
from GamePlay
where ParticipantID = #ParticipantID
END
GO
DATEDIFF does not return a datetime. It returns an int.
Your function might look like this:
CREATE FUNCTION daysPlayed (#ParticipantID int)
RETURNS INT
AS
BEGIN
DECLARE #result INT
SELECT #result=DATEDIFF(day, (select min(startdatetime) from GamePlay), enddatetime) from GamePlay where ParticipantID = #ParticipantID
RETURN #result
END
but i don't think it will do what you want it to do.
Also, please note that using functions in selects over a large number of rows is a guarantee performance hit and you might not be able to use some of the indexes you set up in your tables.
I don't know exactly what you want but see this sample might be helpful
CREATE FUNCTION mDay (#Date nchar(10))
RETURNS nchar(2)
AS
BEGIN
RETURN substring(#Date,9,2)
END
SELECT dbo.Courses.MID, dbo.Masters.ID, dbo.Masters.Name,COUNT(CASE dbo.mDay(CDate) WHEN '01' THEN 2 END) AS day1
FROM dbo.Courses INNER JOIN
dbo.Masters ON dbo.Courses.MID = dbo.Masters.MCode
WHERE (dbo.Courses.CLevel = #Kind) AND (dbo.Courses.CDate BETWEEN #Date1 AND #Date2)
GROUP BY dbo.Courses.MID, dbo.Masters.Name, dbo.Masters.Family, dbo.Masters.ID

how to return a cell into a variable in sql functions

I want to define a scaler function which in that I'm going to return the result into a variable but I do not know how to do this.
CREATE FUNCTION dbo.Funname ( #param int )
RETURNS INT
AS
declare #returnvar int
select #returnvar = select colname from tablename where someconditions = something
return(#returnvar)
I want to make a function something like the top. I mean the result of the select statement which is:
select colname from tablename where someconditions = something
Is only a single cell and we are sure about it. I want to store it into a variable and return it from the function. How can I implement this thing?
I should probably mention that scalar UDFs do come with a considerable health warning and can cause performance issues depending upon how you use them.
Here's an example though.
CREATE FUNCTION dbo.Funname ( #param INT )
RETURNS INT
WITH RETURNS NULL ON NULL INPUT
AS
BEGIN
RETURN (SELECT number FROM master.dbo.spt_values WHERE number < #param)
END
In the above example I didn't use a variable as it is redundant. The version with variable is
BEGIN
DECLARE #Result int
SET #Result = (SELECT number FROM master.dbo.spt_values WHERE number < #param)
RETURN #Result
END
For both of the above you would need to be sure the Query returned at most one row to avoid an error at runtime. For example
select dbo.Funname(-1) Returns -32768
select dbo.Funname(0) Returns error "Subquery returned more than 1 value."
An alternative syntax would be
BEGIN
DECLARE #Result int
SELECT #Result = number FROM master.dbo.spt_values WHERE number < #param
RETURN #Result
END
This would no longer raise the error if the subquery returned more than one value but you would just end up with an arbitrary result with no warning - which is worse.
Following Comments I think this is what you need
CREATE FUNCTION dbo.getcustgrade(#custid CHAR(200))
RETURNS INT
WITH RETURNS NULL ON NULL INPUT
AS
BEGIN
RETURN
( SELECT [cust grade]
FROM ( SELECT customerid,
DENSE_RANK() OVER (ORDER BY COUNT(*) DESC) AS [cust grade]
FROM Orders
GROUP BY CustomerID
)
d
WHERE customerid = #custid
)
END