SQLServerCentral is supported by Red Gate Software Ltd.
 
Log in  ::  Register  ::  Not logged in
Search:  
 
 
        
Home       Members    Calendar    Who's On


123»»»

Select statements included within a function cannot return data to a client Expand / Collapse
Author
Message
Posted Friday, January 02, 2009 9:05 PM
Grasshopper

GrasshopperGrasshopperGrasshopperGrasshopperGrasshopperGrasshopperGrasshopperGrasshopper

Group: General Forum Members
Last Login: Wednesday, March 18, 2009 8:38 PM
Points: 23, Visits: 42
Hi

I'm new to SS. The function below fails to compile, and returns the following error message:
Msg 444, Level 16, State 2, Procedure WKLY_SMA, Line 55
Select statements included within a function cannot return data to a client.

Issue lies with the FETCH RELATIVE -1 line. If I comment out the FETCH RELATIVE -1 line this compiles.

USE [NB_TRD_SYS]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION [dbo].[WKLY_SMA]
(
-- Add the parameters for the function here
@TimeKey int,
@Commodity nvarchar(3),
@Period int
)
RETURNS int
AS
BEGIN
DECLARE @SMA FLOAT -- return variable
DECLARE @MA FLOAT
DECLARE @n INT
DECLARE @C_TKey int
DECLARE @C_CS nvarchar(3)
DECLARE @C_CLS FLOAT
DECLARE @C_First_TKey int
DECLARE @C_First_CS nvarchar(3)
DECLARE @C_First_CLS FLOAT

DECLARE SMA_CURSOR CURSOR SCROLL STATIC FOR -- Tried different types of cursors as the issue is with FETCH RELATIVE...
SELECT
TIME_KEY,
COMM_SYMB,
CLS
FROM dbo.fct_WKLY
WHERE
COMM_SYMB = @Commodity
AND
TIME_KEY <= @TimeKey

OPEN SMA_CURSOR

FETCH FIRST FROM SMA_CURSOR INTO @C_First_TKey, @C_First_CS, @C_First_CLS
FETCH LAST FROM SMA_CURSOR INTO @C_TKey, @C_CS, @C_CLS -- How do I know that my cursor has remained here?

SET @n = 0
SET @MA = 0
SET @SMA = 0

WHILE @n < @Period BEGIN
IF (@C_First_TKey = @C_TKey AND @C_First_CS = @C_CS)
RETURN @SMA
ELSE BEGIN
SET @MA = @MA + @C_CLS
SET @n = @n + 1
FETCH RELATIVE -1 FROM SMA_CURSOR -- Hopefully this is fetching the previous from the LAST.
END
END

SET @SMA = @MA / @Period

CLOSE SMA_CURSOR
DEALLOCATE SMA_CURSOR

-- Return the result of the function
RETURN @SMA

END
GO

Thanks in advance,
Wayne
Post #629198
Posted Friday, January 02, 2009 9:14 PM


Ten Centuries

Ten CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen Centuries

Group: General Forum Members
Last Login: Today @ 12:42 PM
Points: 1,264, Visits: 1,395
Can you also post the table structure with sample data and the error you're getting.

This site should help you in posting the required solution.

www.sqlservercentral.com/articles/Best+Practices/61537/





Pradeep Singh
Post #629199
Posted Saturday, January 03, 2009 12:05 AM
Ten Centuries

Ten CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen CenturiesTen Centuries

Group: General Forum Members
Last Login: 2 days ago @ 3:37 AM
Points: 1,086, Visits: 1,210
Hello,

To quote BOL (Topic “Creating User-defined Functions“): “Only FETCH statements that assign values to local variables using the INTO clause are allowed.”

Hope that helps.

Regards,

John Marsh


www.sql.lu
SQL Server Luxembourg User Group
Post #629220
Posted Saturday, January 03, 2009 4:09 AM
SSCommitted

SSCommittedSSCommittedSSCommittedSSCommittedSSCommittedSSCommittedSSCommittedSSCommitted

Group: General Forum Members
Last Login: Monday, June 22, 2009 4:48 AM
Points: 1,759, Visits: 3,754
Hi Waykna

There's no ORDER BY clause in the query - so FIRST and LAST will be indiscriminate. In any case, looking at the code, I'm pretty sure that this can be accomplished far more simply without the cursor.
What I suggest you do is to show a couple of examples of the data which the query returns. Include at least one dataset where IF (@C_First_TKey = @C_TKey AND @C_First_CS = @C_CS) holds true, and where it doesn't. Check the link in my sig for instructions how to prepare the data as insert statements.

Cheers

ChrisM


Low-hanging fruit picker

For better assistance in answering your questions, please read this.
Post #629246
Posted Saturday, January 03, 2009 7:26 AM


SSChampion

SSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampionSSChampion

Group: General Forum Members
Last Login: Today @ 1:26 PM
Points: 12,626, Visits: 8,658
What's this function supposed to do?
I'd offer help in getting rid of the cursor, but I have absolutely no idea what it's doing.



Gail Shaw

We walk in the dark places no others will enter
We stand on the bridge and none may pass

Post #629260
Posted Saturday, January 03, 2009 9:47 AM
Grasshopper

GrasshopperGrasshopperGrasshopperGrasshopperGrasshopperGrasshopperGrasshopperGrasshopper

Group: General Forum Members
Last Login: Wednesday, March 18, 2009 8:38 PM
Points: 23, Visits: 42
Here's a sample of the data being selected into the cursor

TIME_KEY COMM_SYMB CLOSE
16538 CL 62.03
16539 CL 64.09
16540 CL 62.41
16541 CL 61.05
16542 CL 56.31
16543 CL 52.99
16544 CL 53.47
16545 CL 55.42
16546 CL 59.02
16547 CL 59.89
16548 CL 59.86

What you have is a surrogate key for a weekly time dimension, a commodity symbol for crude oil, and the weekly close. The function is supposed to calculate a running simple moving average where the window (number of periods) is parameterized. I have successfully done this is MS Access & VB. The error msg is in my first posting.
Thx,
Post #629284
Posted Saturday, January 03, 2009 9:52 AM
Grasshopper

GrasshopperGrasshopperGrasshopperGrasshopperGrasshopperGrasshopperGrasshopperGrasshopper

Group: General Forum Members
Last Login: Wednesday, March 18, 2009 8:38 PM
Points: 23, Visits: 42
Hi Gail

It derives a simple moving average. I have successfully implemented this in Access & VB and have been trying to accomplish the equivalent in SS.

The original VB code looks like this:
Public Function SMA_WKLY(Commodity, WkEndDt, period As Integer)


Dim rst As DAO.Recordset
Dim sql As String
Dim ma As Double
Dim n As Integer

sql = "SELECT [COMM_SYMB], [PRD_END_DT], [CLOSE] FROM fct_WKLY" & _
" WHERE [fct_WKLY]![COMM_SYMB] ='" & Commodity & "'" & _
" AND [fct_WKLY]![PRD_END_DT] <= #" & WkEndDt & "#" & _
" ORDER BY [fct_WKLY]![PRD_END_DT];"

Set rst = CurrentDb.OpenRecordset(sql)
rst.MoveLast
For n = 0 To period - 1
If rst.BOF Then
SMA_WKLY = 0
Exit Function
Else
ma = ma + rst.Fields("CLOSE")
End If
rst.MovePrevious
Next n
rst.Close
SMA_WKLY = ma / period

End Function

What's great about this is that any number of commodities and any number of periods dynamically. As data is added to the source table, it simply picks up where you left off and derives the new simple moving averages.

Thx,
Wayne
Post #629287
Posted Saturday, January 03, 2009 10:18 AM
Grasshopper

GrasshopperGrasshopperGrasshopperGrasshopperGrasshopperGrasshopperGrasshopperGrasshopper

Group: General Forum Members
Last Login: Wednesday, March 18, 2009 8:38 PM
Points: 23, Visits: 42
Hi Chris

I omitted the ORDER BY, given that there's an index on the two PK fields being selected.
If this can be accomplished without a cursor that'd be great as I've read that they are a "last resort" and are generally inefficient.

I've responded to another reply by posting the entire VB function that I'm trying to reproduce - I'm migrating from MS access to SS 2008 Express.

Regarding the If statement, I'm trying to simulate the VB beginning of file (BOF)
If rst.BOF Then
SMA_WKLY = 0 (the moving average is zero)

Here's an excerpt from the table with a sample (only showing one commodity crude oil), I collect data for 50 commodities per day, with a 5-day simple moving average SMA_%

TIME_KEY COMM_SYMB TRD_DAY OPEN CLOSE SMA_5
703 CL 2006-12-04 63.81 62.44 0.00
704 CL 2006-12-05 62.42 62.43 0.00
705 CL 2006-12-06 62.42 62.19 0.00
706 CL 2006-12-07 62.31 62.49 0.00
707 CL 2006-12-08 62.75 62.03 62.32
710 CL 2006-12-11 62.21 61.22 62.07
711 CL 2006-12-12 61.3 61.02 61.79
712 CL 2006-12-13 61.85 62.17 61.79
713 CL 2006-12-14 62.23 63.33 61.95
714 CL 2006-12-15 63.37 64.09 62.37
717 CL 2006-12-18 63.62 62.79 62.68
718 CL 2006-12-19 62.73 63.46 63.17
719 CL 2006-12-20 63.3 63.72 63.48
720 CL 2006-12-21 63.58 62.66 63.34


In access I simply pass the TIME_KEY, COMM_SYMB, and period to the function and it derives and returns the SMA for that row. I call it from a simple select statement.
Eg. SELECT fct_WKLY.TIME_KEY, fct_WKLY.COMM_SYMB, SMA_WKLY([COMM_SYMB],[TIME_KEY],5) AS DSMA_5

I can use the same function (SMA_WKLY) to derive any period of SMA.

Thx,
Wayne



Post #629297
Posted Saturday, January 03, 2009 10:41 AM
SSCommitted

SSCommittedSSCommittedSSCommittedSSCommittedSSCommittedSSCommittedSSCommittedSSCommitted

Group: General Forum Members
Last Login: Monday, June 22, 2009 4:48 AM
Points: 1,759, Visits: 3,754
So, of the rows which are returned by the query, you use only the most recent @Period to derive the moving average?

Try the following. It's untested btw...

SELECT SUM(CLS), COUNT(*), -- <-- these two output columns appear here only for testing
CASE WHEN COUNT(*) > 0
THEN SUM(CLS) / (COUNT(*)*1.00)
ELSE 0 END AS MovingAvg
FROM (
SELECT TOP (@Period) TIME_KEY, COMM_SYMB, CLS
FROM dbo.fct_WKLY
WHERE COMM_SYMB = @Commodity AND TIME_KEY <= @TimeKey
ORDER BY TIME_KEY
) d

If this works, then the result can be streamed into a variable within the outer select, and you're done.


Low-hanging fruit picker

For better assistance in answering your questions, please read this.
Post #629305
Posted Saturday, January 03, 2009 10:54 AM


SSCertifiable

SSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiableSSCertifiable

Group: General Forum Members
Last Login: Today @ 12:12 PM
Points: 7,708, Visits: 6,917
Using a function like this in SQL at all in a query could lead to it's own performance problems, but this version is much faster, and about a zillion time simpler than either the SQL Cursors or even the Access/VB versions:
CREATE FUNCTION [dbo].[WKLY_SMA](
@TimeKey int,
@Commodity nvarchar(3),
@Period int)
RETURNS Float AS --was int?
BEGIN
DECLARE @SMA FLOAT -- return variable

SELECT @SMA = Sum(CLS) / Count(CLS)
-- correctly handles missing values and the startup window
FROM dbo.fct_WKLY
WHERE COMM_SYMB = @Commodity
AND TIME_KEY BETWEEN @TimeKey-@Period+1 and @TimeKey

-- Return the result of the function
RETURN @SMA
END



-- RBarryYoung, (302)375-0451 blog: MovingSQL.com, Twitter: @RBarryYoung
Proactive Performance Solutions, Inc.
"Performance is our middle name."
Post #629306
« Prev Topic | Next Topic »

123»»»

Permissions Expand / Collapse