I am getting the follow error when trying to create the following function:
Error 1 SQL80001: Incorrect syntax near 'RETURN SELECT CAST( RAND(
#offset * #seed )* #max as int ) + 1'
What am I doing wrongly?
CREATE FUNCTION [dbo].[fn_RandomInt]( #offset int, #seed int, #max int )
RETURNS INT
AS
RETURN
SELECT CAST( RAND( #offset * #seed )* #max as int ) + 1
Is it possible to create a UDF that will accept a SEED into the Rand() function?
Correct syntax to create a function would be:
CREATE FUNCTION [dbo].[fn_RandomInt]( #offset int, #seed int, #max int )
RETURNS INT
AS
begin
RETURN CAST( RAND( #offset * #seed )* #max as int ) + 1
end
GO
But then you're going to get the error:
Msg 443, Level 16, State 1, Procedure fn_RandomInt, Line 5 Invalid use
of a side-effecting operator 'rand' within a function.
This is because you can't put indeterminate functions inside a UDF. Your functions can use views or procedures that use indeterminate functions though for some reason, so you could create a stored procedure with an output variable that returns the value of the rand function and use it. You could also just send the output of a rand function to the function and use it instead. Like:
select dbo.fn_RandomInt(10, rand(#seed), 20)
Also, if you're trying to get your function to return a random number between #offset and #max, then your formula will not work. You'd want something more like:
CAST(#offset + RAND(#seed) * (#offset-#max+1) as int)
You can get the reason it wont work with this syntax which will give you this error.
CREATE FUNCTION [dbo].[fn_RandomInt]( #offset int, #seed int, #max int )
RETURNS INT
AS
BEGIN
DECLARE #v_offset INT, #v_seed INT, #v_max INT, #result INT
SELECT #result = CAST(RAND( #v_offset * #v_seed ) * #v_max as int ) + 1
RETURN #result
END
It will give you this error -
Msg 443, Level 16, State 1, Procedure RandFn, Line 7
Invalid use of a side-effecting operator ‘rand’ within a function
You can look here for help but you will need to do something different either way since you want to feed RAND() arguments, so the view idea wouldn't work.
http://blog.sqlauthority.com/2012/11/20/sql-server-using-rand-in-user-defined-functions-udf/
Related
I'm asking this question for SQL Server 2008 R2
I'd like to know if there is a way to create multiple functions in a single batch statement.
I've made the following code as an example; suppose I want to take a character string and rearrange its letters in alphabetical order. So, 'Hello' would become 'eHllo'
CREATE FUNCTION char_split (#string varchar(max))
RETURNS #characters TABLE
(
chars varchar(2)
)
AS
BEGIN
DECLARE #length int,
#K int
SET #length = len(#string)
SET #K = 1
WHILE #K < #length+1
BEGIN
INSERT INTO #characters
SELECT SUBSTRING(#string,#K,1)
SET #K = #K+1
END
RETURN
END
CREATE FUNCTION rearrange (#string varchar(max))
RETURNS varchar(max)
AS
BEGIN
DECLARE #SplitData TABLE (
chars varchar(2)
)
INSERT INTO #SplitData SELECT * FROM char_split(#string)
DECLARE #Output varchar(max)
SELECT #Output = coalesce(#Output,' ') + cast(chars as varchar(10))
from #SplitData
order by chars asc
RETURN #Output
END
declare #string varchar(max)
set #string = 'Hello'
select dbo.rearrange(#string)
When I try running this code, I get this error:
'CREATE FUNCTION' must be the first statement in a query batch.
I tried enclosing each function in a BEGIN END block, but no luck. Any advice?
Just use a GO statement between the definition of the UDFs
Not doable. SImple like that.
YOu can make it is one statement using a GO between them.
But as the GO is a batch delimiter.... this means you send multiple batches, which is explicitly NOT Wanted in your question.
So, no - it is not possible to do that in one batch as the error clearly indicates.
I have defined a table-valued function X with 11 parameters. Their types are nvarchar(30), nvarchar(30), datetime, datetime, nvarchar(15), nvarchar(4), xml, nvarchar(8), nvarchar(80), bit, and bit respectively. This is for Microsoft SQL Server 2012. When I call the function with
select * from
X('A','B','2014-01-01','2014-12-31',null,null,'<C><D>E</D></C>',null,null,1,0)
I run into this error:
Parameters were not supplied for the function X
It is apparently different from the following two:
An insufficient number of arguments were supplied for the procedure or function X
Procedure or function X has too many arguments specified.
Is this related to two of the intended parameter values being null? How can I overcome the problem and define/call a table-valued function such as this one with 11 parameters, some of which may carry null?
UPDATE The problem remains if I pass in arbitrary strings instead of null. So there must be another (perhaps stupid) mistake.
The correct way to define a function like the one you describe is the following:
CREATE FUNCTION X
(
-- Add the parameters for the function here
#a nvarchar(30),
#b nvarchar(30),
#c datetime,
#d datetime,
#e nvarchar(15),
#f nvarchar(4),
#g xml,
#h nvarchar(8),
#i nvarchar(80),
#j bit,
#k bit
)
RETURNS
#output TABLE
(
-- Add the column definitions for the TABLE variable here
data nvarchar(250)
)
AS
BEGIN
INSERT INTO #output (data)
VALUES (#a + #b)
RETURN
END
GO
Given the above definition, this:
select * from
X('A','B','2014-01-01','2014-12-31',null,null,'<C><D>E</D></C>',null,null,1,0)
yields the following result:
data
----
AB
If your function does not need any parameters, but you are still getting this error:
Parameters were not supplied for the function
select * from [dbo].[myfunction]
Try adding () after the function call:
select * from [dbo].[myfunction]()
I have made the following scalar valued function
CREATE FUNCTION [dbo].[CountCustomers]
(
#Name varchar
)
RETURNS int
AS
BEGIN
DECLARE #Result int
SET #Result = 1
RETURN #Result
END
But when I am calling it as
SELECT dbo.CountCustomers
I am not able to do it.
You defined a parameter for your function (#Name), so call the function with a value for that parameter:
SELECT dbo.CountCustomers('foo')
You need to pass a value into your function such as this:
SELECT dbo.CountCustomers('name')
The reason is you have #Name varchar as a parameter.
You can also call it like this:
DECLARE #Count INT
SET #Count = dbo.CountCustomers('name')
SELECT #Count
You need to add parentheses to call the function, and then add in one parameter in order for it to work.
SELECT dbo.CountCustomers('hello world')
I'm trying to use sql:variable to read data from a XML file. My problem is, I can read the first or n^th line (or node) of the XML, however I can't make iteration in the lines (or nodes). Here is where I used sql:variable:
CAST((SELECT #xmlExpenseItems.value(N'(/ExpenseItem//ExpenseID/node())[sql:variable("#iteratorVarChar")]','int')) AS int),
CAST((SELECT #xmlExpenseItems.value(N'(/ExpenseItem//ExpenseTypeID/node())[sql:variable("#iteratorVarChar")]','int')) AS int),
CAST((SELECT #xmlExpenseItems.value(N'(/ExpenseItem//ExpenseAmount/node())[sql:variable("#iteratorVarChar")]','float')) AS float)
where #iteratorVarChar is a varchar casted from an int.
I get the error "XQuery [value()]: Only 'http://www.w3.org/2001/XMLSchema#decimal?', 'http://www.w3.org/2001/XMLSchema#boolean?' or 'node()*' expressions allowed as predicates, found 'xs:string ?'" at the first line of the code above.
When I switched #iteratorVarChar with #iterator which is already an int, I get "XQuery [value()]: 'value()' requires a singleton (or empty sequence), found operand of type 'xdt:untypedAtomic *'"
As I said, when I replace sql:variable("#iteratorVarChar") with an int, like 1, then the code works with the first node of the xml.
My question is, am I missing something or am I making a fundamental mistake? How to make this work?
My whole code is below (I commented out the CREATE in order to avoid the recreation errors):
DECLARE #xmlExpenseItems XML
SET #xmlExpenseItems = '
<ExpenseItem>
<ExpenseID>5</ExpenseID>
<ExpenseTypeID>5</ExpenseTypeID>
<ExpenseAmount>5</ExpenseAmount>
</ExpenseItem>
<ExpenseItem>
<ExpenseID>3</ExpenseID>
<ExpenseTypeID>5</ExpenseTypeID>
<ExpenseAmount>7</ExpenseAmount>
</ExpenseItem>
'
--CREATE TABLE #ExpenseItems
--(ExpenseItemID int not null identity(1,1),
--ExpenseID int not null,
--ExpenseTypeID int not null,
--ExpenseAmount float not null
--)
DECLARE #iterator int = 1
DECLARE #IDCount int
SELECT #IDCount = (SELECT #xmlExpenseItems.value('count(/ExpenseItem)', 'int') )
DECLARE #iteratorVarChar varchar(3)
WHILE #iterator <= #IDCount
BEGIN
SET #iteratorVarChar = CAST(#iterator AS varchar(3))
INSERT INTO #ExpenseItems
(ExpenseID, ExpenseTypeID, ExpenseAmount)
VALUES
(
CAST((SELECT #xmlExpenseItems.value(N'(/ExpenseItem//ExpenseID/node())[sql:variable("#iteratorVarChar")]','int')) AS int),
CAST((SELECT #xmlExpenseItems.value(N'(/ExpenseItem//ExpenseTypeID/node())[sql:variable("#iteratorVarChar")]','int')) AS int),
CAST((SELECT #xmlExpenseItems.value(N'(/ExpenseItem//ExpenseAmount/node())[sql:variable("#iteratorVarChar")]','float')) AS float)
)
SET #iterator = #iterator + 1
END
select * from #ExpenseItems
Try a set based approach instead of iteration. The nodes() function returns a rowset from an XML document:
insert #ExpenseItems
(ExpenseID, ExpenseTypeID, ExpenseAmount)
select col.value('ExpenseID[1]', 'int')
, col.value('ExpenseTypeID[1]', 'int')
, col.value('ExpenseAmount[1]', 'int')
from #xmlExpenseItems.nodes('/ExpenseItem') doc(col)
I keep getting this error: Any Ideas?
Invalid use of side-effecting or time-dependent operator in 'newid' within a function.
I am working with MS-SQL Server 2005. Here is the T-SQL statement:
Create Function [dbo].[GetNewNumber]( )
RETURNS int
AS
BEGIN
Declare #code int
set #code = (SELECT CAST(CAST(newid() AS binary(3)) AS int) )
RETURN (#code)
END
You can't use NEWID() within a function.
A usual workaround (in my experience, it's more been the need for GETDATE()) is to have to pass it in:
Create Function [dbo].[GetNewNumber](#newid UNIQUEIDENTIFIER )
RETURNS int
AS
BEGIN
Declare #code int
set #code = (SELECT CAST(CAST(#newid AS binary(3)) AS int) )
RETURN (#code)
END
And call it like:
SELECT dbo.GetNewNumber(NEWID())
The function will not let you use the NewID, but this can be worked around.
Create View vwGetNewNumber
as
Select Cast(Cast(newid() AS binary(3)) AS int) as NextID
Create Function [dbo].[GetNewNumber] ( ) RETURNS int
AS
BEGIN
Declare #code int
Select top 1 #code=NextID from vwGetNewNumber
RETURN (#code)
END
Then you can use select dbo.[GetNewNumber]() as planned.
Does this have to be done with a function call? Whenever I needs this functionality, I just use:
checksum(newid())
This will generate negative numbers -- if they must be positive, you could use
abs(checksum(newid()))