How to set a value to variable from table - sql

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

Related

SQL Server 2016 - Variable showing NULL

We have a scalar function in our application as below
CREATE function dbo.SCMGetEnvProfileValueFn
(#HierarchyCode varchar(255), -- Usually the subsystem code
#Code varchar(50), -- The Code to find
#Default varchar(255) -- If not found, return this default
)
RETURNS varchar(255)
AS
BEGIN
DECLARE #Value as varchar(255)
SELECT #Value = (SELECT TOP(1) Value FROM HVCEnvProfile
WHERE HierarchyCode = #HierarchyCode
AND Code = #Code)
RETURN ISNULL (#Value, #Default)
END
We converted this function to Table function
CREATE FUNCTION SCMGetEnvProfileValueTblFn
(#HierarchyCode varchar(255), -- Usually the subsystem code
#Code varchar(50), -- The Code to find
#Default varchar(255) -- If not found, return this default
)
RETURNS TABLE
AS
RETURN
(SELECT TOP(1) ISNULL (Value, #Default) AS value
FROM HVCEnvProfile
WHERE HierarchyCode = #HierarchyCode AND Code = #Code)
Below 2 statements shows different output. We do not have a column in the table HVCEnvProfile for this entry. Why the variable #Value is showing NULL when there is no row in the table.
SELECT value
FROM dbo.SCMGetEnvProfileValueTblFn('Registration', 'AdmitDtmEffectsLocationHistory', 'TRUE')
SELECT dbo.SCMGetEnvProfileValueFn('Registration', 'AdmitDtmEffectsLocationHistory', 'TRUE')
If a query returns no rows, then no rows will be returned, wrapping an ISNULL won't change that. Example:
SELECT ISNULL(V.C,0)
FROM (VALUES(1),(2),(3),(4))V(C)
WHERE V.C = 5;
Notice this does not return 0, but nothing.
You need to wrap the entire query in an ISNULL.
CREATE FUNCTION SCMGetEnvProfileValueTblFn (#HierarchyCode varchar(255), -- Usually the subsystem code
#Code varchar(50), -- The Code to find
#Default varchar(255) -- If not found, return this default
)
RETURNS table
AS
RETURN
SELECT ISNULL((SELECT TOP (1) [Value]
FROM HVCEnvProfile
WHERE HierarchyCode = #HierarchyCode
AND Code = #Code
ORDER BY SomeColumn), #Default) AS [Value]; --Don' forgot to change the value of SomeColumn
Don't forget, as well, you need an ORDER BY when using a TOP unless you're "happy" with inconsistent results (which I doubt), so i have added one that you will need to amend.

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

SQL Creating a function which checks variables to make a total

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

**Occasional** Arithmetic overflow error converting expression to data type int

I'm running an update script to obfuscate data and am occasionally experiencing the arithmetic overflow error message, as in the title. The table being updated has 260k records and yet the update script will need to be run several times to produce the error. Although it's so rare I can't rely on the code until it's fixed as it's a pain to debug.
Looking at other similar questions, this is often resolved by changing the data type e.g from INT to BIGINT either in the table or in a calculation. However, I can't see where this could be required. I've reduced the script to the below as I've managed to pin point it to the update of one column.
A function is being called by the update and I've included this below. I suspect that, due to the randomness of the error, the use of the NEW_ID function could be causing it but I haven't been able to re-create the error when just running this part of the function multiple times. The NEW_ID function can't be used in functions so it's being called from a view, also included below.
Update script:
UPDATE dbo.Addresses
SET HouseNumber = CASE WHEN LEN(HouseNumber) > 0
THEN dbo.fn_GenerateRandomString (LEN(HouseNumber), 1, 1, 1)
ELSE HouseNumber
END
NEW_ID view and random string function
CREATE VIEW dbo.vw_GetNewID
AS
SELECT NEWID() AS New_ID
CREATE FUNCTION dbo.fn_GenerateRandomString (
#stringLength int,
#upperCaseBit bit,
#lowerCaseBit bit,
#numberBit bit
)
RETURNS nvarchar(100)
AS
BEGIN
-- Sanitise string length values.
IF ISNULL(#stringLength, -1) < 0
SET #stringLength = 0
-- Generate a random string from the specified character sets.
DECLARE #string nvarchar(100) = ''
SELECT
#string += c2
FROM
(
SELECT TOP (#stringLength) c2 FROM (
SELECT c1 FROM
(
VALUES ('A'),('B'),('C')
) AS T1(c1)
WHERE #upperCaseBit = 1
UNION ALL
SELECT c1 FROM
(
VALUES ('a'),('b'),('c')
) AS T1(c1)
WHERE #lowerCaseBit = 1
SELECT c1 FROM
(
VALUES ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9')
) AS T1(c1)
WHERE #numberBit = 1
)
AS T2(c2)
ORDER BY (SELECT ABS(CHECKSUM(New_ID)) from vw_GetNewID)
) AS T2
RETURN #string
END
Addresses table (for testing):
CREATE TABLE dbo.Addresses(HouseNumber nchar(32) NULL)
INSERT Addresses(HouseNumber)
VALUES ('DSjkmf jkghjsh35hjk h2jkhj3h jhf'),
('SDjfksj3548 ksjk'),
(NULL),
(''),
('2a'),
('1234567890'),
('An2b')
Note: only 7k of the rows in the addresses table have a value entered i.e. LEN(HouseNumber) > 0.
An arithmetic overflow in what is otherwise string-based code is confounding. But there is one thing that could be causing the arithmetic overflow. That is your ORDER BY clause:
ORDER BY (SELECT ABS(CHECKSUM(New_ID)) from vw_GetNewID)
CHECKSUM() returns an integer, whose range is -2,147,483,648 to 2,147,483,647. Note the absolute value of the smallest number is 2,147,483,648, and that is just outside the range. You can verify that SELECT ABS(CAST('-2147483648' as int)) generates the arithmetic overflow error.
You don't need the checksum(). Alas, you do need the view because this logic is in a function and NEWID() is side-effecting. But, you can use:
ORDER BY (SELECT New_ID from vw_GetNewID)
I suspect that the reason you are seeing this every million or so rows rather than every 4 billion rows or so is because the ORDER BY value is being evaluated multiple times for each row as part of the sorting process. Eventually, it is going to hit the lower limit.
EDIT:
If you care about efficiency, it is probably faster to do this using string operations rather than tables. I might suggest this version of the function:
CREATE VIEW vw_rand AS SELECT rand() as rand;
GO
CREATE FUNCTION dbo.fn_GenerateRandomString (
#stringLength int,
#upperCaseBit bit,
#lowerCaseBit bit,
#numberBit bit
)
RETURNS nvarchar(100)
AS
BEGIN
DECLARE #string NVARCHAR(255) = '';
-- Sanitise string length values.
IF ISNULL(#stringLength, -1) < 0
SET #stringLength = 0;
DECLARE #lets VARCHAR(255) = '';
IF (#upperCaseBit = 1) SET #lets = #lets + 'ABC';
IF (#lowerCaseBit = 1) SET #lets = #lets + 'abc';
IF (#numberBit = 1) SET #lets = #lets + '0123456789';
DECLARE #len int = len(#lets);
WHILE #stringLength > 0 BEGIN
SELECT #string += SUBSTRING(#lets, 1 + CAST(rand * #len as INT), 1)
FROM vw_rand;
SET #stringLength = #stringLength - 1;
END;
RETURN #string
END;
As a note: rand() is documented as being exclusive of the end of its range, so you don't have to worry about it returning exactly 1.
Also, this version is subtly different from your version because it can pull the same letter more than once (and as a consequence can also handle longer strings). I think this is actually a benefit.

SSRS Multivalue Parameter in Dataset Query issue

I Have an embedded dataset in my report which I pass parameters into.
This works fine for a single select using the = Sign in my And line
I would of thought and google results seem to be saying the same that i can just change the = sign to 'IN'
FROM [database].[dbo].[itemTable]
right Outer Join [database].[dbo].[CategoryTable]
on [database].[dbo].[itemTable].Category= [database].[dbo].[CategoryTable].Category And ([database].[dbo].[itemTable].Region = #pRegion) And ([database].[dbo].[itemTable].CategoryLN = #pCategoryLN )
where [database].[dbo].[CategoryTable].Category != 'RETIRED'
Above works fine but if I change to
[database].[dbo].[itemTable].Region IN #pRegion'
The query window says Incorrect syntax near '#pRegion'.
Looks like all you are missing is brackets around the parameter.
[database].[dbo].[itemTable].Region IN (#pRegion)
Also make sure you don't edit/parse the parameter values.
We've resolved this issue by using a database table-valued function (probably found somewhere on the internet, but I can't remember where)
CREATE FUNCTION [database].[dbo].[ParamSplit]
(
#List nvarchar(max), -- string returned from multivalue report parameter
#SplitOn nvarchar(5) -- separator character
)
RETURNS #RtnValue table
(
Id int identity(1,1),
Value nvarchar(100)
)
AS
BEGIN
While (Charindex(#SplitOn,#List)>0)
Begin
Insert Into #RtnValue (value)
Select Value = ltrim(rtrim(Substring(#List,1,Charindex(#SplitOn,#List)-1)))
Set #List = Substring(#List,Charindex(#SplitOn,#List)+len(#SplitOn),len(#List))
End
Insert Into #RtnValue (Value)
Select Value = ltrim(rtrim(#List))
Return
END
Then you can use it in your dataset query.
where [database].[dbo].[itemTable].Region IN (Select [dbo].[ParamSplit].[Value] from [database].[dbo].[ParamSplit](#pRegion,','))