I am having the error with a Subquery returning more than one value. How do I reduce to one? - sql

At the very end of the Stored procedure a SELECT statement is made to display the contents of the Table including function that will simultaneously populate fields in the table.
Here is the Select Statement:
IF #type = 'SH'
SELECT DISTINCT *
FROM #History
ORDER BY 1, 2, 3, 4, 5
ELSE
SELECT DISTINCT AmhazName
,Activity
,ServiceName
,Sarid
,PerformedDate
,UserRole
,Details
,dbo.ufn_SarHistoryActionText(sarid, status, performeddate) AS [ActionText]
,FullName
,CategoryDescription
,StatusDescription
,ActionPerformed
,Case
when Details like '%ProjManagerId%'
Then dbo.ufn_GetUserForHistoryReport (PerformedDate, SarId, '%ProjManagerId%')
Else
--when Details like '%UserId%'
dbo.ufn_GetUserForHistoryReport (PerformedDate, SarId, '%UserId%')
--(select 'no user') as [AssignedUser]
End as [AssignedUser]
--,dbo.ufn_GetPMForHistoryReport(PerformedDate, SarId) as [AssignedUser]
FROM #history
ORDER BY 1, 2, 3, 4, 5
DROP TABLE #Historyw
Here is the function I believe is causing problems:
ALTER FUNCTION [dbo].[ufn_SarHistoryActionText]
(
-- Add the parameters for the function here
#sarID int
, #status varchar(6)
, #statusDate datetime
)
RETURNS varchar(100)
AS
BEGIN
-- Declare the return variable here
DECLARE #Result varchar(100)
set #Result = (
SELECT C.ActionText
from LuStatusChange as C
WHERE C.FromStatus = dbo.ufn_SarHistoryPriorStatus(#sarID,#status,#statusDate)
AND C.ToStatus = #status
)
-- Return the result of the function
RETURN #Result
END
GO
As I debug and walk through loads of values, I haven't come across anything that resulted in multiple values. maybe I'm missing something.

Add TOP 1 in the select inside the function:
SELECT TOP 1 C.ActionText

Can you replace
set #Result = (
SELECT C.ActionText
from LuStatusChange as C
WHERE C.FromStatus = dbo.ufn_SarHistoryPriorStatus(#sarID,#status,#statusDate)
AND C.ToStatus = #status
)
as below:
#Result ***IN*** (
SELECT C.ActionText
from LuStatusChange as C
WHERE C.FromStatus = dbo.ufn_SarHistoryPriorStatus(#sarID,#status,#statusDate)
AND C.ToStatus = #status
)
If functionally your query should not written more than 1 row, something is wrong with your query.

Related

Sending same parameter twice in exec

I have a simple stored procedure like this:
[dbo].[getStatusList]
#Extended NVARCHAR(255) = 'Project Status',
#Exclude NVARCHAR(255) = '',
#All BIT = 0
AS
SET NOCOUNT ON
IF (#All = 0)
BEGIN
SELECT
[GeneralKey],
[Label]
FROM
[General]
WHERE
[Extended] = #Extended
AND [Label] <> #Exclude
ORDER BY
[OrderID];
END
ELSE
BEGIN
IF (#All = 1)
BEGIN
SELECT
0 AS [GeneralKey],
'Any' AS [Label],
0 AS [OrderID]
UNION ALL
SELECT
[GeneralKey],
[Label],
[OrderID]
FROM
[General]
WHERE
[Extended] = #Extended
AND [Label] <> #Exclude
ORDER BY
[OrderID];
END
END
That I want to do is exec this stored procedure sending twice #Extended parameter like:
exec getStatusList #Extended = 'title1' AND #Extended = 'title2'
It is not possible to do something like this on exec? To only way to solve this is to add another parameter to stored procedure?
Update
As comments below mentioned, I tried this:
CREATE OR ALTER PROCEDURE usp_Get_StatusListByDesignType
-- Add the parameters for the stored procedure here
#Extended NVARCHAR(MAX),
#Exclude NVARCHAR(255) = '',
#All BIT = 0
AS
SET NOCOUNT ON
IF (#All = 0)
BEGIN
DECLARE #Parameter1 VARCHAR(50)
DECLARE #Parameter2 VARCHAR(50)
;WITH CTE AS
(
SELECT
*,
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) rn
FROM
STRING_SPLIT (#Extended,',')
)
SELECT
#Parameter1 = MAX(CASE WHEN rn = 1 THEN VALUE END),
#Parameter2 = MAX(CASE WHEN rn = 2 THEN VALUE END)
FROM
CTE
SELECT
[GeneralKey], [Label]
FROM
[General]
WHERE
[Extended] IN (SELECT #Parameter1, #Parameter2)
AND [Label] <> #Exclude
ORDER BY
[OrderID];
END
ELSE
BEGIN
IF (#All = 1)
BEGIN
SELECT
0 AS [GeneralKey],
'Any' AS [Label],
0 AS [OrderID]
UNION ALL
SELECT
[GeneralKey],
[Label],
[OrderID]
FROM
[General]
WHERE
[Extended] IN (SELECT #Parameter1, #Parameter2)
AND [Label] <> #Exclude
ORDER BY
[OrderID];
END
RETURN;
But I get this error:
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
You can let pass your parameter like para1Val1,para1Val2... connected with , comma.
then use STRING_SPLIT function to split it from , comma then get the parameter.
DECLARE #Extended varchar(max)='title1,titl2'
Here is a simple
DECLARE #Extended varchar(max)='title1,titl2'
select *,row_number() over(order by (select NULL)) rn
from STRING_SPLIT (#Extended,',')
Then you can set parameters in SP.
declare parameters variable, then use row_number make your parameter row number.
next step use condition aggregate function set the parameter in select clause.
declare #parameter1 varchar(50)
declare #parameter2 varchar(50)
;with cte as (
select *,row_number() over(order by (select NULL)) rn
from STRING_SPLIT (#Extended,',')
)
select #parameter1 = MAX(case when rn = 1 then value end),
#parameter2 = MAX(case when rn = 2 then value end)
from cte
sqlfiddle
This method :
exec getStatusList #Extended='title1' AND #Extended = 'title2'
it's not going to work at all as a parameter or a variable in general can only hold one value and nothing more. So, you can't do that unless you execute the store procedure twice and specify the parameters on each one of them. Or you may use loops to do it. But i'm not fan of loops and I always suggests to avoid them as much as possible.
The method that I see it fits your situation is a TVP with some modifications on the store procedure itself.
So, you'll pass the values in comma separate values in #Extended and from the store procedure you'll use IN() and NOT IN() instead of = and <> this will extend it to have more values to compare rather than one value.
Then you can use XML to split the values and turn them into rows.
So we will use this :
SELECT LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)')))
FROM (
SELECT CAST('<XMLRoot><RowData>' + REPLACE(#Extended,',','</RowData><RowData>') + '</RowData></XMLRoot>' AS XML) Extended
) D
CROSS APPLY Extended.nodes('/XMLRoot/RowData')m(n)
You can inject it directly into the store procedure with modifying the operators that I mentioned above, and it will work just fine. but for the code reuse, we will use it as TVP.
CREATE FUNCTION SplitToRows
(
#Extended VARCHAR(MAX)
)
RETURNS TABLE
AS
RETURN
(
SELECT LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)'))) Extended
FROM (
SELECT CAST('<XMLRoot><RowData>' + REPLACE(#Extended,',','</RowData><RowData>') + '</RowData></XMLRoot>' AS XML) Extended
) D
CROSS APPLY Extended.nodes('/XMLRoot/RowData')m(n)
)
Now, you can modify the store procedure to the following :
[dbo].[getStatusList]
#Extended NVARCHAR(255) = 'Project Status'
, #Exclude NVARCHAR(255) = ''
, #All BIT = 0
AS
SET NOCOUNT ON
IF(#All = 0)
BEGIN
SELECT
[GeneralKey]
, [Label]
FROM [General]
WHERE
[Extended] IN( SELECT * FROM dbo.SplitToRows(#Extended) )
AND [Label] NOT IN( SELECT * FROM dbo.SplitToRows(#Exclude) )
ORDER BY
[OrderID];
END
ELSE
BEGIN
IF(#All = 1)
BEGIN
SELECT
0 AS [GeneralKey]
, 'Any' AS [Label]
, 0 AS [OrderID]
UNION ALL
SELECT
[GeneralKey]
, [Label]
, [OrderID]
FROM [General]
WHERE
[Extended] IN( SELECT * FROM dbo.SplitToRows(#Extended) )
AND [Label] NOT IN( SELECT * FROM dbo.SplitToRows(#Exclude) )
ORDER BY
[OrderID];
END
END
Now, you can pass multiple separated values in #Extended and #Exclude at the same time like this :
#Extended = 'title1, title2, title3'
#Exclude = 'title5, title8'
so both parameters will use the same method.

SQL dynamic columns and Update multiple columns

I have a table UserPermission which has a number of columns of TINYINT type. e.g Read, Write, Update, Delete, Access etc.
I get three parameters in the stored procedure: #UserId, #ColNames, #ColValues where #ColNames and #ColValues are comma separated values.
How can I insert or update the table row (if already exists) with the passed column names and corresponding values.
I try to write the dynamic query which runs fine for INSERT but I was unable to write the UPDATE query dynamically with each column and its value to be concatenate.
Any response would be appreciated
Thanks in advance.
This is a somewhat dirty way to do what you require. However, if you create the following Stored Procedure:
CREATE FUNCTION [dbo].[stringSplit]
(
#String NVARCHAR(4000),
#Delimiter NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
WITH Split(stpos,endpos)
AS(
SELECT 0 AS stpos, CHARINDEX(#Delimiter,#String) AS endpos
UNION ALL
SELECT endpos+1, CHARINDEX(#Delimiter,#String,endpos+1)
FROM Split
WHERE endpos > 0
)
SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
'Data' = SUBSTRING(#String,stpos,COALESCE(NULLIF(endpos,0),LEN(#String)+1)-stpos)
FROM Split
)
You can then use that Procedure to join the data together:
DECLARE #TotalCols INT
DECLARE #TotalVals INT
SET #TotalCols = (
SELECT COUNT(ID) AS Total
FROM dbo.stringSplit('department, teamlead', ',')
);
SET #TotalVals = (
SELECT COUNT(ID) AS Total
FROM dbo.stringSplit('IT, Bob', ',')
);
IF #TotalCols = #TotalVals
BEGIN
IF OBJECT_ID('tempdb..#temptable') IS NOT NULL
DROP TABLE #temptable
CREATE TABLE #temptable (
ColName VARCHAR(MAX) NULL
,ColValue VARCHAR(MAX) NULL
)
INSERT INTO #temptable
SELECT a.DATA
,b.DATA
FROM dbo.stringSplit('department, teamlead', ',') AS a
INNER JOIN dbo.stringSplit('IT, Bob', ',') AS b ON a.Id = b.Id
SELECT *
FROM #temptable;
END
It's not very efficient, but it will bring you the desired results.
You can then use the temp table to update, insert and delete as required.
Instead of having a comma delimited list I would create a separate parameter for each Column and make its default value to NULL and in the code update nothing if its null or insert 0. Something like this....
CREATE PROCEDURE usp_UserPermissions
#UserID INT
,#Update INT = NULL --<-- Make default values NULL
,#Delete INT = NULL
,#Read INT = NULL
,#Write INT = NULL
,#Access INT = NULL
AS
BEGIN
SET NOCOUNT ON;
Declare #t TABLE (UserID INT, [Update] INT,[Read] INT
,[Write] INT,[Delete] INT,[Access] INT)
INSERT INTO #t (Userid, [Update],[Read],[Write],[Delete],[Access])
VALUES (#UserID , #Update , #Read, #Write , #Delete, #Access)
IF EXISTS (SELECT 1 FROM UserPermission WHERE UserID = #UserID)
BEGIN
UPDATE up -- Only update if a value was provided else update to itself
SET up.[Read] = ISNULL(t.[Read] , up.[Read])
,up.[Write] = ISNULL(t.[Write] , up.[Write])
,up.[Update] = ISNULL(t.[Update] , up.[Update])
,up.[Delete] = ISNULL(t.[Delete] , up.[Delete])
,up.[Access] = ISNULL(t.[Access] , up.[Access])
FROM UserPermission up
INNER JOIN #t t ON up.UserID = t.UserID
END
ELSE
BEGIN
-- if already no row exists for that User add a row
-- If no value was passed for a column add 0 as default
INSERT INTO UserPermission (Userid, [Update],[Read],[Write],[Delete],[Access])
SELECT Userid
, ISNULL([Update], 0)
, ISNULL([Read], 0)
, ISNULL([Write], 0)
, ISNULL([Delete], 0)
, ISNULL([Access], 0)
FROM #t
END
END

Function return table variable

I'm trying to create a function that return a table variable.So firstly i get data from Table1 and put it in another table variable. Here i want check if this variable isempty the function return the parameter result else return the result of the table variable
The function script is bellow :
USE[DATABase1]
GO
IF OBJECT_ID (N'CodeFunc', N'TF') IS NOT NULL DROP FUNCTION dbo.CodeFunc;
GO
CREATE FUNCTION CodeFunc ( #Code nvarchar(4) , #Table nvarchar(40) = '' )
RETURNS #VirtualDAT TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
Code nvarchar(400)
)
AS
BEGIN
DECLARE #CodeM nvarchar(400)
DECLARE #imax INT SET #imax = ##ROWCOUNT
DECLARE #i INT SET #i = 1
DECLARE #SelectDAT TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
Code nvarchar(400)
)
INSERT #SelectDAT
SELECT Code FROM table1
WHERE table1.id = 41
IF(EXISTS (SELECT 1 FROM #SelectDAT))
BEGIN
WHILE (#i <= #imax)
BEGIN
SELECT #CodeM = Code FROM #SelectDAT WHERE RowID = #i
INSERT INTO #VirtualDAT(Code) VALUES (#CodeM)
SET #i = #i + 1
END
END
ELSE
INSERT INTO #VirtualDAT(Code) VALUES (#Code)
RETURN
END
So this script works without put it inside function.
And i test this function like this :SELECT * FROM dbo.CodeFunc( 'toto',Default ) the result is :
IF(EXISTS (SELECT 1 FROM #SelectDAT)) no record returned
esle the result is ok
As VR46 says. The ##ROWCOUNT will be set to 0 because there is no query before it. Any code executing in a function happens as a seperate set of queries. It was probably returning a value outside the function because you had previously used the query window for another unrelated query
You could re-factor this function quite dramatically. Look below, ##ROWCOUNT will work here as it is just after the insert query and will definitely have a value based on the insert.
I have not been able to test this, but I think something like this should do the same job.
USE[DATABase1]
GO
IF OBJECT_ID (N'CodeFunc', N'TF') IS NOT NULL DROP FUNCTION dbo.CodeFunc;
GO
CREATE FUNCTION CodeFunc ( #Code nvarchar(4) , #Table nvarchar(40) = '' )
RETURNS #VirtualDAT TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
Code nvarchar(400)
)
AS
BEGIN
insert into #VirtualDAT
Select Code from table1 where table1.id = 41
if ##ROWCOUNT = 0
begin
INSERT INTO #VirtualDAT(Code) VALUES (#Code)
end
RETURN
END
Since you are assigning #imax with ##ROWCOUNT right after declaration of variable will be initialized with zero.
From MSDN ##ROWCOUNT
Returns the number of rows affected by the last statement.
If am not wrong you need to assign value to #imax after the insert into..select query.
INSERT #SelectDAT
SELECT Code FROM table1
WHERE table1.id = 41
SET #imax= ##ROWCOUNT
You can do the same in SET BASED APPROACH without using while loop.
CREATE FUNCTION Codefunc (#Code NVARCHAR(4),
#Table NVARCHAR(40) = '')
returns #VirtualDAT TABLE (
rowid INT IDENTITY ( 1, 1 ),
code NVARCHAR(400))
AS
BEGIN
IF EXISTS (SELECT code
FROM table1
WHERE table1.id = 41)
BEGIN
INSERT INTO #VirtualDAT
(code)
SELECT code
FROM table1
WHERE table1.id = 41
END
ELSE
INSERT INTO #VirtualDAT
(code)
VALUES (#Code)
RETURN
END

IN Clause is not taking dynamic passed value in SQL 2008

I have below SQL Procedure where I am passing and setting dynamic values.
Code: SQL Procedure Name: GetArchivedData
ALTER PROCEDURE [dbo].[GetArchivedData](#PublicationURL varchar(100),#Number int,#Action varchar(max))
AS
DECLARE #TEST Varchar(max)
IF (#Action = 'ALL')
BEGIN
SET #TEST = '''ADD'''+','+'''UPD'''+','+'''DEL''';
END
ELSE
BEGIN
SET #TEST = #Action
END
IF (#Number !=0)
BEGIN
PRINT 'Inside'+ #TEST
BEGIN TRANSACTION TRAN1
SELECT
1 AS Tag,
NULL AS Parent,
NULL AS [root!1!],
NULL AS [Item!2!Id],
NULL AS [Item!2!Action],
NULL AS [Item!2!Publication_Id],
NULL AS [Item!2!Item_Reference_Id],
NULL AS [Item!2!Item_type],
convert( datetime, '9999-01-01' ) AS [Item!2!Last_Published_Date],
NULL AS [Item!2!Url],
NULL AS [Item!2!Schema_Id]
UNION
SELECT TOP (#Number)
2,
1,
'1',
T.ID,
T.ACTION,
T.PUBLICATION_ID,
T.ITEM_REFERENCE_ID,
T.ITEM_TYPE,
T.LAST_PUBLISHED_DATE,
T.URL,
T.SCHEMA_ID
FROM DBO.AUTN_ITEMS T WHERE FLAG=1 AND ACTION IN (#TEST) AND URL LIKE #PublicationURL+'%'
ORDER BY [Item!2!Last_Published_Date] DESC
FOR XML EXPLICIT
COMMIT TRANSACTION TRAN1
END
ELSE IF (#Number = 0)
BEGIN
PRINT 'Outside'+ #TEST
BEGIN TRANSACTION TRAN2
SELECT
1 AS Tag,
NULL AS Parent,
NULL AS [root!1!],
NULL AS [Item!2!Id],
NULL AS [Item!2!Action],
NULL AS [Item!2!Publication_Id],
NULL AS [Item!2!Item_Reference_Id],
NULL AS [Item!2!Item_type],
convert( datetime, '9999-01-01' ) AS [Item!2!Last_Published_Date],
NULL AS [Item!2!Url],
NULL AS [Item!2!Schema_Id]
UNION
SELECT
2,
1,
'1',
T.ID,
T.ACTION,
T.PUBLICATION_ID,
T.ITEM_REFERENCE_ID,
T.ITEM_TYPE,
T.LAST_PUBLISHED_DATE,
T.URL,
T.SCHEMA_ID
FROM DBO.AUTN_ITEMS T WHERE FLAG=1 AND ACTION IN (#TEST) AND URL LIKE #PublicationURL+'%'
ORDER BY [Item!2!Last_Published_Date] DESC
FOR XML EXPLICIT
COMMIT TRANSACTION TRAN2
END
RETURN
Excuting SQL Procedure:
DECLARE #return_value int
EXEC #return_value = [dbo].[GetArchivedData]
#PublicationURL = N'/in',
#Number = 0,
#Action = N'ALL'
SELECT 'Return Value' = #return_value
I can see the values are getting set properly, if I am printing it in the procedure however one value works perfectly but when I am setting SET #TEST = '''ADD'''+','+'''UPD'''+','+'''DEL'''; no results are returned
Please suggest!!
Use Table Variable instead of #Test as string like this,
DECLARE #ActionTbl table ([Action] varchar(3))
IF (#Action = 'ALL')
BEGIN
INSERT INTO #ActionTbl SELECT 'Add' AS ID
UNION ALL
SELECT 'UPD' AS ID
UNION ALL
SELECT 'DEL' ID
END
ELSE
BEGIN
INSERT INTO #ActionTbl VALUES(#Action)
END
And in query use
ACTION IN (Select Action from #ActionTbl)
Instead of
ACTION IN (#TEST)
First of all the main issue here - you CAN'T use IN to search one string in another. IN is used to search value in the row set of values. Also the #TEST STRING value is 'ADD,'UPD','DEL' (each item is with quotas). I guess the Action field contains values without quotas so additional y to other answers there is one more way:
Replace
ACTION IN (#TEST)
with
#TEST LIKE '%'''+ACTION+'''%'

SQL Table Valued Function in Select Statement

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