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
Related
I am trying to create a function in SQL Server using the following, but I think I am missing something in either in syntax or query
CREATE FUNCTION DEMO.Get_Rate_For_Absence
(#company_id_ VARCHAR,
#emp_no_ VARCHAR,
#account_date_ DATE)
RETURN DECIMAL(10, 2) AS
BEGIN
DECLARE #RATE_ DECIMAL(10, 2)
SET #RATE_ = SELECT rate
FROM DEMO.Employee
WHERE COMPANY_ID = '#company_id_ '
AND Emp_no = '#emp_no_ '
AND ORG_CODE = '#wage_code_'
AND ACCOUNT_DATE = '#account_date_'
RETURN #RATE
END
The SQL statement that I am trying to write inside function code block is:
SELECT DISTINCT rate
FROM DEMO.Employee
WHERE Company_ID = #company_id_
AND EMP_NO = #emp_no_
AND ACCOUNT_DATE = #account_date_
Something like:
CREATE OR ALTER FUNCTION DEMO.Get_Rate_For_Absence
(#company_id VARCHAR(200),
#emp_no VARCHAR(200),
#account_date DATE)
RETURNS DECIMAL(10, 2) AS
BEGIN
DECLARE #RATE DECIMAL(10, 2)
SET #RATE = (
SELECT rate
FROM DEMO.Employee
WHERE COMPANY_ID = #company_id
AND Emp_no = #emp_no
AND ACCOUNT_DATE = #account_date
)
RETURN #RATE
END
Perhaps you actually want to return a whole resultset rather than just a single value.
Then you should use an inline Table Valued Function (of the form RETURNS TABLE AS RETURN SELECT ...) which in any case performs much better than a scalar function.
Variables don't go in quotes so you just do COMPANY_ID = #company_id_.
Always declare varchar with a length.
CREATE OR ALTER FUNCTION DEMO.Get_Rate_For_Absence (
#company_id_ VARCHAR(100),
#emp_no_ VARCHAR(100),
#wage_code_ VARCAHR(100),
#account_date_ DATE
)
RETURNS TABLE AS RETURN
SELECT e.rate
FROM DEMO.Employee e
WHERE e.COMPANY_ID = #company_id_
AND e.Emp_no = #emp_no_
AND e.ORG_CODE = #wage_code_
AND e.ACCOUNT_DATE = #account_date_;
You use it slightly differently than scalar functions, as it goes in the FROM part
SELECT r.rate
FROM DEMO.Get_Rate_For_Absence('a', 'b', 'c', GETDATE()) r;
Or
SELECT r.rate
FROM SomeTable t
CROSS APPLY DEMO.Get_Rate_For_Absence(t.a, t.b, t.c, t.date) r;
ALTER FUNCTION [dbo].[FSmapping] (
#runID as bigInt,
#TypMap as char(1)
)
RETURNS TABLE
AS
RETURN
IF (#TypMap ='C')
BEGIN
SELECT b.[A/C],g.accountNumber, b.[Process], g.[endingBalanceLC], g.beginningBalanceLC
FROM table1 g
LEFT JOIN table2 b ON b.[Acc2]=g.Account_Synt
where RunID = #runID
END
I am trying to implement a table function in SQL. Basically it should have 3 more IFs and the only thing that's going to switch is the table2, from table2-4 based on inputs C I R S. The error is the IF statement, I know that something is missing, I am having a hard time implementing some sort of a switch function with table as a return value. The Select works flawlessly without the IF syntax, it also says errors on the two variables inside the IF
Must declare the scalar variable runID and TypMap
You need to change your query as multi-statement table-valued functions so your function has to like as below:
CREATE OR ALTER FUNCTION [dbo].[FSmapping] (
#runID as bigInt,
#TypMap as char(1)
)
RETURNS #Table1JoinTable2
TABLE (
--table definition here
Col1 VARCHAR(10)
)
AS
BEGIN
IF (#TypMap ='C')
BEGIN
INSERT INTO #Table1JoinTable2
SELECT #TypMap
--Put select query SELECT b.[A/C],g.accountNumber, b.[Process], g.[endingBalanceLC], g.beginningBalanceLC
--FROM table1 g
--LEFT JOIN table2 b ON b.[Acc2]=g.Account_Synt
--where RunID = #runID
END
IF (#TypMap ='I')
BEGIN
INSERT INTO #Table1JoinTable2
SELECT #TypMap
--Put your select query
END
IF (#TypMap ='I')
BEGIN
INSERT INTO #Table1JoinTable2
SELECT #TypMap
--Put your select query
END
IF (#TypMap ='R')
BEGIN
INSERT INTO #Table1JoinTable2
SELECT #TypMap
--Put your select query
END
IF (#TypMap ='S')
BEGIN
INSERT INTO #Table1JoinTable2
SELECT #TypMap
--Put your select query
END
RETURN
END
If TypMap does not come C, what will the function return?
i think u need 'ELSE'.
IF Boolean_expression
BEGIN
-- Statement block executes when the Boolean expression is TRUE
END
ELSE
BEGIN
-- Statement block executes when the Boolean expression is FALSE
END
SQL is not my best thing but I have been trying to optimize this stored procedure. It had multiple scalar-valued functions that I tried to change to table-valued functions because I read in many places that it's a more efficient way of doing it. And now I have them made but not real sure how to implement or if I maybe just didn't create them correctly.
This is the function I'm calling.
Alter FUNCTION [IsNotSenateActivityTableValue]
(
#ActivityCode int,
#BillId int,
#TextToDisplay varchar(max)
)
returns #T table(result varchar(max))
as
begin
DECLARE #result varchar(max);
declare #countcodes int;
declare #ishousebill int;
select #ishousebill = count(billid)
from BillMaster
where BillID = #BillID and Chamber = 'H'
If (#ishousebill = 0)
begin
SELECT #countcodes = count([ActivityCode])
FROM [HouseCoreData].[dbo].[ActivityCode]
where ActivityDescription not like '%(H)%' and ActivityType = 'S'
and [ActivityCode] = #ActivityCode
if (#countcodes = 0)
begin
set #result = 'test'
end
else
begin
set #result = 'test2'
end
end
else
begin
set #result = #TextToDisplay
end
RETURN
END
And this is how I was trying to call them like this. I would prefer just being able to put them in the top but really anything that works would be good.
SELECT distinct
ActionDates.result as ActionDate
,ActivityDescriptions.result as ActivityDescription
FROM BillWebReporting.vwBillDetailWithSubjectIndex as vw
left outer join [BillWebReporting].[HasHouseSummary] as HasSummary on vw.BillID = HasSummary.BillID
outer APPLY dbo.IsNotSenateActivityDateTableValue(ActivityCode,vw.BillID,[ActionDate]) ActionDates
OUTER APPLY dbo.IsNotSenateActivityTableValue(ActivityCode,vw.BillID,[ActivityDescription]) as ActivityDescriptions
Getting a count just to see if at least one row exists is very expensive. You should use EXISTS instead, which can potentially short circuit without materializing the entire count.
Here is a more efficient way using an inline table-valued function instead of a multi-statement table-valued function.
ALTER FUNCTION dbo.[IsNotSenateActivityTableValue] -- always use schema prefix!
(
#ActivityCode int,
#BillId int,
#TextToDisplay varchar(max)
)
RETURNS TABLE
AS
RETURN (SELECT result = CASE WHEN EXISTS
(SELECT 1 FROM dbo.BillMaster
WHERE BillID = #BillID AND Chamber = 'H'
) THEN #TextToDisplay ELSE CASE WHEN EXISTS
(SELECT 1 FROM [HouseCoreData].[dbo].[ActivityCode]
where ActivityDescription not like '%(H)%'
and ActivityType = 'S'
and [ActivityCode] = #ActivityCode
) THEN 'test2' ELSE 'test' END
END);
GO
Of course it could also just be a scalar UDF...
ALTER FUNCTION dbo.[IsNotSenateActivityScalar] -- always use schema prefix!
(
#ActivityCode int,
#BillId int,
#TextToDisplay varchar(max)
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #result VARCHAR(MAX);
SELECT #result = CASE WHEN EXISTS
(SELECT 1 FROM dbo.BillMaster
WHERE BillID = #BillID AND Chamber = 'H'
) THEN #TextToDisplay ELSE CASE WHEN EXISTS
(SELECT 1 FROM [HouseCoreData].[dbo].[ActivityCode]
where ActivityDescription not like '%(H)%'
and ActivityType = 'S'
and [ActivityCode] = #ActivityCode
) THEN 'test2' ELSE 'test' END
END;
RETURN (#result);
END
GO
Table-valued functions return a table, in which, like any other table, rows have to be inserted.
Instead of doing set #result = ....., do:
INSERT INTO #T (result) VALUES ( ..... )
EDIT: As a side note, I don't really understand the reason for this function to be table-valued. You are essentially returning one value.
First of all UDFs generally are very non-performant. I am not sure about MySQL, but in Sql Server a UDF is recompiled every time (FOR EACH ROW OF OUTPUT) it is executed, except for what are called inline UDFs, which only have a single select statement, which is folded into the SQL of the outer query it is included in... and so is only compiled once.
MySQL does have inline table-valued functions, use it instead... in SQL Server, the syntax would be:
CREATE FUNCTION IsNotSenateActivityTableValue
(
#ActivityCode int,
#BillId int,
#TextToDisplay varchar(max)
)
RETURNS TABLE
AS
RETURN
(
Select case
When y.bilCnt + z.actCnt = 0 Then 'test'
when y.bilCnt = 0 then 'test2'
else #TextToDisplay end result
From (Select Count(billId) bilCnt
From BillMaster
Where BillID = #BillID
And Chamber = 'H') y
Full Join
(Select count([ActivityCode]) actCnt
From [HouseCoreData].[dbo].[ActivityCode]
Where ActivityDescription not like '%(H)%'
And ActivityType = 'S'
And [ActivityCode] = #ActivityCode) z
)
GO
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')
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()