How to get default value of optional parameter of table function - sql

I know function name TestOptionalParameter and parameter name number.
parameter is optional and has default value, which can change later
the function is
CREATE FUNCTION TestOptionalParameter
(
#number int = 1
)
RETURNS TABLE
AS
RETURN
(
SELECT #number * #number as sqr
)
how can I get value 1 from another function (or stored procedure) in the same database?
UPDATE
basically I don't need the result of function (I can get it with select * from TestOptionalParameter(default));
I'm trying to know what is default value (1 in my example) and save it into variable (something like declare x int = dbo.GetDefaultValue('TestOptionalParameter', '#number'))
so I need equivalent of c# reflection (Can I retrieve default value of parameter in method?)

Based on your comment reply I think you want call function in another function with the current parameter value pass.
So I think, this example might work for you.
CREATE FUNCTION TestOptionalParameter
(
#number int = 1
)
RETURNS TABLE
AS
RETURN
(
SELECT #number * #number as sqr
)
CREATE FUNCTION TestOptionalParameterNew
(
#number int = 1
)
RETURNS TABLE
AS
RETURN
(
SELECT #number * #number as total from [dbo].[TestOptionalParameter](#number)
)
select * from TestOptionalParameterNew(4)

The only way to get the parameter value seems to be to parse the ROUTINE_DEFINITION column of the information_schema.routines view as apparently the value isn't stored in any column, but rather evaluated from the definition at runtime.
If you know the parameter name in advance and the text in the procedure is well-formed then something like this could work. Note that in this example the substring returned is only 1 character long, so if the value can be longer you would have to extract a longer string.
SELECT
SUBSTRING
(
routine_definition,
PATINDEX('%#number int = %', ROUTINE_DEFINITION) + 14,
1
) ParamValue
FROM information_schema.routines
WHERE routine_type = 'function' AND
routine_name = 'TestOptionalParameter'
If you don't know the parameter names they can be found in the view referenced above, so it should be possible to find all default values by parsing the text definition, although it won't be easy.

You can use the below query to get the details about function
SELECT * FROM information_schema.routines WHERE routine_type='function' AND routine_name ='TestOptionalParameter'

Related

Anything like template literals while writing a search query in SQL?

I am writing a stored procedure to get a particular value.
declare #num int
set #num = (SELECT Id
FROM [sometable]
WHERE Name like '%today%')
-- returns #num = 1
Select Value
FROM [anothertable]
where name like 'days1'
In the last line of the query I want to add "1" or any other number after 'days', depending on the variable #num.
How can I do it, sort of like how we use template literals in Javascript, using the ${} syntax but in SQL?
You can just use the first query as a sub-query of the second:
select [Value]
from anothertable
where [name] = Concat('days', (select Id from sometable where [Name] like '%today%'));

How to select from text list randomly?

I'm trying to build SQL function that I can use as a default value for a column. The function is about selecting an avatar image path randomly if the user didn't assign an image.
I have tried to but a completely wrong example to just approach the image not the solution
what I need to do is something like this
select top 1 from "avatar1,png, avatar2,png, avatar3.png, avatar4.png, avatar5.png" order by rand();
and I will convert it to a function like this
CREATE FUNCTION dbo.ReturnAvatar()
RETURNS nvarchar(100)
AS
BEGIN
DECLARE #ret nvarchar(100);
SET #ret = (select top 1 from "avatar1,png, avatar2,png, avatar3.png, avatar4.png, avatar5.png" as tbl order by rand());
RETURN #ret;
END;
this is just to explain the idea that I'm not able to apply. I don't know if SQL server has something like this or not.
Here is one way:
CREATE VIEW getNewID AS SELECT newid() as new_id
CREATE FUNCTION dbo.ReturnAvatar()
RETURNS nvarchar(100)
AS
BEGIN
DECLARE #ret nvarchar(100);
SET #ret = (SELECT TOP 1 value
FROM
STRING_SPLIT('avatar1.png,avatar2.png,avatar3.png,avatar4.png,avatar5.png', ',')
ORDER BY (SELECT new_id FROM getNewID));
RETURN #ret;
END;
Note that your current CSV string of filenames does not seem proper, because comma does not indicate the start of the extension in either Windows or Linux. So, I have assumed dot everywhere. In addition, if you want to use STRING_SPLIT, you may only split on a single character. Therefore, I assume that comma will be the delimiter here.
You do not need to create a table at all. Simply put the number inside your string and choose the number randomly:
select 'avatar'+str(round(rand()*5+1,0))+'.png'
would be fine.
Put that into your function and you are all set.
rand() produces 0..1(excl.) so you can simply multiply it by 5 and add 1 to get your range of 1...5
Demo: http://sqlfiddle.com/#!18/9eecb/82866
Documentation:
ROUND ( numeric_expression , length [ ,function ] )
STR ( float_expression [, length [, decimal]])
rand(seed)
So essentially you could boil it down to:
select 'avatar'+ltrim(str(rand()*5+1,20,0))+'.png'
with
ltrim(string) taking care of the space
create function dbo.ReturnAvatar(#uid uniqueidentifier, #avatars int = 10)
returns varchar(100)
as
begin
return ('avatar' + cast(abs(checksum(#uid)) % isnull(abs(#avatars), 10)+1 as varchar(100)) + '.png')
end
go
create table myusers
(
username varchar(50),
theavatar varchar(100) default( dbo.ReturnAvatar(newid(), default))
);
insert into myusers(username)
select top (10000) 'user' + cast(row_number() over(order by(select null)) as varchar(50))
from master.dbo.spt_values as a
cross join master.dbo.spt_values as b;
go
select theavatar, count(*)
from myusers
group by theavatar;
go
drop table myusers;

Replace NULL with nulliff returns different query results

I'm trying to build a dynamic query, I will pass a parameter and if the parameter is empty, then the query returns all the items I have in the table. The second query returns all the items I have in the table. The first query returns a much shorter result.
Any ideas?
select ...
where table.code = isnull(nullif('',''),table.code)
select ...
where table.code = isnull(null,table.code)
This simple test shows the problem :
SELECT ISNULL(NULLIF('', ''), 'TEST')
Instead of returning TEST, as expected, it returns 'T', because the first NULLIF returns a null of varchar(1) type (the type of its parameters), so the return value of ISNULL is also casted to varchar(1).
I would simply use COALESCE instead of ISNULL (it doesn't try to convert the return value to the type of the first value) :
SELECT COALESCE(NULLIF('', ''), 'TEST')
This one returns the correct value 'TEST'.
So to fix your query you only need to replace ISNULL by COALESCE :
select ...
where table.code = coalesce(nullif('',''),table.code)
You can apply isnull() function twice as :
select ...
where table.code = isnull( isnull(null,#yourparam),table.code)
if #yourparam is null then isnull(null,#yourparam) returns null, too.
Let your parameter be denoted by #param that you supply in the query at runtime.
Your query could then be
declare #param varchar(50)
select * from table
where nullif(#param, '') is null or table.code = #param
for example see following two snippets, first query will return result only when there exists a table by the name you supplied, second query will return all records
declare #param varchar(50) = 'table_from_your_db'
select *
from information_schema.tables t
where nullif(#param, '') is null or t.table_name = #param
declare #param varchar(50) = ''
select *
from information_schema.tables t
where nullif(#param, '') is null or t.table_name = #param

return 2 columns values from scalar function in sql

Is ther a way were we can return 2 values with one variable in SQL Scalar function
ALTER FUNCTION [report].[fGetKPI] (#metricName AS VARCHAR(50))
RETURNS SMALLINT
AS
BEGIN
DECLARE
#kipId AS SMALLINT,
#metricId as SMALLINT;
SELECT TOP 1 #kipId = [KPI_Id], #metricId = [MetricId] FROM report.[KPIDetails] WHERE [MetricName] = #metricName;
IF (#kipId IS NULL OR #metricId IS NULL)
DECLARE #Error AS INT = dbo.fThrowError('No key found for the component "' + #metricName + '"');
RETURN (select #kipId,#metricId);
END
Scalar functions, by definition, return a single value:
User-defined scalar functions return a single data value of the type defined in the RETURNS clause.
Source: Types of Functions on TechNet
If you want to return more than one value, you should use a table-valued function.
You could also try to somehow pack your two SMALLINT values into one INT or convert them into a string with some separator, but that could get complicated and is not really (in most cases) necessary.
You can re-write the function as :
IF OBJECT_ID('[report].[fGetKPI]') IS NOT NULL
DROP FUNCTION [report].[fGetKPI];
GO
CREATE FUNCTION [report].[fGetKPI] (#metricName AS VARCHAR(50))
RETURNS TABLE
AS
RETURN
SELECT TOP 1 [KPI_Id], [MetricId] FROM report.[KPIDetails] WHERE [MetricName] = #metricName;
Go
and then further use dbo.fThrowError by selecting data from function. Hope this helps!!!

How do I make a function in SQL Server that accepts a column of data?

I made the following function in SQL Server 2008 earlier this week that takes two parameters and uses them to select a column of "detail" records and returns them as a single varchar list of comma separated values. Now that I get to thinking about it, I would like to take this table and application-specific function and make it more generic.
I am not well-versed in defining SQL functions, as this is my first. How can I change this function to accept a single "column" worth of data, so that I can use it in a more generic way?
Instead of calling:
SELECT ejc_concatFormDetails(formuid, categoryName)
I would like to make it work like:
SELECT concatColumnValues(SELECT someColumn FROM SomeTable)
Here is my function definition:
FUNCTION [DNet].[ejc_concatFormDetails](#formuid AS int, #category as VARCHAR(75))
RETURNS VARCHAR(1000) AS
BEGIN
DECLARE #returnData VARCHAR(1000)
DECLARE #currentData VARCHAR(75)
DECLARE dataCursor CURSOR FAST_FORWARD FOR
SELECT data FROM DNet.ejc_FormDetails WHERE formuid = #formuid AND category = #category
SET #returnData = ''
OPEN dataCursor
FETCH NEXT FROM dataCursor INTO #currentData
WHILE (##FETCH_STATUS = 0)
BEGIN
SET #returnData = #returnData + ', ' + #currentData
FETCH NEXT FROM dataCursor INTO #currentData
END
CLOSE dataCursor
DEALLOCATE dataCursor
RETURN SUBSTRING(#returnData,3,1000)
END
As you can see, I am selecting the column data within my function and then looping over the results with a cursor to build my comma separated varchar.
How can I alter this to accept a single parameter that is a result set and then access that result set with a cursor?
Others have answered your main question - but let me point out another problem with your function - the terrible use of a CURSOR!
You can easily rewrite this function to use no cursor, no WHILE loop - nothing like that. It'll be tons faster, and a lot easier, too - much less code:
FUNCTION DNet.ejc_concatFormDetails
(#formuid AS int, #category as VARCHAR(75))
RETURNS VARCHAR(1000)
AS
RETURN
SUBSTRING(
(SELECT ', ' + data
FROM DNet.ejc_FormDetails
WHERE formuid = #formuid AND category = #category
FOR XML PATH('')
), 3, 1000)
The trick is to use the FOR XML PATH('') - this returns a concatenated list of your data columns and your fixed ', ' delimiters. Add a SUBSTRING() on that and you're done! As easy as that..... no dogged-slow CURSOR, no messie concatenation and all that gooey code - just one statement and that's all there is.
You can use table-valued parameters:
CREATE FUNCTION MyFunction(
#Data AS TABLE (
Column1 int,
Column2 nvarchar(50),
Column3 datetime
)
)
RETURNS NVARCHAR(MAX)
AS BEGIN
/* here you can do what you want */
END
You can use Table Valued Parameters as of SQL Server 2008, which would allow you to pass a TABLE variable in as a parameter. The limitations and examples for this are all in that linked article.
However, I'd also point out that using a cursor could well be painful for performance.
You don't need to use a cursor, as you can do it all in 1 SELECT statement:
SELECT #MyCSVString = COALESCE(#MyCSVString + ', ', '') + data
FROM DNet.ejc_FormDetails
WHERE formuid = #formuid AND category = #category
No need for a cursor
Your question is a bit unclear. In your first SQL statement it looks like you're trying to pass columns to the function, but there is no WHERE clause. In the second SQL statement you're passing a collection of rows (results from a SELECT). Can you supply some sample data and expected outcome?
Without fully understanding your goal, you could look into changing the parameter to be a table variable. Fill a table variable local to the calling code and pass that into the function. You could do that as a stored procedure though and wouldn't need a function.