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.
Related
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
I might be overlooking something here or it could be the lack of coffee this am... I have a SQL Server function I'm trying to create and have it spit out results.
This is my code:
ALTER FUNCTION [dbo].[fn_top_forms] ()
RETURNS #topforms TABLE
(
-- Add the column definitions for the TABLE variable here
clicks varchar(50),
title varchar(150),
urlpath varchar(max)
)
AS
BEGIN
DECLARE #clicks as varchar(50)
DECLARE #title varchar(150)
DECLARE #urlpath varchar(max)
DECLARE #everyone_relevant as bit
IF #everyone_relevant = 1
BEGIN
INSERT INTO #topforms
SELECT TOP (10) clicks, title, url
FROM dbo.view_all_forms
ORDER BY clicks DESC
END
RETURN
END
My issue is that its not returning any values but I know in the view_all_forms, there is indeed data there. I'm using this in new query to return the results, it has the columns but no data. Any ideas?
Yup, looks like I just didnt have the thing set write... Note to self, DRINK MORE coffee in the am.
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.
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,','))
I have just started to create a stored function this is my first time so I am having a few problems. Currently I call the function using SELECT test(); (test is the function name for now). I want to send a number to the function (username ID) and have the username returned.
I have this working by using SELECT test(1); 1 is the ID of a user in the table. This seems to work as the username is returned, but if I type in any number the same username is returned also.
BEGIN
DECLARE new_username VARCHAR(90);
SELECT `username` INTO new_username FROM `users` WHERE `ID` = ID;
return new_username;
END
I have set the paramter as ID int .
Am I right in thinking that the keyword INTO will put the value of the username into the variable new_username ? If I run it without the INTO I get the error:
Not allowed to return a result set from a function
Have I made any obvious mistakes in this, I hope I havent done it totally wrong. Thanks for any advice :).
Edit : I just added a few more rows into my table , I now get the error:
Result consisted of more than one row
Full sql version:
CREATE DEFINER=`elliotts`#`%` FUNCTION `test`(ID int)
RETURNS varchar(32) CHARSET latin1
BEGIN
DECLARE new_username VARCHAR(32);
SELECT `username`
INTO new_username
FROM `users`
WHERE `ID` = ID;
return new_username;
END
Use:
DROP FUNCTION IF EXISTS `example`.`test` $$
CREATE FUNCTION `example`.`test` (param INT) RETURNS VARCHAR(32)
BEGIN
DECLARE new_username VARCHAR(32);
SELECT `username`
INTO new_username
FROM `users`
WHERE `ID` = param;
RETURN COALESCE(new_username, 'Username not found');
END $$
Mind that the VARCHAR length of the RETURN value matches the variable, which should match the column length you want to return.