SQL Creating a function which checks variables to make a total - sql

I've made 2 views which calculates the profit per reservation.
One calculates the profit from bikes and the other one from accessories.
Now when I tried SUMing them together, I get false reports. I found out why: something a reservation doesnt contain a bike or a accessory. This field gives a NULL return, which makes my SUM function unusable, because it lacks results.
I tried this:
USE Biker
GO
CREATE FUNCTION fnMaxOmzet
(
#Fiets AS int,
#Accessoire AS int
)
RETURNS int
AS
BEGIN
DECLARE #MaxOmzet AS int
IF #Fiets = NULL
SET #MaxOmzet = #Accessoire
ELSE IF #Accessoire = NULL
SET #MaxOmzet = #Fiets
ELSE
SET #MaxOmzet = #Fiets + #Accessoire
RETURN #MaxOmzet
END
But it isn't working because it gives multiple results..
Probably cause I am using '=' while it checking a list.
Does anyone know a way to make this function?
SELECT * FROM dbo.vAccessoireOmzet
SELECT * FROM dbo.vFietsOmzet
https://ibb.co/bTmJ26
Expected result: List of ID 1 - 100 and the total of AccesoireOmzet + FietsOmzet

Personally, I would use an inline Table Function. You'll find the performance is significantly faster.
NExt, you don't test for the value NULL by using the = operator, you use IS NULL.
This should cater for the logic you have above, however it is untested due to missing DDL and sample data:
CREATE FUNCTION dbo.fnMaxOmzet (#Fiets int, #Accessoire int)
RETURNS TABLE AS
RETURN
(
SELECT CASE WHEN #Fiets IS NULL THEN #Accessoire
WHEN #Accessoire IS NULL THEN #Fiets
ELSE #Fiets + #Accessoire
END AS MaxOmzet
)
GO
OK, just seen your comment with this sql: SELECT dbo.fnMaxOmzet((SELECT FietsOmzet FROM dbo.vFietsOmzet), (SELECT AccessoireOmzet FROM dbo.vAccessoireOmzet)).
That syntax is just wrong, sorry. You need to use APPLY and a JOIN. This is pure guess work as we have no DDL and Sample data... however:
SELECT MO.MaxOmzet--, --**Your other columns go here**
FROM vFietsOmzet FO
JOIN vAccessoireOmzet AO ON --**Your JOIN criteria goes here**
CROSS APPLY dbo.fnMaxOmzet (FO.FietsOmzet, AO.AccessoireOmzet) MO;

You need to use IS NULL rather than = NULL.
But you could also use something like this:
USE Biker
GO
CREATE FUNCTION fnMaxOmzet
(
#Fiets AS int,
#Accessoire AS int
)
RETURNS int
AS
BEGIN
RETURN ISNULL(#Fiets, 0) + ISNULL(#Accessoire, 0)
END
You can read here for more information on the behaviour of = NULL.
Update:
I imagine what you might be looking for is something like:
SELECT
ISNULL(F.Huurovereenkomst_id, A.Huurovereenkomst_id) Huurovereenkomst_id
, SUM(dbo.fnMaxOmzet(F.FietsOmzet, A.AccessoireOmzet)) MaxOmzet
FROM
dbo.vFietsOmzet F
FULL JOIN dbo.vAccessoireOmzet A ON F.Huurovereenkomst_id = A.Huurovereenkomst_id
GROUP BY ISNULL(F.Huurovereenkomst_id, A.Huurovereenkomst_id)

use coalesce thats position
USE Biker
GO
CREATE FUNCTION fnMaxOmzet
(
#Fiets AS int,
#Accessoire AS int
)
RETURNS int
AS
BEGIN
RETURN coalesce(#Fiets+#Accessoire,#Fiets,#Accessoire) from MyTableVar;
END

Related

How to set a value to variable from table

I'm trying to create this function, this return the final score of a student in a subject, this student can take the test multiple times
create function finalResult(#stC varchar(10), #sj varchar(10))
returns float
as begin
declare #result float
set #result = (select sum(score)/count(studentCode)
from Results
where #stC = studentCode and #sj = subject)
return #result
end
The problem here is it says "Select statements included within a function cannot return data to a client" when I remove the () wrapping the select. Idk how this thing works can anyone explain
you can thin it down a bit. I am also assuming SCORE is NOT an int
Example
create function finalResult(#stC varchar(10), #sj varchar(10))
returns float --<< consider using a decimal()
as
begin
return (
select sum(score)/nullif(count(*) ,0) -- added a divide by zero error trap
from Results
where #stC = studentCode
and #sj = subject
)
end

SQL Server WHERE clause : column IS NULL or column = parameter value

The code snippet below is what I'm trying to achieve, but I'm having trouble making it work. If the parameter that gets passed into the procedure is null, I want to only return the rows with a WHERE clause IS NULL, but if there is a value, I want to return the rows that are equal to the value passed in. Dynamic SQL seems like it would work, but I'm curious if there's an easier way I'm missing. Thanks in advance.
PARAM:
#id varchar(10) = '123456789'
SELECT *
FROM TABLE T
WHERE
CASE
WHEN #id IS NULL THEN (id IS NULL)
ELSE id = #id
END
The logic you want is:
WHERE (#id IS NULL AND id IS NULL) OR
id = #id
You're trying to use a CASE expression like a Case (Switch) statement. Switches don't exist in T-SQL, and a CASE expression returns a scalar value not a boolean result.
Don't, however, use CASE expressions in the WHERE, use proper Boolean logic:
SELECT *
FROM YourTable YT
WHERE (ID = #ID
OR (ID IS NULL AND #ID IS NULL))

UDF showing weird behavior when checking if variable holds null value

I am trying to write a UDF that is used by a check constraint but seem to keep running into an issue.
When having a complete empty table the following results in 1
declare #a float = (SELECT max(amount) FROM Bid WHERE auctionId = 1)
if(#a is null) select 1
else select 0
But when I try to implement similar logic into an UDF it seems to return 0 every time running it against an empty table.
CREATE FUNCTION dbo.fn_ck_newBidIsHigher(#auction int, #bid numeric(8,2))
RETURNS bit
BEGIN
DECLARE #currentHighestBid float = (SELECT max(amount) FROM Bid WHERE auctionId = #auction)
IF((#bid > #currentHighestBid) OR (#currentHighestBid is null))
BEGIN
RETURN 1
END
RETURN 0
END
GO
've been looking at it for over an hour now (maybe that's the problem), but I can't figure out where it's going wrong.
I am calling the function in the check constraint as follows:
ALTER TABLE Bid ADD CONSTRAINT ck_New_Bid_Must_Be_Higher_Than_Previous_Bid CHECK(dbo.fn_ck_newBidIsHigher(auctionId, amount) = 1)
Instead of using an IF, how about using a CASE? When using a CASE you can have more than 2 possible results. F.e. 1 or 0 or NULL, instead of only 1 or 0.
Also, it's safer to compare values of the same type.
Just to avoid any unexpected side-effects from the implicit cast/convert before the comparison.
CREATE FUNCTION dbo.fn_ck_newBidIsHigher(#auction int, #bid numeric(8,2))
RETURNS bit
BEGIN
DECLARE #currentHighestBid numeric(8,2) = (SELECT max(amount) FROM Bid WHERE auctionId = #auction);
DECLARE #result bit = (
case
when #bid > #currentHighestBid then 1
when #bid <= #currentHighestBid then 0
end);
RETURN #result;
END

UDF Table UDV or Scalar UDF?

I will do my best to make this question better than my last fiasco. I am getting the dreaded >"cannot find either column "dbo" or the user-defined function or aggregate "dbo.PriMonthAvgPrice", or the name is ambiguous.<
I am attempting to find the avg sales price from the previous month. Here is my UDF:
USE [WoodProduction]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[PriMonthAvgPrice]
(
-- Add the parameters for the function here
#endofmonth datetime,
#begofmonth datetime,
#PlantCode varchar
)
RETURNS decimal (10,2)
AS
BEGIN
-- Declare the return variable here
DECLARE #MonthEndAvgPrice decimal (10,2)
-- Add the T-SQL statements to compute the return value here
SELECT #MonthEndAvgPrice =
(
select
sum(Actual_Sales_Dollars/Actual_Volume)
FROM
woodproduction.dbo.plywood_layup_sales pls
WHERE
Production_Date between #begofmonth and #endofmonth
and actual_volume <> 0
and #PlantCode = pls.Plant_Code
)
-- Return the result of the function
RETURN #MonthEndAvgPrice
END
This is my SELECT statement from my query:
SELECT
DISTINCT
P.[Plant_Number]
,p.plant_name
,pls.plant_code
,(pls.[Budget_Realization]) AS 'BR'
,(pls.[Actual_Volume] ) AS 'AV'
,(pls.[Budget_Volume]) AS 'BV'
--,sum (dpb.[Gross_Production_Per_Hr]) AS 'GPB'
,(p.Production_Volume) AS 'PV'
,CASE
WHEN coalesce (pls.[Actual_Volume],0) = 0 and
coalesce (pls.[Actual_Sales_Dollars],0) = 0
THEN 0
ELSE (pls.[Actual_Sales_Dollars]/pls.[Actual_Volume])
END
AS 'AP'
,pls.production_date
,[dbo].[PriMonthAvgPrice](#endofmonth,#begofmonth, pls.plant_code) AS 'PriMoAvgPrice'
My BASIC understanding is that I HAVE created a Scalar Function. From what I've been reading about my error however, This error returns on TVF's. Is this true? I created a SVF prior to this dealing with just determining a prior month end date so it wasn't as involved as this one where I create the query in the UDF.
Do I need to change this to a TVF? And if so, how do I incorporate SELECT * when I have to join multiple tables along with this?
Thanks in advance.
Aaron
You don't show the from clause, but is the database you created the function in part of it?
Does it work if you fully qualify the name (include the database)?
Have you independently tested the function with:
select [dbo].[PriMonthAvgPrice] ('01/01/2011', '02/01/2011', 'test')
Note: of course you would use some actual values that should return a result.
Please run this and tell us the values returned:
SELECT Actual_Sales_Dollars,Actual_Volume, pls.PLant_code
FROM woodproduction.dbo.plywood_layup_sales pls
WHERE Production_Date between '09-01-2011' and '09-30-2011'
and actual_volume <> 0

TSQL - Count specific values within Multistatement table UDF and pass them to additional column

during coding my project I have encountered an obstacle and cannot go through with this issue...
here is what I would like to achieve, I have a simple table which stores data connected with football players like: Number (ID), Name, Goals (are additional ones, but right now are irrelevant) and I have created a UDF Multistatement table LotOfGoals which looks as follows:
CREATE FUNCTION LotOfGoals()
RETURNS #Players TABLE
(
Number INT,
Name VARCHAR(20),
Goals INT
FuzzyLevel FLOAT(3) --extra column which I would like to add to result
)
AS
BEGIN
INSERT #Players
SELECT Number, Name, Goals
FROM FuzzyFootballTeam
WHERE Goals > 2
ORDER BY Number
-- here FuzzyLevel column should include data counted by MembershipLevel
-- scalar UDF.
-- I want to pass each number of goals into MembershipLevel function and
-- insert return value into a new column FuzzyLevel.
RETURN
END
GO
now MembershipLevel function:
CREATE FUNCTION MembershipLevel(#Goals INT)
RETURNS float(3)
AS
BEGIN
DECLARE #Level float(3)
SET #Level = 0.25*#Goals - 0.5;
RETURN #Level
END
As I have written, after WHERE clause I would like to pass each number of goals to a MembershipLevel and then its return value insert into new column FuzzyLevel.
I would be really really grateful for any hint, idea etc.
Thanks in advance !
True, I will change to in-line one. One more question is there any way to use FuzzyLevel column in where clause ? (I receive Invalid column name 'FuzzinessLevel') what I want is to limit allowed fuzzylevel. I have expanded both functions with one more additional argument #AcceptedFuzzyLevel float and the scalar function looks like this:
DECLARE #Level float(3)
DECLARE #TempLevel float(3)
IF (#Goals <= 2)
SET #TempLevel = 0;
IF (#TempLevel >= #FuzzyLevelAccepted)
SET #Level = #TempLevel;
ELSE IF (#Goals > 2 AND #Goals < 6)
SET #TempLevel = 0.25*#Goals - 0.5;
IF (#TempLevel >= #FuzzyLevelAccepted)
SET #Level = #TempLevel;
ELSE IF (#Goals >= 6)
SET #TempLevel = 1;
IF (#TempLevel >= #FuzzyLevelAccepted)
SET #Level = #TempLevel;
RETURN #Level
But after execution I also receive records with NULL values.
OK, I have fixed it. Just resolved following inequality: x > 4y + 2. Works but I`m curious why it is not possible to use new column in Where clause.
Thanks a million !
Just add it as a column because MembershipLevel is a scalar udf. It doesn't matter about the outer code (stored proc or tablek valued udf or SELECT)
INSERT #Players (Number, Name, Goals, FuzzyLevel)
SELECT Number, Name, Goals,
dbo.MembershipLevel(Goals)
FROM FuzzyFootballTeam ft
WHERE Goals > 2
ORDER BY Number
Observations: I'd explicitly specify the column list for #Players. I'd also ask myself why this isn't an in-line table valued function: a multi-statement udf is often a performance killer...