Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 SQL Server 2000 Forums
 SQL Server Development (2000)
 Funky Output Parameter Behavior

Author  Topic 

Onamuji
Aged Yak Warrior

504 Posts

Posted - 2002-04-04 : 14:22:05
Ok, so I don't know if this is an ADO issue or a SQL Server issue. Please just read on and lend any helpful thoughts. I'm running SQL Server 2000 and ADO 2.6.

Have a procedure that calls another procedure that produces output parameters.

exec report$operating_plan$criteria_text @classification output, @status output, @project_owner output, @team output, @department output

is called by another procedure where all the parameters for that procedure are passed through the the one above. Both procedures have output declare on their parameters.

I run SQL Profiler to watch it execute and copy the Replay code from there and execute it. I get back the correct results.

Classification Status Project Owner Team Department
------------------ ------ ------------- ---- ----------
Business Necessity All All All All

However in the ASP code I declare the parameters as adParamInputOutput. Because they pass in a CSV of primary keys. In this instance Classification was '3' going in, and should be that coming out. However it resorts back to '3' when it comes out. The code for all the SQL is too long but i'll post the primary functions.

' This executes a procedure, returns get rows and parameters are persisted
Public Function Execute(ByVal commandText, ByVal commandType, ByRef parameters)
Dim objCommand: Set objCommand = CreateCommand(commandText, commandType, parameters)
Dim objRecordset: Set objRecordset = Server.CreateObject("ADODB.Recordset")

Execute = Null
objRecordset.CacheSize = 20
objRecordset.CursorLocation = adUseClient
objRecordset.CursorType = adOpenStatic
objRecordset.LockType = adLockReadOnly

Call OpenConnection()
Set objCommand.ActiveConnection = m_objConnection
Call objRecordset.Open(objCommand)

If objRecordset.State = adStateOpen Then
If Not objRecordset.BOF And Not objRecordset.EOF Then
Execute = objRecordset.GetRows()
End If

Call objRecordset.Close()
End If

If IsArray(parameters) Then Call PersistParameters(objCommand, parameters)
Set objCommand.ActiveConnection = Nothing
Call CloseConnection()
Set objCommand = Nothing
Set objRecordset = Nothing
End Function

' Two functions used by the above
Private Function CreateCommand(ByVal commandText, ByVal commandType, ByRef parameters)
Dim objCommand: Set objCommand = Server.CreateObject("ADODB.Command")
objCommand.CommandText = commandText
objCommand.CommandType = commandType

If IsArray(parameters) Then
Dim objParameter

For Each objParameter In parameters
If IsObject(objParameter) And Not objParameter Is Nothing Then
Call objCommand.Parameters.Append(objParameter)
End If
Next
End If

Set CreateCommand = objCommand
Set objCommand = Nothing
End Function

Private Function PersistParameters(ByRef command, ByRef parameters)
Dim intLength: intLength = UBound(parameters)
Dim intPos

For intPos = 0 To intLength
With command.Parameters.Item(parameters(intPos).Name)
Set parameters(intPos) = CreateParameter(.Name, .Type, .Direction, .Size, .Value)
End With
Next
End Function

' The function that is called.
Public Function [report$operating_plan](ByRef classification, ByRef status, ByRef project_owner, ByRef team, ByRef department, ByVal start_date, ByVal team_report)
Dim db: Set db = New SqlDatabase
Dim parameters(6)

Set parameters(0) = CreateParameter("@classification", adVarChar, adParamInputOutput, 8000, classification)
Set parameters(1) = CreateParameter("@status", adVarChar, adParamInputOutput, 8000, status)
Set parameters(2) = CreateParameter("@project_owner", adVarChar, adParamInputOutput, 8000, project_owner)
Set parameters(3) = CreateParameter("@team", adVarChar, adParamInputOutput, 8000, team)
Set parameters(4) = CreateParameter("@department", adVarChar, adParamInputOutput, 8000, department)
Set parameters(5) = CreateParameter("@start_date", adDate, adParamInput, 8, start_date)
Set parameters(6) = CreateParameter("@team_report", adBoolean, adParamInput, 1, team_report)

[report$operating_plan] = db.Execute("report$operating_plan", adCmdStoredProc, parameters)
Set db = Nothing

classification = parameters(0).Value
status = parameters(1).Value
project_owner = parameters(2).Value
team = parameters(3).Value
department = parameters(4).Value
End Function

Now here is the best part. This works. Except when I try to pass a value that is not null or string len > 1. Here is the code for the one procedure that is having issues:

create procedure report$operating_plan$criteria_text
@classification varchar(8000) = null output,
@status varchar(8000) = null output,
@project_owner varchar(8000) = null output,
@team varchar(8000) = null output,
@department varchar(8000) = null output
as
set nocount on

declare @sql nvarchar(4000)

set @classification = isnull(@classification, '')
set @status = isnull(@status, '')
set @project_owner = isnull(@project_owner, '')
set @team = isnull(@team, '')
set @department = isnull(@department, '')

if len(@classification) < 1
begin
set @classification = 'All'
end
else
begin
set @sql = 'select name
from project_classifications
where project_classification_id in (select convert(int, value)
from dbo.csv_to_table(''' + @classification + '''))'
exec table_to_csv @sql, @classification output
end

if len(@status) < 1
begin
set @status = 'All'
end
else
begin
set @sql = 'select name
from project_statuses
where project_status_id in (select convert(int, value)
from dbo.csv_to_table(''' + @status + '''))'

exec table_to_csv @sql, @status output
end

if len(@project_owner) < 1
begin
set @project_owner = 'All'
end
else
begin
set @sql = 'select last_name + '', '' + first_name
from users
where user_id in (select convert(int, value)
from dbo.csv_to_table(''' + @project_owner + '''))'

exec table_to_csv @sql, @project_owner output
end

if len(@team) < 1
begin
set @team = 'All'
end
else
begin
set @sql = 'select name
from teams
where team_id in (select convert(int, value)
from dbo.csv_to_table(''' + @team + '''))'

exec table_to_csv @sql, @team output
end

if len(@department) < 1
begin
set @department = 'All'
end
else
begin
set @sql = 'select name
from departments
where department_id in (select convert(int, value)
from dbo.csv_to_table(''' + @department + '''))'

exec table_to_csv @sql, @department output
end
go

This code works fine in query analyzer. But in ASP if something is passed it bombs out. The only thing that works is if len(@param) < 1 works fine because 'All' is returned. But any other value is not. I have looked through the code and as you can see it should work fine. I have everything ByRef reference that needs it. Maybe I'm missing something and I'm hoping someone here can give me a hand.

To better state my question. Does anyone know of a bug that causes the output parameter in a Command object to get dropped while all the others remain the same. All values are null except for the one and that one does not return a proper value. It returns the value passed.

Edited by - onamuji on 04/04/2002 14:38:23

Onamuji
Aged Yak Warrior

504 Posts

Posted - 2002-04-04 : 14:51:41
This is the only reference I can find for this sort of thing on TechNet ...
quote:
Note Regarding Return Values and Output Parameters
Stored procedure return values and output parameters are only available if the CommandType is adCmdStoredProc and the command consists of only a single stored procedure call.

If you are submitting a batch of commands along with the stored procedure call, or submitting multiple stored procedure calls, then the return value(s) and output parameters are not available directly. You can get them indirectly by declaring some SQL Server variables and returning them as a Recordset. In the example below, the batch calls two stored procedures, both of which have a return value. The ? parameter markers can only be used for input parameters. You will receive an error if you try to use them with the OUTPUT keyword.

Still that doesn't seem correct. I'm just passing parameters in that get passed to another procedure that returns values from another procedure ? what's wrong with that ?

Go to Top of Page

robvolk
Most Valuable Yak

15732 Posts

Posted - 2002-04-04 : 14:54:18
quote:
In this instance Classification was '3' going in, and should be that coming out. However it resorts back to '3' when it comes out.



I may be completely stoned, but that doesn't sound like a problem. You said it was '3' going in, and it should be '3' coming out, and indeed it is '3' coming out.

Is classification the only variable that this happens to? If it is, then I'm REALLY at a loss to explain this, unless there is something special about the Classifications table that doesn't apply to the others. The dbo.csv_to_table() function may be the culprit, but the code for it isn't here. What does this function return if it's passed an empty string?

I'm thinking that there is some kind of default ADO Connection behavior that affects how empty strings are interpreted. ADO may be doing something internally that screws up empty strings. This would depend on what settings the provider supports, and if you're connecting with the ODBC provider, I'm almost positive that's what's happening. ODBC is pretty finicky about certain things, and empty strings are pretty finicky in themselves; the twain may not be meeting here.

Have you tried using ByVal instead of ByRef? It's a shot in the dark, but I have the feeling that there may either be a mismatch of ByVal with ByRef somewhere, or that passing ByRef in one of the functions may not be the right way to go.

And you DEFINITELY AREN'T gonna like this suggestion, but if all else fails, can you separate the variables with one set for input only, and another for output only?

Edited by - robvolk on 04/04/2002 14:55:46
Go to Top of Page

Onamuji
Aged Yak Warrior

504 Posts

Posted - 2002-04-04 : 15:02:14
Ya sorry sometimes I lose it ... been beating my brains in because of this for a whole what 20 minutes... its sad i usually get things fixed by now ... ok no it is 3 going in and is supposed to be the result set above (Business Necessity). Here's the code for all the other hidden functions:

create procedure table_to_csv
@sql nvarchar(4000),
@csv varchar(8000) output
as
set nocount on

declare @id int
declare @value varchar(8000)
declare @string varchar(8000)

set @string = ''

create table #table_to_csv (id int identity(1,1), value varchar(8000))
insert into #table_to_csv (value)
exec(@sql)

while exists(select * from #table_to_csv)
begin
select top 1
@id = id,
@value = value
from #table_to_csv

set @string = @string + @value + ', '

delete
from #table_to_csv
where id = @id
end

set @csv = left(@string, len(@string) - 1)
drop table #table_to_csv
go

create function csv_to_table(@csv varchar(8000)) returns @csv_to_table table (value varchar(8000)) as
begin
declare @position int
set @position = charindex (',', @csv, 0)

while len(@csv) > 0 and @position > 0
begin
insert into @csv_to_table (value)
select ltrim(rtrim(substring(@csv, 0, @position)))

set @csv = ltrim(rtrim(substring(@csv, @position + 1, len(@csv))))
set @position = charindex (',', @csv, 0)
end

if len(@csv) > 0
insert into @csv_to_table (value)
select @csv

return
end
go

I don't see either of these as being the problem since when I execute this stored proc from Query Analyzer it's all good. Just to clarify how this process goes:

ASP Page -> ASP Function -> Stored Proc 1 (Result set) -> Stored Proc 2 (Output params) -> For each param call Stored Proc 3 if needed -> return result to Stored Proc 2 -> return result to Stored Proc 1 -> stored proc 1 returns result set along with output parameters back to ASP function -> asp function to asp page -> to client

Gar! Wish I could find this bug. Maybe I'll just have to break the call into two separate calls but I want to find out why it is behaving like this. *gets out his bat and threatens the servers to behave*

Go to Top of Page

Onamuji
Aged Yak Warrior

504 Posts

Posted - 2002-04-04 : 15:06:36
So i just tested an idea, move the code from Stored Proc 2 into stored proc 1 ... still didn't fix a thing! still works correctly in QA.

It has to be something to do with adParamInputOutput... here's my connection string: PROVIDER=sqloledb;SERVER=testfinapp01;UID=stars;PWD=;DATABASE=stars;. All the other parameters are Null when they go in their parameters, except for this one. Even if i change the null to an empty string it's still the same ... null is converted to '' in procedure. The null or empty strings return 'All' yet the ones passed in with data return the same data they were passed in with.

Go to Top of Page

robvolk
Most Valuable Yak

15732 Posts

Posted - 2002-04-04 : 15:19:34
Is it possible that the tables/columns being passed to the SQL Server functions contain Nulls? Or empty strings? The reason I ask is because Nulls will completely FUBAR a string building operation, 1st row, last row, and anywhere in between. #table_to_csv can permit nulls in the value column AFAICT, and the table_to_csv code doesn't have any null-handling code. Empty strings could lead to consecutive commas that confuse the other function(s).

Just a WAG.

BTW, why do you SELECT a single row, concatenate it, and then DELETE it? Why not do this:

set @string = ''
create table #table_to_csv (id int identity(1,1), value varchar(8000))
insert into #table_to_csv (value) exec(@sql)

SELECT @string=@string + value + ',' FROM #table_to_csv
set @csv = left(@string, len(@string) - 1)
drop table #table_to_csv


Go to Top of Page

Onamuji
Aged Yak Warrior

504 Posts

Posted - 2002-04-04 : 15:29:26
because i'm crazy?

oh no i need to iterate through each record ... no just the first one...

oh i also do handle nulls ... with isnull(@param, '') .... in the master function ... guess i should add that to the csv stuff too huh :-) probably would have gotten around to it after 5000000000 errors...

Edited by - onamuji on 04/04/2002 15:33:11
Go to Top of Page

Onamuji
Aged Yak Warrior

504 Posts

Posted - 2002-04-04 : 15:43:22
ah i think i have it! so just for kicks i broke it up into two calls from ASP ... get this dreaded error :-)
quote:

Microsoft OLE DB Provider for SQL Server error '80040e09'

SELECT permission denied on object 'teams', database 'stars', owner 'dbo'.

/includes/SqlDatabase.asp, line 119


Nice, right? So I think I get why it doesn't return a value. f***** up s*** man... damn it .. i don't want to give select permissions on my tables :-( any ideas to get around this. I assume it's because of EXEC(@sql) ... wonder if sp_executesql will fix that ...


Go to Top of Page

robvolk
Most Valuable Yak

15732 Posts

Posted - 2002-04-04 : 16:53:36
quote:
oh no i need to iterate through each record ... no just the first one...

The SELECT @string=@string + value + ',' construct DOES iterate through each row (there's no TOP 1 in this SELECT). It's the foundation of Garth's CSV building technique:

http://www.sqlteam.com/item.asp?ItemID=2368

The SELECT permission problem can be solved by keeping the table name out of the dynamic SQL, if it's possible (I don't think it is, but there's more than one way to skin a cat).

Dare I ask what all of these functions and everything are ganging up to do? I mean, what's the object of the exercise? Building CSV's from results in tables? I can think of a few pure T-SQL ways of doing it without ANY SQL functions, possibly only a single SP. In fact, I can even think of a pure ADO method of constructing the CSV's that require nothing more than simple SELECT statements on the T-SQL side.

Go to Top of Page

Onamuji
Aged Yak Warrior

504 Posts

Posted - 2002-04-04 : 17:16:24
no, actually it goes beyond that. writing a report to report on resource usage for a particular project. the reason for using CSV's are that they can select multiple filters for project status/classification, team, department, etc... the csv is constructed with multiple selected items and rather than building a temp table i just use the table function. now since i have it passing numbers in a CSV format which represent primary key's in a table i need to display what i filtered by... obviously if the string is null or '' then it's all fields else i need to select those fields and thus that is why I used table_to_csv because i needed it to give me the names separated by commas ...

it used to be passed by csv of names ... but what if the name had a comma in it and plus i think numbers are better to compare in a table than a string.

thus the use of all this ... i should post that query it's freakin crazy and fast amazingly enough ... the old queries used to go 5-6 seconds this one is sub second ... i'll do that tomorrow maybe from work :-)

Go to Top of Page

Onamuji
Aged Yak Warrior

504 Posts

Posted - 2002-04-05 : 12:36:07
ok explain to me why that works for putting a table into a CSV format ? is it because you are appending to it .. or what ... i thought that when selecting into a variable it only takes the first row?! bah it works but i need to know why ... :-( maybe i can use this in some other places too...

Go to Top of Page

robvolk
Most Valuable Yak

15732 Posts

Posted - 2002-04-05 : 13:46:54
This will set the variable to the LAST row only:

SELECT @var=myColumn FROM myTable

This will concatenate EACH row's value to the string:

SELECT @var = @var + myColumn + ', ' FROM myTable

It's really not much different from a VB loop:

For x=1 to 10
var = array(x)
Next


vs.

For x=1 to 10
var = var & array(x) & ", "
Next


Since SELECT is working on a set, it processes it as a loop internally.

Go to Top of Page
   

- Advertisement -