cipriano555
asked on
How to turn a query into a Function
I need to generate random data. I have some that will generate one of the 5 FACILITY_CODEs at random:
Declare @RAND as float
Select @RAND = 5*RAND(CAST( NEWID() AS varbinary ))
;with CTE_FAC(facility_code, CUM_WEIGHT)
AS
(
select facility_code, CUM_WEIGHT from DBO.dist_facility where cum_weight > @RAND
)
SELECT TOP 1 facility_code FROM CTE_FAC ORDER BY CUM_WEIGHT ASC
It would be nice to package this into a neat little function so I can do something like this
Select RAND_FACILITY
How to wrap the above logic into a user defined function?
Thanks,
Cipriano
Declare @RAND as float
Select @RAND = 5*RAND(CAST( NEWID() AS varbinary ))
;with CTE_FAC(facility_code, CUM_WEIGHT)
AS
(
select facility_code, CUM_WEIGHT from DBO.dist_facility where cum_weight > @RAND
)
SELECT TOP 1 facility_code FROM CTE_FAC ORDER BY CUM_WEIGHT ASC
It would be nice to package this into a neat little function so I can do something like this
Select RAND_FACILITY
How to wrap the above logic into a user defined function?
Thanks,
Cipriano
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
I also tried this and it had a sytax error as well
CREATE FUNCTION RAND_FACILITY
()
RETURNS VARCHAR(50)
AS
BEGIN
DECLARE @ResultVar as VARCHAR(50)
Declare @RAND as float
Select @RAND = 5*RAND(CAST( NEWID() AS varbinary ))
;with CTE_FAC(facility_code, CUM_WEIGHT)
AS
(
select facility_code, CUM_WEIGHT from DBO.dist_facility where cum_weight > @RAND
)
RETURN SELECT TOP 1 facility_code FROM CTE_FAC ORDER BY CUM_WEIGHT ASC
END
GO
CREATE FUNCTION RAND_FACILITY
()
RETURNS VARCHAR(50)
AS
BEGIN
DECLARE @ResultVar as VARCHAR(50)
Declare @RAND as float
Select @RAND = 5*RAND(CAST( NEWID() AS varbinary ))
;with CTE_FAC(facility_code, CUM_WEIGHT)
AS
(
select facility_code, CUM_WEIGHT from DBO.dist_facility where cum_weight > @RAND
)
RETURN SELECT TOP 1 facility_code FROM CTE_FAC ORDER BY CUM_WEIGHT ASC
END
GO
ASKER
This runs and returns one random (according to my probability function) facility code:
Declare @RAND as float
Select @RAND = 5*RAND(CAST( NEWID() AS varbinary ))
;with CTE_FAC(facility_code, CUM_WEIGHT)
AS
(
select facility_code, CUM_WEIGHT from DBO.dist_facility where cum_weight > @RAND
)
SELECT TOP 1 facility_code FROM CTE_FAC ORDER BY CUM_WEIGHT ASC
Declare @RAND as float
Select @RAND = 5*RAND(CAST( NEWID() AS varbinary ))
;with CTE_FAC(facility_code, CUM_WEIGHT)
AS
(
select facility_code, CUM_WEIGHT from DBO.dist_facility where cum_weight > @RAND
)
SELECT TOP 1 facility_code FROM CTE_FAC ORDER BY CUM_WEIGHT ASC
Sorry. Was a little too rushed earlier. Please try function code below:
SELECT ..., dbo.RAND_FACILITY(NEWID())
FROM ...
....
SELECT ..., dbo.RAND_FACILITY(NEWID())
FROM ...
....
CREATE FUNCTION RAND_FACILITY (
@rand uniqueidentifier
)
RETURNS VARCHAR(50)
AS
BEGIN
RETURN (
SELECT TOP 1
facility_code
FROM
DBO.dist_facility
WHERE
cum_weight > @RAND
ORDER BY
CUM_WEIGHT ASC
)
END --FUNCTION
GO
ASKER
This error message was generated:
Msg 206, Level 16, State 2, Procedure RAND_FACILITY, Line 7
Operand type clash: uniqueidentifier is incompatible with float
It doesn't like this line: cum_weight > @RAND
I don't see how @RAND is a random number betweeen 0 and 5.
Msg 206, Level 16, State 2, Procedure RAND_FACILITY, Line 7
Operand type clash: uniqueidentifier is incompatible with float
It doesn't like this line: cum_weight > @RAND
I don't see how @RAND is a random number betweeen 0 and 5.
ASKER
Given that the code below works correctly...
Declare @RAND as float
Select @RAND = 5*RAND(CAST( NEWID() AS varbinary ))
;with CTE_FAC(facility_code, CUM_WEIGHT)
AS
(
select facility_code, CUM_WEIGHT from DBO.dist_facility where cum_weight > @RAND
)
SELECT TOP 1 facility_code FROM CTE_FAC ORDER BY CUM_WEIGHT ASC
... I think my main problem is I don't know how to take the result from that last select statement and make it be the return value of a function. Should be easy, something done all the time, but I can't get any syntax to work...
Declare @RAND as float
Select @RAND = 5*RAND(CAST( NEWID() AS varbinary ))
;with CTE_FAC(facility_code, CUM_WEIGHT)
AS
(
select facility_code, CUM_WEIGHT from DBO.dist_facility where cum_weight > @RAND
)
SELECT TOP 1 facility_code FROM CTE_FAC ORDER BY CUM_WEIGHT ASC
... I think my main problem is I don't know how to take the result from that last select statement and make it be the return value of a function. Should be easy, something done all the time, but I can't get any syntax to work...
Sorry. Try this:
...
cum_weight > 5 * RAND(CAST(@rand AS varbinary))
...
The big thing is to make sure SQL re-evaluates the function for every row; that's why I'm using NEWID() as the input param -- SQL "understands" that this value changes every time.
...
cum_weight > 5 * RAND(CAST(@rand AS varbinary))
...
The big thing is to make sure SQL re-evaluates the function for every row; that's why I'm using NEWID() as the input param -- SQL "understands" that this value changes every time.
ASKER
This also works and seems a step closer...
Declare @RAND as float
DECLARE @FACILITY_CODE AS VARCHAR(50)
Select @RAND = 5*RAND(CAST( NEWID() AS varbinary ))
;with CTE_FAC(facility_code, CUM_WEIGHT)
AS
(
select facility_code, CUM_WEIGHT from DBO.dist_facility where cum_weight > @RAND
),
CTE_FAC2
AS
(
SELECT TOP 1 facility_code FROM CTE_FAC ORDER BY CUM_WEIGHT ASC
)
SELECT @FACILITY_CODE = FACILITY_CODE FROM CTE_FAC2
SELECT @FACILITY_CODE
Declare @RAND as float
DECLARE @FACILITY_CODE AS VARCHAR(50)
Select @RAND = 5*RAND(CAST( NEWID() AS varbinary ))
;with CTE_FAC(facility_code, CUM_WEIGHT)
AS
(
select facility_code, CUM_WEIGHT from DBO.dist_facility where cum_weight > @RAND
),
CTE_FAC2
AS
(
SELECT TOP 1 facility_code FROM CTE_FAC ORDER BY CUM_WEIGHT ASC
)
SELECT @FACILITY_CODE = FACILITY_CODE FROM CTE_FAC2
SELECT @FACILITY_CODE
ASKER
This generates an error:
CREATE FUNCTION RAND_FACILITY (
)
RETURNS VARCHAR(50)
AS
BEGIN
Declare @RAND as float
DECLARE @FACILITY_CODE AS VARCHAR(50)
Select @RAND = 5*RAND(CAST( NEWID() AS varbinary ))
;with CTE_FAC(facility_code, CUM_WEIGHT)
AS
(
select facility_code, CUM_WEIGHT from DBO.dist_facility where cum_weight > @RAND
),
CTE_FAC2
AS
(
SELECT TOP 1 facility_code FROM CTE_FAC ORDER BY CUM_WEIGHT ASC
)
SELECT @FACILITY_CODE = FACILITY_CODE FROM CTE_FAC2
RETURN(@FACILITY_CODE)
END;
Msg 443, Level 16, State 1, Procedure RAND_FACILITY, Line 9
Invalid use of a side-effecting operator 'newid' within a function.
Msg 443, Level 16, State 1, Procedure RAND_FACILITY, Line 11
Invalid use of a side-effecting operator 'rand' within a function.
CREATE FUNCTION RAND_FACILITY (
)
RETURNS VARCHAR(50)
AS
BEGIN
Declare @RAND as float
DECLARE @FACILITY_CODE AS VARCHAR(50)
Select @RAND = 5*RAND(CAST( NEWID() AS varbinary ))
;with CTE_FAC(facility_code, CUM_WEIGHT)
AS
(
select facility_code, CUM_WEIGHT from DBO.dist_facility where cum_weight > @RAND
),
CTE_FAC2
AS
(
SELECT TOP 1 facility_code FROM CTE_FAC ORDER BY CUM_WEIGHT ASC
)
SELECT @FACILITY_CODE = FACILITY_CODE FROM CTE_FAC2
RETURN(@FACILITY_CODE)
END;
Msg 443, Level 16, State 1, Procedure RAND_FACILITY, Line 9
Invalid use of a side-effecting operator 'newid' within a function.
Msg 443, Level 16, State 1, Procedure RAND_FACILITY, Line 11
Invalid use of a side-effecting operator 'rand' within a function.
ASKER
This works, but it's not a function so it can't be selected....
CREATE PROC RAND_FACILITY
AS
BEGIN
Declare @RAND as float
DECLARE @FACILITY_CODE AS VARCHAR(50)
Select @RAND = 5*RAND(CAST( NEWID() AS varbinary ))
;with CTE_FAC(facility_code, CUM_WEIGHT)
AS
(
select facility_code, CUM_WEIGHT from DBO.dist_facility where cum_weight > @RAND
),
CTE_FAC2
AS
(
SELECT TOP 1 facility_code FROM CTE_FAC ORDER BY CUM_WEIGHT ASC
)
SELECT FACILITY_CODE FROM CTE_FAC2
END;
exec rand_facility
CREATE PROC RAND_FACILITY
AS
BEGIN
Declare @RAND as float
DECLARE @FACILITY_CODE AS VARCHAR(50)
Select @RAND = 5*RAND(CAST( NEWID() AS varbinary ))
;with CTE_FAC(facility_code, CUM_WEIGHT)
AS
(
select facility_code, CUM_WEIGHT from DBO.dist_facility where cum_weight > @RAND
),
CTE_FAC2
AS
(
SELECT TOP 1 facility_code FROM CTE_FAC ORDER BY CUM_WEIGHT ASC
)
SELECT FACILITY_CODE FROM CTE_FAC2
END;
exec rand_facility
ASKER
Scott: I tried this, but got the error below
CREATE FUNCTION RAND_FACILITY3 (
@rand uniqueidentifier
)
RETURNS VARCHAR(50)
AS
BEGIN
RETURN (
SELECT TOP 1
facility_code
FROM
DBO.dist_facility
WHERE
cum_weight > 5*RAND(CAST(@rand AS varbinary))
ORDER BY
CUM_WEIGHT ASC
)
END --FUNCTION
Msg 443, Level 16, State 1, Procedure RAND_FACILITY3, Line 15
Invalid use of a side-effecting operator 'rand' within a function.
CREATE FUNCTION RAND_FACILITY3 (
@rand uniqueidentifier
)
RETURNS VARCHAR(50)
AS
BEGIN
RETURN (
SELECT TOP 1
facility_code
FROM
DBO.dist_facility
WHERE
cum_weight > 5*RAND(CAST(@rand AS varbinary))
ORDER BY
CUM_WEIGHT ASC
)
END --FUNCTION
Msg 443, Level 16, State 1, Procedure RAND_FACILITY3, Line 15
Invalid use of a side-effecting operator 'rand' within a function.
You cannot use a non-deterministic function in a UDF. Both NEWID and RAND are non-determinsitic.
If you notice Scott is passing in those values (NEWID() and/or RAND()) which sidesteps the whole problem. You should be able to do the same.
ASKER
ok, this works...
CREATE FUNCTION [dbo].[RAND_FACILITY] (
@RAND AS FLOAT
)
RETURNS VARCHAR(50)
AS
BEGIN
DECLARE @FACILITY_CODE AS VARCHAR(50)
;with CTE_FAC(facility_code, CUM_WEIGHT)
AS
(
select facility_code, CUM_WEIGHT from DBO.dist_facility where cum_weight > @RAND
),
CTE_FAC2
AS
(
SELECT TOP 1 facility_code FROM CTE_FAC ORDER BY CUM_WEIGHT ASC
)
SELECT @FACILITY_CODE = FACILITY_CODE FROM CTE_FAC2
RETURN(@FACILITY_CODE)
END;
SELECT [ManagedCareDemo].[dbo].[R AND_FACILI TY] ((SELECT SUM(WEIGHT) FROM DIST_FACILITY)*RAND(CAST( NEWID() AS varbinary )
CREATE FUNCTION [dbo].[RAND_FACILITY] (
@RAND AS FLOAT
)
RETURNS VARCHAR(50)
AS
BEGIN
DECLARE @FACILITY_CODE AS VARCHAR(50)
;with CTE_FAC(facility_code, CUM_WEIGHT)
AS
(
select facility_code, CUM_WEIGHT from DBO.dist_facility where cum_weight > @RAND
),
CTE_FAC2
AS
(
SELECT TOP 1 facility_code FROM CTE_FAC ORDER BY CUM_WEIGHT ASC
)
SELECT @FACILITY_CODE = FACILITY_CODE FROM CTE_FAC2
RETURN(@FACILITY_CODE)
END;
SELECT [ManagedCareDemo].[dbo].[R
ASKER
acperkins: OK, I get it, thanks for the clarification. I Scott's original solution I couldn't see where any random value was generated. the point is, you can't do that in a UDF, so you generate it in the query and pass it in as an argument. I'm used to "normal" programming languages where this kind of restriction wouldn't occur.
Scott: thanks for your help.
Scott: thanks for your help.
ASKER
Oh....and now that I'm done I'll answer my own question:
Q. How do you turn a query into a function?
A. We assume you have some code that ends with a select statement that returns a scalar: "VAL" This gives the idea:
CREATE FUNCTION F1 (@ARG1 AS FLOAT)
AS BEGIN
DECLARE @RETURNVAL AS VARCHAR(50)
...other code...
SELECT @RETURNVAL = VAL FROM TABLE1
RETURN(@RETURNVAL)
END
Then you can do this:
SELECT F1(4.00)
The code in the BEGIN block cannot have any non-deterministic functions in it, such as RAND, so if you need a random number, you'll pass it in as an input argument.
Q. How do you turn a query into a function?
A. We assume you have some code that ends with a select statement that returns a scalar: "VAL" This gives the idea:
CREATE FUNCTION F1 (@ARG1 AS FLOAT)
AS BEGIN
DECLARE @RETURNVAL AS VARCHAR(50)
...other code...
SELECT @RETURNVAL = VAL FROM TABLE1
RETURN(@RETURNVAL)
END
Then you can do this:
SELECT F1(4.00)
The code in the BEGIN block cannot have any non-deterministic functions in it, such as RAND, so if you need a random number, you'll pass it in as an input argument.
No problem.
Sorry, I was in a hurry and scrambled it up anyway.
Now that I think about it, probably best to use a table-valued function that returns the result set you need; you just pass in a parameter of 5 * RAND(CAST(NEWID() AS varbinary)) to the function.
Sorry, I was in a hurry and scrambled it up anyway.
Now that I think about it, probably best to use a table-valued function that returns the result set you need; you just pass in a parameter of 5 * RAND(CAST(NEWID() AS varbinary)) to the function.
>>I'm used to "normal" programming languages where this kind of restriction wouldn't occur.<<
If you have time try reading up on T-SQL; the restriction is in there for a reason, there is nothing arbitrary about it.
If you have time try reading up on T-SQL; the restriction is in there for a reason, there is nothing arbitrary about it.
ASKER
My question is one of syntax. I want to populate a table of data using the law reflected in the code I wrote. I wanted a function so I can select it in a query. Your solution will not return a FACILITY_CODE so I can't use it, and it doesn't use the distribution function (which I didn't show, it is numbers in a table.) Here is an example of something I tried, but it won't run:
CREATE FUNCTION RAND_FACILITY
()
RETURNS VARCHAR(50)
AS
BEGIN
DECLARE @ResultVar as VARCHAR(50)
Declare @RAND as float
Select @RAND = 5*RAND(CAST( NEWID() AS varbinary ))
;with CTE_FAC(facility_code, CUM_WEIGHT)
AS
(
select facility_code, CUM_WEIGHT from DBO.dist_facility where cum_weight > @RAND
)
SELECT @ResultVar = TOP 1 facility_code FROM CTE_FAC ORDER BY CUM_WEIGHT ASC
RETURN @ResultVar
END
GO