Coding a set of queries

P

Pwyd

So i discovered through time that when it states that it has "too few
parameters, expected #" that what it really means is that the query you're
trying to run depends on something else that also has parameters which, since
they aren't explicitly being stated at this time, are considered 'parameters
not provided.' At any rate, my question is, how do i explicitly state where
the query should look for those values? the query uses two seperate tables,
and it looks like this, in sql:

SELECT Count(ReversalMemo.RecordNum) AS CountOfRecordNum
FROM ReversalMemo, UserIDQuery_SN_to_ProcName
HAVING
(((ReversalMemo.Signee1)=[UserIDQuery_SN_to_ProcName].[ProcessorName]));


run on its own in query view, it works fine. in code in VB, not so good :(
How do i explicitly provide the "parameter" it needs, or must i "recreate"
the query itself by finding the other table's value myself and coding the
logic used to determine the countofrecordnum's value?
 
K

Ken Sheridan

You need to loop through the query's parameters collection and evaluate the
parameters, e.g. with a procedure like this:

Public Sub OpenTempQuery(strSQL As String)

Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Dim prm As DAO.Parameter
Dim strQdf As String
Static n As Integer

Set dbs = CurrentDb

n = n + 1
strQdf = "qdfTemp" & n

' create temporary QueryDef object
Set qdf = dbs.CreateQueryDef(strQdf, strSQL)

' evaluate QueryDef object's parameters
For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

' open temporary query
DoCmd.OpenQuery strQdf

' delete temporary QueryDef object
dbs.QueryDefs.Delete qdf.Name

End Sub

You'd then call it, passing the SQL statement into the procedure as a string
expression, e.g.

Dim strSQL As String

strSQL = " SELECT COUNT(*) AS CountOfRows " & _
"FROM ReversalMemo, UserIDQuery_SN_to_ProcName " & _
"WHERE ReversalMemo.Signee1 = " & _
"UserIDQuery_SN_to_ProcName.ProcessorName"

OpenTempQuery strSQL

The parameters must be something which can be evaluated such as a reference
to a control on an open form such as Forms!MyForm!MyComboBox, and not a
simple system generated parameter prompt such as [Enter name:].

BTW note that in the SQL statement a WHERE clause should be used not a
HAVING clause, even though the latter will have the same effect in this case.
The HAVING clause results in this instance from designing the query in query
design view without changing the 'total' from 'Group By' to 'Where'. A
HAVING clause is used to restrict a query's result set after grouping and
usually on the basis of some aggregated value, e.g. HAVING SUM(Sales) >=
10000 to restrict the result set to rows with total sales of 10,000 currency
units or more. To restrict before grouping a WHERE clause should be used.

Ken Sheridan
Stafford, England

Pwyd said:
So i discovered through time that when it states that it has "too few
parameters, expected #" that what it really means is that the query you're
trying to run depends on something else that also has parameters which, since
they aren't explicitly being stated at this time, are considered 'parameters
not provided.' At any rate, my question is, how do i explicitly state where
the query should look for those values? the query uses two seperate tables,
and it looks like this, in sql:

SELECT Count(ReversalMemo.RecordNum) AS CountOfRecordNum
FROM ReversalMemo, UserIDQuery_SN_to_ProcName
HAVING
(((ReversalMemo.Signee1)=[UserIDQuery_SN_to_ProcName].[ProcessorName]));


run on its own in query view, it works fine. in code in VB, not so good :(
How do i explicitly provide the "parameter" it needs, or must i "recreate"
the query itself by finding the other table's value myself and coding the
logic used to determine the countofrecordnum's value?
 
P

Pwyd

oh wow. I understand what you're stating and the logic of it, but not all of
the code, i'll definately need some help with this. So the parameter i'm
speaking of is not one that pops up, requesting a parameter... its provided
by a second table, its never explicitly entered by the user. Does that
change anything? or perhaps i'm misunderstanding what you've stated.

Thanks for the info on the "having", i usually don't change the "group by"
unless i put in any criteria at all for that field, then i change it to a
where. What are the properties of a querydef object? what is its common
usage?



Ken Sheridan said:
You need to loop through the query's parameters collection and evaluate the
parameters, e.g. with a procedure like this:

Public Sub OpenTempQuery(strSQL As String)

Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Dim prm As DAO.Parameter
Dim strQdf As String
Static n As Integer

Set dbs = CurrentDb

n = n + 1
strQdf = "qdfTemp" & n

' create temporary QueryDef object
Set qdf = dbs.CreateQueryDef(strQdf, strSQL)

' evaluate QueryDef object's parameters
For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

' open temporary query
DoCmd.OpenQuery strQdf

' delete temporary QueryDef object
dbs.QueryDefs.Delete qdf.Name

End Sub

You'd then call it, passing the SQL statement into the procedure as a string
expression, e.g.

Dim strSQL As String

strSQL = " SELECT COUNT(*) AS CountOfRows " & _
"FROM ReversalMemo, UserIDQuery_SN_to_ProcName " & _
"WHERE ReversalMemo.Signee1 = " & _
"UserIDQuery_SN_to_ProcName.ProcessorName"

OpenTempQuery strSQL

The parameters must be something which can be evaluated such as a reference
to a control on an open form such as Forms!MyForm!MyComboBox, and not a
simple system generated parameter prompt such as [Enter name:].

BTW note that in the SQL statement a WHERE clause should be used not a
HAVING clause, even though the latter will have the same effect in this case.
The HAVING clause results in this instance from designing the query in query
design view without changing the 'total' from 'Group By' to 'Where'. A
HAVING clause is used to restrict a query's result set after grouping and
usually on the basis of some aggregated value, e.g. HAVING SUM(Sales) >=
10000 to restrict the result set to rows with total sales of 10,000 currency
units or more. To restrict before grouping a WHERE clause should be used.

Ken Sheridan
Stafford, England

Pwyd said:
So i discovered through time that when it states that it has "too few
parameters, expected #" that what it really means is that the query you're
trying to run depends on something else that also has parameters which, since
they aren't explicitly being stated at this time, are considered 'parameters
not provided.' At any rate, my question is, how do i explicitly state where
the query should look for those values? the query uses two seperate tables,
and it looks like this, in sql:

SELECT Count(ReversalMemo.RecordNum) AS CountOfRecordNum
FROM ReversalMemo, UserIDQuery_SN_to_ProcName
HAVING
(((ReversalMemo.Signee1)=[UserIDQuery_SN_to_ProcName].[ProcessorName]));


run on its own in query view, it works fine. in code in VB, not so good :(
How do i explicitly provide the "parameter" it needs, or must i "recreate"
the query itself by finding the other table's value myself and coding the
logic used to determine the countofrecordnum's value?
 
K

Ken Sheridan

A parameter is a variable which a query can use in a number of ways, but most
frequently it restricts the rows returned. This is what a simple prompt of
the type you are probably used to does, but it can also be a reference to a
control on a form. Take a look at the Sales by Year query in the sample
Northwind database which comes with Access; this includes the criterion on
the ShippedDate column:

Is Not Null And Between [Forms]![Sales by Year Dialog]![BeginningDate] And
[Forms]![Sales by Year Dialog]![EndingDate]

The references to the [Forms]![Sales by Year Dialog]![BeginningDate] and
[Forms]![Sales by Year Dialog]![EndingDate]controls are parameters. The
procedure I sent you will work with a query containing this type of parameter
because it can be evaluated by the Eval function. A simple prompt would not
work, however, because that cannot be evaluated. If you look at the Sales
by Year query in SQL view you'll notice that the parameters are declared as
DateTime at the start of the SQL statement. In my experience people often
omit to do this, but with dates its actually quite important as otherwise a
parameter value entered as a date in short date format could be
misinterpreted as an arithmetical expression.

I'm not sure what you have in mind when you say "its provided by a second
table". A value in a column in another table is not a parameter.

One common cause of a 'parameter expected' error is because users
incorporate a reference to a control on a form in a literal SQL string in
code, e.g "WHERE LastName = Forms!MyForm!txtLastName". What should be done
in a case like that is to concatenate the value of the control into the SQL
string; "WHERE LastName = """ & Forms!MyForm!txtLastName & """". That's not
the case with the SQL you posted, however. The most likely culprit is a
parameter in the UserIDQuery_SN_to_ProcName, and my function would handle
that. Sometimes the error is just a result of a simple typo. If a column
name is entered in the string incorrectly Access will treat it as a
parameter; essentially anything it doesn't recognize as an object is treated
as a parameter. However, you say that the query opens fine using the same
SQL as a query rather than in code, which suggests there is no typo.

The fact that its opening as a query does beg the question why you want to
build and open via VBA at all. Why not just save the query and open it with
the OpenQuery method?

A querydef object is an object which defines a query (its short for 'query
definition'). Saved queries are querydef objects in the current databases
querydefs collection. A querydef object does not have to be a saved query,
however. One can be temporarily created in code. This is commonly done and
its rows accessed by means of a recordset object created with the
OpenRecordset method of the querydef object. In my function a querydef
object is created with the CreateQueryDef method of the database object and
saved by giving it name. Its then opened in datasheet view with the
OpenQuery method and finally deleted from the current databases querydefs
collection. So its not quite as temporary as a true temporary querydef
object.

As a simple example paste the following procedure into a standard module:

Public Sub OpenRstFromQuery(strQuery As String)

Dim dbs As DAO.Database, qdf As DAO.QueryDef
Dim prm As DAO.Parameter, rst As DAO.Recordset

Set dbs = CurrentDb
Set qdf = dbs.QueryDefs(strQuery)

For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

Set rst = qdf.OpenRecordset

With rst
Do While Not .EOF
Debug.Print .Fields(0); .Fields(1)
.MoveNext
Loop
End With

End Sub

Then in the debug window (aka immediate window) call it, passing it the name
of any simple query with at least two columns returned like so:

OpenRstFromQuery "NameOfQueryGoesHere"

You'll see that it lists the values from first two columns of the query to
the debug window.

Ken Sheridan
Stafford, England

Pwyd said:
oh wow. I understand what you're stating and the logic of it, but not all of
the code, i'll definately need some help with this. So the parameter i'm
speaking of is not one that pops up, requesting a parameter... its provided
by a second table, its never explicitly entered by the user. Does that
change anything? or perhaps i'm misunderstanding what you've stated.

Thanks for the info on the "having", i usually don't change the "group by"
unless i put in any criteria at all for that field, then i change it to a
where. What are the properties of a querydef object? what is its common
usage?



Ken Sheridan said:
You need to loop through the query's parameters collection and evaluate the
parameters, e.g. with a procedure like this:

Public Sub OpenTempQuery(strSQL As String)

Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Dim prm As DAO.Parameter
Dim strQdf As String
Static n As Integer

Set dbs = CurrentDb

n = n + 1
strQdf = "qdfTemp" & n

' create temporary QueryDef object
Set qdf = dbs.CreateQueryDef(strQdf, strSQL)

' evaluate QueryDef object's parameters
For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

' open temporary query
DoCmd.OpenQuery strQdf

' delete temporary QueryDef object
dbs.QueryDefs.Delete qdf.Name

End Sub

You'd then call it, passing the SQL statement into the procedure as a string
expression, e.g.

Dim strSQL As String

strSQL = " SELECT COUNT(*) AS CountOfRows " & _
"FROM ReversalMemo, UserIDQuery_SN_to_ProcName " & _
"WHERE ReversalMemo.Signee1 = " & _
"UserIDQuery_SN_to_ProcName.ProcessorName"

OpenTempQuery strSQL

The parameters must be something which can be evaluated such as a reference
to a control on an open form such as Forms!MyForm!MyComboBox, and not a
simple system generated parameter prompt such as [Enter name:].

BTW note that in the SQL statement a WHERE clause should be used not a
HAVING clause, even though the latter will have the same effect in this case.
The HAVING clause results in this instance from designing the query in query
design view without changing the 'total' from 'Group By' to 'Where'. A
HAVING clause is used to restrict a query's result set after grouping and
usually on the basis of some aggregated value, e.g. HAVING SUM(Sales) >=
10000 to restrict the result set to rows with total sales of 10,000 currency
units or more. To restrict before grouping a WHERE clause should be used.

Ken Sheridan
Stafford, England

Pwyd said:
So i discovered through time that when it states that it has "too few
parameters, expected #" that what it really means is that the query you're
trying to run depends on something else that also has parameters which, since
they aren't explicitly being stated at this time, are considered 'parameters
not provided.' At any rate, my question is, how do i explicitly state where
the query should look for those values? the query uses two seperate tables,
and it looks like this, in sql:

SELECT Count(ReversalMemo.RecordNum) AS CountOfRecordNum
FROM ReversalMemo, UserIDQuery_SN_to_ProcName
HAVING
(((ReversalMemo.Signee1)=[UserIDQuery_SN_to_ProcName].[ProcessorName]));


run on its own in query view, it works fine. in code in VB, not so good :(
How do i explicitly provide the "parameter" it needs, or must i "recreate"
the query itself by finding the other table's value myself and coding the
logic used to determine the countofrecordnum's value?
 
P

Pwyd

I've had the same trouble with the openquery method. Perhaps we could try
that instead. Here, let me post the code, and yes, the query i'm trying to
run does refer to a value based on a second table. Here:

Public Function testfunction()
Dim adminQuery As Recordset
Dim processorQuery As Recordset
Dim recordCount As Recordset

Set adminQuery = CurrentDb.OpenRecordset("IsAdminQuery")
Set processorQuery = CurrentDb.OpenRecordset("IsProcessorQuery")
'Set recordCount = CurrentDb.OpenRecordset("CountOfUserExistingRecords")

If (adminQuery.BOF And adminQuery.EOF) Then
If (processorQuery.BOF And processorQuery.EOF) Then
DoCmd.Close
ElseIf (recordCount!RecordNum <= 0) Then
DoCmd.OpenForm ("Costpoint Processor New Record")
Else: DoCmd.OpenForm ("ReversalCostpoint Processor")
End If
Else: DoCmd.OpenForm ("Costpoint Reversals")
End If


End Function

The count of user existing records query is the problem. It looks like this:
SELECT Count(ReversalMemo.RecordNum) AS CountOfRecordNum
FROM ReversalMemo, UserIDQuery_SN_to_ProcName
HAVING
(((ReversalMemo.Signee1)=[UserIDQuery_SN_to_ProcName].[ProcessorName]));

The user_sn... query produces a user's true name based on a shortname from
their login. It's later used to filter their records. The purpose of this
particular part of the function is, if the user is listed as a processor, but
has 0 records (null or 0 result) the form opens up the new record form
instead of the processor form, which would be a blank white page since its
not for data entry.

Its basically a very simple decision tree. Is the user an admin? if yes,
open admin version. if no, is the user a processor? if yes, open the
processor form, showing them only their own records. if no, close. if the
processor is valid but has no records, start a new record.

The count of user records query uses two seperate tables in the same query
to determine if the record count. Running it through VB, it expects a
parameter value for the criteria i've set, since its not: concurrently
running, can't be determined without checking the row value determined by the
other query (user_sn_to...), I suppose, or explicitly defined. What else
can i provide that might help you see what i'm trying to accomplish, and the
simplest way to do it?

I'd also been advised to try a make-table query, to put the two values in
one table so that they could be easily read, but i can't compact on exit,
because i do not own the drive priveledges where the database is served, so i
would be left with lots of bloating :(


Ken Sheridan said:
A parameter is a variable which a query can use in a number of ways, but most
frequently it restricts the rows returned. This is what a simple prompt of
the type you are probably used to does, but it can also be a reference to a
control on a form. Take a look at the Sales by Year query in the sample
Northwind database which comes with Access; this includes the criterion on
the ShippedDate column:

Is Not Null And Between [Forms]![Sales by Year Dialog]![BeginningDate] And
[Forms]![Sales by Year Dialog]![EndingDate]

The references to the [Forms]![Sales by Year Dialog]![BeginningDate] and
[Forms]![Sales by Year Dialog]![EndingDate]controls are parameters. The
procedure I sent you will work with a query containing this type of parameter
because it can be evaluated by the Eval function. A simple prompt would not
work, however, because that cannot be evaluated. If you look at the Sales
by Year query in SQL view you'll notice that the parameters are declared as
DateTime at the start of the SQL statement. In my experience people often
omit to do this, but with dates its actually quite important as otherwise a
parameter value entered as a date in short date format could be
misinterpreted as an arithmetical expression.

I'm not sure what you have in mind when you say "its provided by a second
table". A value in a column in another table is not a parameter.

One common cause of a 'parameter expected' error is because users
incorporate a reference to a control on a form in a literal SQL string in
code, e.g "WHERE LastName = Forms!MyForm!txtLastName". What should be done
in a case like that is to concatenate the value of the control into the SQL
string; "WHERE LastName = """ & Forms!MyForm!txtLastName & """". That's not
the case with the SQL you posted, however. The most likely culprit is a
parameter in the UserIDQuery_SN_to_ProcName, and my function would handle
that. Sometimes the error is just a result of a simple typo. If a column
name is entered in the string incorrectly Access will treat it as a
parameter; essentially anything it doesn't recognize as an object is treated
as a parameter. However, you say that the query opens fine using the same
SQL as a query rather than in code, which suggests there is no typo.

The fact that its opening as a query does beg the question why you want to
build and open via VBA at all. Why not just save the query and open it with
the OpenQuery method?

A querydef object is an object which defines a query (its short for 'query
definition'). Saved queries are querydef objects in the current databases
querydefs collection. A querydef object does not have to be a saved query,
however. One can be temporarily created in code. This is commonly done and
its rows accessed by means of a recordset object created with the
OpenRecordset method of the querydef object. In my function a querydef
object is created with the CreateQueryDef method of the database object and
saved by giving it name. Its then opened in datasheet view with the
OpenQuery method and finally deleted from the current databases querydefs
collection. So its not quite as temporary as a true temporary querydef
object.

As a simple example paste the following procedure into a standard module:

Public Sub OpenRstFromQuery(strQuery As String)

Dim dbs As DAO.Database, qdf As DAO.QueryDef
Dim prm As DAO.Parameter, rst As DAO.Recordset

Set dbs = CurrentDb
Set qdf = dbs.QueryDefs(strQuery)

For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

Set rst = qdf.OpenRecordset

With rst
Do While Not .EOF
Debug.Print .Fields(0); .Fields(1)
.MoveNext
Loop
End With

End Sub

Then in the debug window (aka immediate window) call it, passing it the name
of any simple query with at least two columns returned like so:

OpenRstFromQuery "NameOfQueryGoesHere"

You'll see that it lists the values from first two columns of the query to
the debug window.

Ken Sheridan
Stafford, England

Pwyd said:
oh wow. I understand what you're stating and the logic of it, but not all of
the code, i'll definately need some help with this. So the parameter i'm
speaking of is not one that pops up, requesting a parameter... its provided
by a second table, its never explicitly entered by the user. Does that
change anything? or perhaps i'm misunderstanding what you've stated.

Thanks for the info on the "having", i usually don't change the "group by"
unless i put in any criteria at all for that field, then i change it to a
where. What are the properties of a querydef object? what is its common
usage?



Ken Sheridan said:
You need to loop through the query's parameters collection and evaluate the
parameters, e.g. with a procedure like this:

Public Sub OpenTempQuery(strSQL As String)

Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Dim prm As DAO.Parameter
Dim strQdf As String
Static n As Integer

Set dbs = CurrentDb

n = n + 1
strQdf = "qdfTemp" & n

' create temporary QueryDef object
Set qdf = dbs.CreateQueryDef(strQdf, strSQL)

' evaluate QueryDef object's parameters
For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

' open temporary query
DoCmd.OpenQuery strQdf

' delete temporary QueryDef object
dbs.QueryDefs.Delete qdf.Name

End Sub

You'd then call it, passing the SQL statement into the procedure as a string
expression, e.g.

Dim strSQL As String

strSQL = " SELECT COUNT(*) AS CountOfRows " & _
"FROM ReversalMemo, UserIDQuery_SN_to_ProcName " & _
"WHERE ReversalMemo.Signee1 = " & _
"UserIDQuery_SN_to_ProcName.ProcessorName"

OpenTempQuery strSQL

The parameters must be something which can be evaluated such as a reference
to a control on an open form such as Forms!MyForm!MyComboBox, and not a
simple system generated parameter prompt such as [Enter name:].

BTW note that in the SQL statement a WHERE clause should be used not a
HAVING clause, even though the latter will have the same effect in this case.
The HAVING clause results in this instance from designing the query in query
design view without changing the 'total' from 'Group By' to 'Where'. A
HAVING clause is used to restrict a query's result set after grouping and
usually on the basis of some aggregated value, e.g. HAVING SUM(Sales) >=
10000 to restrict the result set to rows with total sales of 10,000 currency
units or more. To restrict before grouping a WHERE clause should be used.

Ken Sheridan
Stafford, England

:

So i discovered through time that when it states that it has "too few
parameters, expected #" that what it really means is that the query you're
trying to run depends on something else that also has parameters which, since
they aren't explicitly being stated at this time, are considered 'parameters
not provided.' At any rate, my question is, how do i explicitly state where
the query should look for those values? the query uses two seperate tables,
and it looks like this, in sql:

SELECT Count(ReversalMemo.RecordNum) AS CountOfRecordNum
FROM ReversalMemo, UserIDQuery_SN_to_ProcName
HAVING
(((ReversalMemo.Signee1)=[UserIDQuery_SN_to_ProcName].[ProcessorName]));


run on its own in query view, it works fine. in code in VB, not so good :(
How do i explicitly provide the "parameter" it needs, or must i "recreate"
the query itself by finding the other table's value myself and coding the
logic used to determine the countofrecordnum's value?
 
K

Ken Sheridan

How are you getting the current user's login name for the query? If you are
calling the Windows API GetUserName function that's not a parameter so should
be OK, but if you are getting it in some other way its possible it could be
being treated as a parameter.

Rather than creating recordset objects why not use the DLookup function to
get the value of any non-Null column from a query? Test for
IsNull(DLookup("ColumnName","QueryName")) to identify no rows returned, and
for DLookup("ColumnName","QueryName") = 0 to identify where the Count
operator returns a 0.

BTW don't use recordcount as an object variable's name; it’s a property name
in both DAO and ADO so should be avoided.

I also note that you refer to recordCount!RecordNum <= 0, but in the SQL
statement the column name is CountOfRecordNum.

Ken Sheridan
Stafford, England

Pwyd said:
I've had the same trouble with the openquery method. Perhaps we could try
that instead. Here, let me post the code, and yes, the query i'm trying to
run does refer to a value based on a second table. Here:

Public Function testfunction()
Dim adminQuery As Recordset
Dim processorQuery As Recordset
Dim recordCount As Recordset

Set adminQuery = CurrentDb.OpenRecordset("IsAdminQuery")
Set processorQuery = CurrentDb.OpenRecordset("IsProcessorQuery")
'Set recordCount = CurrentDb.OpenRecordset("CountOfUserExistingRecords")

If (adminQuery.BOF And adminQuery.EOF) Then
If (processorQuery.BOF And processorQuery.EOF) Then
DoCmd.Close
ElseIf (recordCount!RecordNum <= 0) Then
DoCmd.OpenForm ("Costpoint Processor New Record")
Else: DoCmd.OpenForm ("ReversalCostpoint Processor")
End If
Else: DoCmd.OpenForm ("Costpoint Reversals")
End If


End Function

The count of user existing records query is the problem. It looks like this:
SELECT Count(ReversalMemo.RecordNum) AS CountOfRecordNum
FROM ReversalMemo, UserIDQuery_SN_to_ProcName
HAVING
(((ReversalMemo.Signee1)=[UserIDQuery_SN_to_ProcName].[ProcessorName]));

The user_sn... query produces a user's true name based on a shortname from
their login. It's later used to filter their records. The purpose of this
particular part of the function is, if the user is listed as a processor, but
has 0 records (null or 0 result) the form opens up the new record form
instead of the processor form, which would be a blank white page since its
not for data entry.

Its basically a very simple decision tree. Is the user an admin? if yes,
open admin version. if no, is the user a processor? if yes, open the
processor form, showing them only their own records. if no, close. if the
processor is valid but has no records, start a new record.

The count of user records query uses two seperate tables in the same query
to determine if the record count. Running it through VB, it expects a
parameter value for the criteria i've set, since its not: concurrently
running, can't be determined without checking the row value determined by the
other query (user_sn_to...), I suppose, or explicitly defined. What else
can i provide that might help you see what i'm trying to accomplish, and the
simplest way to do it?

I'd also been advised to try a make-table query, to put the two values in
one table so that they could be easily read, but i can't compact on exit,
because i do not own the drive priveledges where the database is served, so i
would be left with lots of bloating :(


Ken Sheridan said:
A parameter is a variable which a query can use in a number of ways, but most
frequently it restricts the rows returned. This is what a simple prompt of
the type you are probably used to does, but it can also be a reference to a
control on a form. Take a look at the Sales by Year query in the sample
Northwind database which comes with Access; this includes the criterion on
the ShippedDate column:

Is Not Null And Between [Forms]![Sales by Year Dialog]![BeginningDate] And
[Forms]![Sales by Year Dialog]![EndingDate]

The references to the [Forms]![Sales by Year Dialog]![BeginningDate] and
[Forms]![Sales by Year Dialog]![EndingDate]controls are parameters. The
procedure I sent you will work with a query containing this type of parameter
because it can be evaluated by the Eval function. A simple prompt would not
work, however, because that cannot be evaluated. If you look at the Sales
by Year query in SQL view you'll notice that the parameters are declared as
DateTime at the start of the SQL statement. In my experience people often
omit to do this, but with dates its actually quite important as otherwise a
parameter value entered as a date in short date format could be
misinterpreted as an arithmetical expression.

I'm not sure what you have in mind when you say "its provided by a second
table". A value in a column in another table is not a parameter.

One common cause of a 'parameter expected' error is because users
incorporate a reference to a control on a form in a literal SQL string in
code, e.g "WHERE LastName = Forms!MyForm!txtLastName". What should be done
in a case like that is to concatenate the value of the control into the SQL
string; "WHERE LastName = """ & Forms!MyForm!txtLastName & """". That's not
the case with the SQL you posted, however. The most likely culprit is a
parameter in the UserIDQuery_SN_to_ProcName, and my function would handle
that. Sometimes the error is just a result of a simple typo. If a column
name is entered in the string incorrectly Access will treat it as a
parameter; essentially anything it doesn't recognize as an object is treated
as a parameter. However, you say that the query opens fine using the same
SQL as a query rather than in code, which suggests there is no typo.

The fact that its opening as a query does beg the question why you want to
build and open via VBA at all. Why not just save the query and open it with
the OpenQuery method?

A querydef object is an object which defines a query (its short for 'query
definition'). Saved queries are querydef objects in the current databases
querydefs collection. A querydef object does not have to be a saved query,
however. One can be temporarily created in code. This is commonly done and
its rows accessed by means of a recordset object created with the
OpenRecordset method of the querydef object. In my function a querydef
object is created with the CreateQueryDef method of the database object and
saved by giving it name. Its then opened in datasheet view with the
OpenQuery method and finally deleted from the current databases querydefs
collection. So its not quite as temporary as a true temporary querydef
object.

As a simple example paste the following procedure into a standard module:

Public Sub OpenRstFromQuery(strQuery As String)

Dim dbs As DAO.Database, qdf As DAO.QueryDef
Dim prm As DAO.Parameter, rst As DAO.Recordset

Set dbs = CurrentDb
Set qdf = dbs.QueryDefs(strQuery)

For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

Set rst = qdf.OpenRecordset

With rst
Do While Not .EOF
Debug.Print .Fields(0); .Fields(1)
.MoveNext
Loop
End With

End Sub

Then in the debug window (aka immediate window) call it, passing it the name
of any simple query with at least two columns returned like so:

OpenRstFromQuery "NameOfQueryGoesHere"

You'll see that it lists the values from first two columns of the query to
the debug window.

Ken Sheridan
Stafford, England

Pwyd said:
oh wow. I understand what you're stating and the logic of it, but not all of
the code, i'll definately need some help with this. So the parameter i'm
speaking of is not one that pops up, requesting a parameter... its provided
by a second table, its never explicitly entered by the user. Does that
change anything? or perhaps i'm misunderstanding what you've stated.

Thanks for the info on the "having", i usually don't change the "group by"
unless i put in any criteria at all for that field, then i change it to a
where. What are the properties of a querydef object? what is its common
usage?



:

You need to loop through the query's parameters collection and evaluate the
parameters, e.g. with a procedure like this:

Public Sub OpenTempQuery(strSQL As String)

Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Dim prm As DAO.Parameter
Dim strQdf As String
Static n As Integer

Set dbs = CurrentDb

n = n + 1
strQdf = "qdfTemp" & n

' create temporary QueryDef object
Set qdf = dbs.CreateQueryDef(strQdf, strSQL)

' evaluate QueryDef object's parameters
For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

' open temporary query
DoCmd.OpenQuery strQdf

' delete temporary QueryDef object
dbs.QueryDefs.Delete qdf.Name

End Sub

You'd then call it, passing the SQL statement into the procedure as a string
expression, e.g.

Dim strSQL As String

strSQL = " SELECT COUNT(*) AS CountOfRows " & _
"FROM ReversalMemo, UserIDQuery_SN_to_ProcName " & _
"WHERE ReversalMemo.Signee1 = " & _
"UserIDQuery_SN_to_ProcName.ProcessorName"

OpenTempQuery strSQL

The parameters must be something which can be evaluated such as a reference
to a control on an open form such as Forms!MyForm!MyComboBox, and not a
simple system generated parameter prompt such as [Enter name:].

BTW note that in the SQL statement a WHERE clause should be used not a
HAVING clause, even though the latter will have the same effect in this case.
The HAVING clause results in this instance from designing the query in query
design view without changing the 'total' from 'Group By' to 'Where'. A
HAVING clause is used to restrict a query's result set after grouping and
usually on the basis of some aggregated value, e.g. HAVING SUM(Sales) >=
10000 to restrict the result set to rows with total sales of 10,000 currency
units or more. To restrict before grouping a WHERE clause should be used.

Ken Sheridan
Stafford, England

:

So i discovered through time that when it states that it has "too few
parameters, expected #" that what it really means is that the query you're
trying to run depends on something else that also has parameters which, since
they aren't explicitly being stated at this time, are considered 'parameters
not provided.' At any rate, my question is, how do i explicitly state where
the query should look for those values? the query uses two seperate tables,
and it looks like this, in sql:

SELECT Count(ReversalMemo.RecordNum) AS CountOfRecordNum
FROM ReversalMemo, UserIDQuery_SN_to_ProcName
HAVING
(((ReversalMemo.Signee1)=[UserIDQuery_SN_to_ProcName].[ProcessorName]));


run on its own in query view, it works fine. in code in VB, not so good :(
How do i explicitly provide the "parameter" it needs, or must i "recreate"
the query itself by finding the other table's value myself and coding the
logic used to determine the countofrecordnum's value?
 
P

Pwyd

i'm using a GetUserID() public function built with the help of the users
here, and yes i believe it uses the windows API.

Does dlookup instantiate the query on its own, or does it require the query
is running when it is called?

as for the column name, yes, that is how its referred to by me, i was
unsure if that was correct. Does the recordset object adopt the field names
of the thing it contains, or is it still proper to refer to the name of the
query itself thats being run?

In addition, is the way i've contstructed it improper, or is dlookup just
more efficient, faster, etc?



Ken Sheridan said:
How are you getting the current user's login name for the query? If you are
calling the Windows API GetUserName function that's not a parameter so should
be OK, but if you are getting it in some other way its possible it could be
being treated as a parameter.

Rather than creating recordset objects why not use the DLookup function to
get the value of any non-Null column from a query? Test for
IsNull(DLookup("ColumnName","QueryName")) to identify no rows returned, and
for DLookup("ColumnName","QueryName") = 0 to identify where the Count
operator returns a 0.

BTW don't use recordcount as an object variable's name; it’s a property name
in both DAO and ADO so should be avoided.

I also note that you refer to recordCount!RecordNum <= 0, but in the SQL
statement the column name is CountOfRecordNum.

Ken Sheridan
Stafford, England

Pwyd said:
I've had the same trouble with the openquery method. Perhaps we could try
that instead. Here, let me post the code, and yes, the query i'm trying to
run does refer to a value based on a second table. Here:

Public Function testfunction()
Dim adminQuery As Recordset
Dim processorQuery As Recordset
Dim recordCount As Recordset

Set adminQuery = CurrentDb.OpenRecordset("IsAdminQuery")
Set processorQuery = CurrentDb.OpenRecordset("IsProcessorQuery")
'Set recordCount = CurrentDb.OpenRecordset("CountOfUserExistingRecords")

If (adminQuery.BOF And adminQuery.EOF) Then
If (processorQuery.BOF And processorQuery.EOF) Then
DoCmd.Close
ElseIf (recordCount!RecordNum <= 0) Then
DoCmd.OpenForm ("Costpoint Processor New Record")
Else: DoCmd.OpenForm ("ReversalCostpoint Processor")
End If
Else: DoCmd.OpenForm ("Costpoint Reversals")
End If


End Function

The count of user existing records query is the problem. It looks like this:
SELECT Count(ReversalMemo.RecordNum) AS CountOfRecordNum
FROM ReversalMemo, UserIDQuery_SN_to_ProcName
HAVING
(((ReversalMemo.Signee1)=[UserIDQuery_SN_to_ProcName].[ProcessorName]));

The user_sn... query produces a user's true name based on a shortname from
their login. It's later used to filter their records. The purpose of this
particular part of the function is, if the user is listed as a processor, but
has 0 records (null or 0 result) the form opens up the new record form
instead of the processor form, which would be a blank white page since its
not for data entry.

Its basically a very simple decision tree. Is the user an admin? if yes,
open admin version. if no, is the user a processor? if yes, open the
processor form, showing them only their own records. if no, close. if the
processor is valid but has no records, start a new record.

The count of user records query uses two seperate tables in the same query
to determine if the record count. Running it through VB, it expects a
parameter value for the criteria i've set, since its not: concurrently
running, can't be determined without checking the row value determined by the
other query (user_sn_to...), I suppose, or explicitly defined. What else
can i provide that might help you see what i'm trying to accomplish, and the
simplest way to do it?

I'd also been advised to try a make-table query, to put the two values in
one table so that they could be easily read, but i can't compact on exit,
because i do not own the drive priveledges where the database is served, so i
would be left with lots of bloating :(


Ken Sheridan said:
A parameter is a variable which a query can use in a number of ways, but most
frequently it restricts the rows returned. This is what a simple prompt of
the type you are probably used to does, but it can also be a reference to a
control on a form. Take a look at the Sales by Year query in the sample
Northwind database which comes with Access; this includes the criterion on
the ShippedDate column:

Is Not Null And Between [Forms]![Sales by Year Dialog]![BeginningDate] And
[Forms]![Sales by Year Dialog]![EndingDate]

The references to the [Forms]![Sales by Year Dialog]![BeginningDate] and
[Forms]![Sales by Year Dialog]![EndingDate]controls are parameters. The
procedure I sent you will work with a query containing this type of parameter
because it can be evaluated by the Eval function. A simple prompt would not
work, however, because that cannot be evaluated. If you look at the Sales
by Year query in SQL view you'll notice that the parameters are declared as
DateTime at the start of the SQL statement. In my experience people often
omit to do this, but with dates its actually quite important as otherwise a
parameter value entered as a date in short date format could be
misinterpreted as an arithmetical expression.

I'm not sure what you have in mind when you say "its provided by a second
table". A value in a column in another table is not a parameter.

One common cause of a 'parameter expected' error is because users
incorporate a reference to a control on a form in a literal SQL string in
code, e.g "WHERE LastName = Forms!MyForm!txtLastName". What should be done
in a case like that is to concatenate the value of the control into the SQL
string; "WHERE LastName = """ & Forms!MyForm!txtLastName & """". That's not
the case with the SQL you posted, however. The most likely culprit is a
parameter in the UserIDQuery_SN_to_ProcName, and my function would handle
that. Sometimes the error is just a result of a simple typo. If a column
name is entered in the string incorrectly Access will treat it as a
parameter; essentially anything it doesn't recognize as an object is treated
as a parameter. However, you say that the query opens fine using the same
SQL as a query rather than in code, which suggests there is no typo.

The fact that its opening as a query does beg the question why you want to
build and open via VBA at all. Why not just save the query and open it with
the OpenQuery method?

A querydef object is an object which defines a query (its short for 'query
definition'). Saved queries are querydef objects in the current databases
querydefs collection. A querydef object does not have to be a saved query,
however. One can be temporarily created in code. This is commonly done and
its rows accessed by means of a recordset object created with the
OpenRecordset method of the querydef object. In my function a querydef
object is created with the CreateQueryDef method of the database object and
saved by giving it name. Its then opened in datasheet view with the
OpenQuery method and finally deleted from the current databases querydefs
collection. So its not quite as temporary as a true temporary querydef
object.

As a simple example paste the following procedure into a standard module:

Public Sub OpenRstFromQuery(strQuery As String)

Dim dbs As DAO.Database, qdf As DAO.QueryDef
Dim prm As DAO.Parameter, rst As DAO.Recordset

Set dbs = CurrentDb
Set qdf = dbs.QueryDefs(strQuery)

For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

Set rst = qdf.OpenRecordset

With rst
Do While Not .EOF
Debug.Print .Fields(0); .Fields(1)
.MoveNext
Loop
End With

End Sub

Then in the debug window (aka immediate window) call it, passing it the name
of any simple query with at least two columns returned like so:

OpenRstFromQuery "NameOfQueryGoesHere"

You'll see that it lists the values from first two columns of the query to
the debug window.

Ken Sheridan
Stafford, England

:

oh wow. I understand what you're stating and the logic of it, but not all of
the code, i'll definately need some help with this. So the parameter i'm
speaking of is not one that pops up, requesting a parameter... its provided
by a second table, its never explicitly entered by the user. Does that
change anything? or perhaps i'm misunderstanding what you've stated.

Thanks for the info on the "having", i usually don't change the "group by"
unless i put in any criteria at all for that field, then i change it to a
where. What are the properties of a querydef object? what is its common
usage?



:

You need to loop through the query's parameters collection and evaluate the
parameters, e.g. with a procedure like this:

Public Sub OpenTempQuery(strSQL As String)

Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Dim prm As DAO.Parameter
Dim strQdf As String
Static n As Integer

Set dbs = CurrentDb

n = n + 1
strQdf = "qdfTemp" & n

' create temporary QueryDef object
Set qdf = dbs.CreateQueryDef(strQdf, strSQL)

' evaluate QueryDef object's parameters
For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

' open temporary query
DoCmd.OpenQuery strQdf

' delete temporary QueryDef object
dbs.QueryDefs.Delete qdf.Name

End Sub

You'd then call it, passing the SQL statement into the procedure as a string
expression, e.g.

Dim strSQL As String

strSQL = " SELECT COUNT(*) AS CountOfRows " & _
"FROM ReversalMemo, UserIDQuery_SN_to_ProcName " & _
"WHERE ReversalMemo.Signee1 = " & _
"UserIDQuery_SN_to_ProcName.ProcessorName"

OpenTempQuery strSQL

The parameters must be something which can be evaluated such as a reference
to a control on an open form such as Forms!MyForm!MyComboBox, and not a
simple system generated parameter prompt such as [Enter name:].

BTW note that in the SQL statement a WHERE clause should be used not a
HAVING clause, even though the latter will have the same effect in this case.
The HAVING clause results in this instance from designing the query in query
design view without changing the 'total' from 'Group By' to 'Where'. A
HAVING clause is used to restrict a query's result set after grouping and
usually on the basis of some aggregated value, e.g. HAVING SUM(Sales) >=
10000 to restrict the result set to rows with total sales of 10,000 currency
units or more. To restrict before grouping a WHERE clause should be used.

Ken Sheridan
Stafford, England

:

So i discovered through time that when it states that it has "too few
parameters, expected #" that what it really means is that the query you're
trying to run depends on something else that also has parameters which, since
they aren't explicitly being stated at this time, are considered 'parameters
not provided.' At any rate, my question is, how do i explicitly state where
the query should look for those values? the query uses two seperate tables,
and it looks like this, in sql:

SELECT Count(ReversalMemo.RecordNum) AS CountOfRecordNum
FROM ReversalMemo, UserIDQuery_SN_to_ProcName
HAVING
(((ReversalMemo.Signee1)=[UserIDQuery_SN_to_ProcName].[ProcessorName]));


run on its own in query view, it works fine. in code in VB, not so good :(
How do i explicitly provide the "parameter" it needs, or must i "recreate"
the query itself by finding the other table's value myself and coding the
logic used to determine the countofrecordnum's value?
 
K

Ken Sheridan

Your GetUserID function almost certainly calls the Widows API GetUserName
function, so I think we can rule that out as the mysterious parameter.

A query doesn't have to be open when the DLookup function (or any other
domain function such as DCount, DMax etc) is called.

If you create a recordset object based on a query then its Fields collection
is the set of columns returned by the query, so it does in effect "adopt the
field names". You cannot refer directly to the value of a field returned by
a query; you either have to create a recordset object (as my sample
OpenRstFromQuery procedure does) or use one of the domain function such as
DLookup.

But we are getting away from the original problem of what is the expected
parameter. You should be able to list the parameters for the SQL statement
with the following function:

Public Sub ListParameters(strSQL As String)

Dim dbs As DAO.Database, qdf As DAO.QueryDef
Dim prm As DAO.Parameter

Set dbs = CurrentDb
Set qdf = dbs.CreateQueryDef("", strSQL)

For Each prm In qdf.Parameters
Debug.Print prm.Name
Next prm

End Sub

Call it like so:

ListParameters "SELECT * FROM CountOfUserExistingRecords"

Any parameters expected by the CountOfUserExistingRecords query should be
listed to the debug window. This should point you in the direction of the
cause of the problem, and hopefully to a solution.

Ken Sheridan
Stafford, England

Pwyd said:
i'm using a GetUserID() public function built with the help of the users
here, and yes i believe it uses the windows API.

Does dlookup instantiate the query on its own, or does it require the query
is running when it is called?

as for the column name, yes, that is how its referred to by me, i was
unsure if that was correct. Does the recordset object adopt the field names
of the thing it contains, or is it still proper to refer to the name of the
query itself thats being run?

In addition, is the way i've contstructed it improper, or is dlookup just
more efficient, faster, etc?



Ken Sheridan said:
How are you getting the current user's login name for the query? If you are
calling the Windows API GetUserName function that's not a parameter so should
be OK, but if you are getting it in some other way its possible it could be
being treated as a parameter.

Rather than creating recordset objects why not use the DLookup function to
get the value of any non-Null column from a query? Test for
IsNull(DLookup("ColumnName","QueryName")) to identify no rows returned, and
for DLookup("ColumnName","QueryName") = 0 to identify where the Count
operator returns a 0.

BTW don't use recordcount as an object variable's name; it’s a property name
in both DAO and ADO so should be avoided.

I also note that you refer to recordCount!RecordNum <= 0, but in the SQL
statement the column name is CountOfRecordNum.

Ken Sheridan
Stafford, England

Pwyd said:
I've had the same trouble with the openquery method. Perhaps we could try
that instead. Here, let me post the code, and yes, the query i'm trying to
run does refer to a value based on a second table. Here:

Public Function testfunction()
Dim adminQuery As Recordset
Dim processorQuery As Recordset
Dim recordCount As Recordset

Set adminQuery = CurrentDb.OpenRecordset("IsAdminQuery")
Set processorQuery = CurrentDb.OpenRecordset("IsProcessorQuery")
'Set recordCount = CurrentDb.OpenRecordset("CountOfUserExistingRecords")

If (adminQuery.BOF And adminQuery.EOF) Then
If (processorQuery.BOF And processorQuery.EOF) Then
DoCmd.Close
ElseIf (recordCount!RecordNum <= 0) Then
DoCmd.OpenForm ("Costpoint Processor New Record")
Else: DoCmd.OpenForm ("ReversalCostpoint Processor")
End If
Else: DoCmd.OpenForm ("Costpoint Reversals")
End If


End Function

The count of user existing records query is the problem. It looks like this:
SELECT Count(ReversalMemo.RecordNum) AS CountOfRecordNum
FROM ReversalMemo, UserIDQuery_SN_to_ProcName
HAVING
(((ReversalMemo.Signee1)=[UserIDQuery_SN_to_ProcName].[ProcessorName]));

The user_sn... query produces a user's true name based on a shortname from
their login. It's later used to filter their records. The purpose of this
particular part of the function is, if the user is listed as a processor, but
has 0 records (null or 0 result) the form opens up the new record form
instead of the processor form, which would be a blank white page since its
not for data entry.

Its basically a very simple decision tree. Is the user an admin? if yes,
open admin version. if no, is the user a processor? if yes, open the
processor form, showing them only their own records. if no, close. if the
processor is valid but has no records, start a new record.

The count of user records query uses two seperate tables in the same query
to determine if the record count. Running it through VB, it expects a
parameter value for the criteria i've set, since its not: concurrently
running, can't be determined without checking the row value determined by the
other query (user_sn_to...), I suppose, or explicitly defined. What else
can i provide that might help you see what i'm trying to accomplish, and the
simplest way to do it?

I'd also been advised to try a make-table query, to put the two values in
one table so that they could be easily read, but i can't compact on exit,
because i do not own the drive priveledges where the database is served, so i
would be left with lots of bloating :(


:

A parameter is a variable which a query can use in a number of ways, but most
frequently it restricts the rows returned. This is what a simple prompt of
the type you are probably used to does, but it can also be a reference to a
control on a form. Take a look at the Sales by Year query in the sample
Northwind database which comes with Access; this includes the criterion on
the ShippedDate column:

Is Not Null And Between [Forms]![Sales by Year Dialog]![BeginningDate] And
[Forms]![Sales by Year Dialog]![EndingDate]

The references to the [Forms]![Sales by Year Dialog]![BeginningDate] and
[Forms]![Sales by Year Dialog]![EndingDate]controls are parameters. The
procedure I sent you will work with a query containing this type of parameter
because it can be evaluated by the Eval function. A simple prompt would not
work, however, because that cannot be evaluated. If you look at the Sales
by Year query in SQL view you'll notice that the parameters are declared as
DateTime at the start of the SQL statement. In my experience people often
omit to do this, but with dates its actually quite important as otherwise a
parameter value entered as a date in short date format could be
misinterpreted as an arithmetical expression.

I'm not sure what you have in mind when you say "its provided by a second
table". A value in a column in another table is not a parameter.

One common cause of a 'parameter expected' error is because users
incorporate a reference to a control on a form in a literal SQL string in
code, e.g "WHERE LastName = Forms!MyForm!txtLastName". What should be done
in a case like that is to concatenate the value of the control into the SQL
string; "WHERE LastName = """ & Forms!MyForm!txtLastName & """". That's not
the case with the SQL you posted, however. The most likely culprit is a
parameter in the UserIDQuery_SN_to_ProcName, and my function would handle
that. Sometimes the error is just a result of a simple typo. If a column
name is entered in the string incorrectly Access will treat it as a
parameter; essentially anything it doesn't recognize as an object is treated
as a parameter. However, you say that the query opens fine using the same
SQL as a query rather than in code, which suggests there is no typo.

The fact that its opening as a query does beg the question why you want to
build and open via VBA at all. Why not just save the query and open it with
the OpenQuery method?

A querydef object is an object which defines a query (its short for 'query
definition'). Saved queries are querydef objects in the current databases
querydefs collection. A querydef object does not have to be a saved query,
however. One can be temporarily created in code. This is commonly done and
its rows accessed by means of a recordset object created with the
OpenRecordset method of the querydef object. In my function a querydef
object is created with the CreateQueryDef method of the database object and
saved by giving it name. Its then opened in datasheet view with the
OpenQuery method and finally deleted from the current databases querydefs
collection. So its not quite as temporary as a true temporary querydef
object.

As a simple example paste the following procedure into a standard module:

Public Sub OpenRstFromQuery(strQuery As String)

Dim dbs As DAO.Database, qdf As DAO.QueryDef
Dim prm As DAO.Parameter, rst As DAO.Recordset

Set dbs = CurrentDb
Set qdf = dbs.QueryDefs(strQuery)

For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

Set rst = qdf.OpenRecordset

With rst
Do While Not .EOF
Debug.Print .Fields(0); .Fields(1)
.MoveNext
Loop
End With

End Sub

Then in the debug window (aka immediate window) call it, passing it the name
of any simple query with at least two columns returned like so:

OpenRstFromQuery "NameOfQueryGoesHere"

You'll see that it lists the values from first two columns of the query to
the debug window.

Ken Sheridan
Stafford, England

:

oh wow. I understand what you're stating and the logic of it, but not all of
the code, i'll definately need some help with this. So the parameter i'm
speaking of is not one that pops up, requesting a parameter... its provided
by a second table, its never explicitly entered by the user. Does that
change anything? or perhaps i'm misunderstanding what you've stated.

Thanks for the info on the "having", i usually don't change the "group by"
unless i put in any criteria at all for that field, then i change it to a
where. What are the properties of a querydef object? what is its common
usage?



:

You need to loop through the query's parameters collection and evaluate the
parameters, e.g. with a procedure like this:

Public Sub OpenTempQuery(strSQL As String)

Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Dim prm As DAO.Parameter
Dim strQdf As String
Static n As Integer

Set dbs = CurrentDb

n = n + 1
strQdf = "qdfTemp" & n

' create temporary QueryDef object
Set qdf = dbs.CreateQueryDef(strQdf, strSQL)

' evaluate QueryDef object's parameters
For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

' open temporary query
DoCmd.OpenQuery strQdf

' delete temporary QueryDef object
dbs.QueryDefs.Delete qdf.Name

End Sub

You'd then call it, passing the SQL statement into the procedure as a string
expression, e.g.

Dim strSQL As String

strSQL = " SELECT COUNT(*) AS CountOfRows " & _
"FROM ReversalMemo, UserIDQuery_SN_to_ProcName " & _
"WHERE ReversalMemo.Signee1 = " & _
"UserIDQuery_SN_to_ProcName.ProcessorName"

OpenTempQuery strSQL

The parameters must be something which can be evaluated such as a reference
to a control on an open form such as Forms!MyForm!MyComboBox, and not a
simple system generated parameter prompt such as [Enter name:].

BTW note that in the SQL statement a WHERE clause should be used not a
HAVING clause, even though the latter will have the same effect in this case.
The HAVING clause results in this instance from designing the query in query
design view without changing the 'total' from 'Group By' to 'Where'. A
HAVING clause is used to restrict a query's result set after grouping and
usually on the basis of some aggregated value, e.g. HAVING SUM(Sales) >=
10000 to restrict the result set to rows with total sales of 10,000 currency
units or more. To restrict before grouping a WHERE clause should be used.

Ken Sheridan
Stafford, England

:

So i discovered through time that when it states that it has "too few
parameters, expected #" that what it really means is that the query you're
trying to run depends on something else that also has parameters which, since
they aren't explicitly being stated at this time, are considered 'parameters
not provided.' At any rate, my question is, how do i explicitly state where
the query should look for those values? the query uses two seperate tables,
and it looks like this, in sql:

SELECT Count(ReversalMemo.RecordNum) AS CountOfRecordNum
FROM ReversalMemo, UserIDQuery_SN_to_ProcName
HAVING
(((ReversalMemo.Signee1)=[UserIDQuery_SN_to_ProcName].[ProcessorName]));


run on its own in query view, it works fine. in code in VB, not so good :(
How do i explicitly provide the "parameter" it needs, or must i "recreate"
the query itself by finding the other table's value myself and coding the
logic used to determine the countofrecordnum's value?
 
P

Pwyd

Ken, i'm not seeing the parameter list. The sub gets pasted in a module and
run with the call you stated. The call is pasted after the recordsets are
opened. Is this the correct placement?



Ken Sheridan said:
Your GetUserID function almost certainly calls the Widows API GetUserName
function, so I think we can rule that out as the mysterious parameter.

A query doesn't have to be open when the DLookup function (or any other
domain function such as DCount, DMax etc) is called.

If you create a recordset object based on a query then its Fields collection
is the set of columns returned by the query, so it does in effect "adopt the
field names". You cannot refer directly to the value of a field returned by
a query; you either have to create a recordset object (as my sample
OpenRstFromQuery procedure does) or use one of the domain function such as
DLookup.

But we are getting away from the original problem of what is the expected
parameter. You should be able to list the parameters for the SQL statement
with the following function:

Public Sub ListParameters(strSQL As String)

Dim dbs As DAO.Database, qdf As DAO.QueryDef
Dim prm As DAO.Parameter

Set dbs = CurrentDb
Set qdf = dbs.CreateQueryDef("", strSQL)

For Each prm In qdf.Parameters
Debug.Print prm.Name
Next prm

End Sub

Call it like so:

ListParameters "SELECT * FROM CountOfUserExistingRecords"

Any parameters expected by the CountOfUserExistingRecords query should be
listed to the debug window. This should point you in the direction of the
cause of the problem, and hopefully to a solution.

Ken Sheridan
Stafford, England

Pwyd said:
i'm using a GetUserID() public function built with the help of the users
here, and yes i believe it uses the windows API.

Does dlookup instantiate the query on its own, or does it require the query
is running when it is called?

as for the column name, yes, that is how its referred to by me, i was
unsure if that was correct. Does the recordset object adopt the field names
of the thing it contains, or is it still proper to refer to the name of the
query itself thats being run?

In addition, is the way i've contstructed it improper, or is dlookup just
more efficient, faster, etc?



Ken Sheridan said:
How are you getting the current user's login name for the query? If you are
calling the Windows API GetUserName function that's not a parameter so should
be OK, but if you are getting it in some other way its possible it could be
being treated as a parameter.

Rather than creating recordset objects why not use the DLookup function to
get the value of any non-Null column from a query? Test for
IsNull(DLookup("ColumnName","QueryName")) to identify no rows returned, and
for DLookup("ColumnName","QueryName") = 0 to identify where the Count
operator returns a 0.

BTW don't use recordcount as an object variable's name; it’s a property name
in both DAO and ADO so should be avoided.

I also note that you refer to recordCount!RecordNum <= 0, but in the SQL
statement the column name is CountOfRecordNum.

Ken Sheridan
Stafford, England

:

I've had the same trouble with the openquery method. Perhaps we could try
that instead. Here, let me post the code, and yes, the query i'm trying to
run does refer to a value based on a second table. Here:

Public Function testfunction()
Dim adminQuery As Recordset
Dim processorQuery As Recordset
Dim recordCount As Recordset

Set adminQuery = CurrentDb.OpenRecordset("IsAdminQuery")
Set processorQuery = CurrentDb.OpenRecordset("IsProcessorQuery")
'Set recordCount = CurrentDb.OpenRecordset("CountOfUserExistingRecords")

If (adminQuery.BOF And adminQuery.EOF) Then
If (processorQuery.BOF And processorQuery.EOF) Then
DoCmd.Close
ElseIf (recordCount!RecordNum <= 0) Then
DoCmd.OpenForm ("Costpoint Processor New Record")
Else: DoCmd.OpenForm ("ReversalCostpoint Processor")
End If
Else: DoCmd.OpenForm ("Costpoint Reversals")
End If


End Function

The count of user existing records query is the problem. It looks like this:
SELECT Count(ReversalMemo.RecordNum) AS CountOfRecordNum
FROM ReversalMemo, UserIDQuery_SN_to_ProcName
HAVING
(((ReversalMemo.Signee1)=[UserIDQuery_SN_to_ProcName].[ProcessorName]));

The user_sn... query produces a user's true name based on a shortname from
their login. It's later used to filter their records. The purpose of this
particular part of the function is, if the user is listed as a processor, but
has 0 records (null or 0 result) the form opens up the new record form
instead of the processor form, which would be a blank white page since its
not for data entry.

Its basically a very simple decision tree. Is the user an admin? if yes,
open admin version. if no, is the user a processor? if yes, open the
processor form, showing them only their own records. if no, close. if the
processor is valid but has no records, start a new record.

The count of user records query uses two seperate tables in the same query
to determine if the record count. Running it through VB, it expects a
parameter value for the criteria i've set, since its not: concurrently
running, can't be determined without checking the row value determined by the
other query (user_sn_to...), I suppose, or explicitly defined. What else
can i provide that might help you see what i'm trying to accomplish, and the
simplest way to do it?

I'd also been advised to try a make-table query, to put the two values in
one table so that they could be easily read, but i can't compact on exit,
because i do not own the drive priveledges where the database is served, so i
would be left with lots of bloating :(


:

A parameter is a variable which a query can use in a number of ways, but most
frequently it restricts the rows returned. This is what a simple prompt of
the type you are probably used to does, but it can also be a reference to a
control on a form. Take a look at the Sales by Year query in the sample
Northwind database which comes with Access; this includes the criterion on
the ShippedDate column:

Is Not Null And Between [Forms]![Sales by Year Dialog]![BeginningDate] And
[Forms]![Sales by Year Dialog]![EndingDate]

The references to the [Forms]![Sales by Year Dialog]![BeginningDate] and
[Forms]![Sales by Year Dialog]![EndingDate]controls are parameters. The
procedure I sent you will work with a query containing this type of parameter
because it can be evaluated by the Eval function. A simple prompt would not
work, however, because that cannot be evaluated. If you look at the Sales
by Year query in SQL view you'll notice that the parameters are declared as
DateTime at the start of the SQL statement. In my experience people often
omit to do this, but with dates its actually quite important as otherwise a
parameter value entered as a date in short date format could be
misinterpreted as an arithmetical expression.

I'm not sure what you have in mind when you say "its provided by a second
table". A value in a column in another table is not a parameter.

One common cause of a 'parameter expected' error is because users
incorporate a reference to a control on a form in a literal SQL string in
code, e.g "WHERE LastName = Forms!MyForm!txtLastName". What should be done
in a case like that is to concatenate the value of the control into the SQL
string; "WHERE LastName = """ & Forms!MyForm!txtLastName & """". That's not
the case with the SQL you posted, however. The most likely culprit is a
parameter in the UserIDQuery_SN_to_ProcName, and my function would handle
that. Sometimes the error is just a result of a simple typo. If a column
name is entered in the string incorrectly Access will treat it as a
parameter; essentially anything it doesn't recognize as an object is treated
as a parameter. However, you say that the query opens fine using the same
SQL as a query rather than in code, which suggests there is no typo.

The fact that its opening as a query does beg the question why you want to
build and open via VBA at all. Why not just save the query and open it with
the OpenQuery method?

A querydef object is an object which defines a query (its short for 'query
definition'). Saved queries are querydef objects in the current databases
querydefs collection. A querydef object does not have to be a saved query,
however. One can be temporarily created in code. This is commonly done and
its rows accessed by means of a recordset object created with the
OpenRecordset method of the querydef object. In my function a querydef
object is created with the CreateQueryDef method of the database object and
saved by giving it name. Its then opened in datasheet view with the
OpenQuery method and finally deleted from the current databases querydefs
collection. So its not quite as temporary as a true temporary querydef
object.

As a simple example paste the following procedure into a standard module:

Public Sub OpenRstFromQuery(strQuery As String)

Dim dbs As DAO.Database, qdf As DAO.QueryDef
Dim prm As DAO.Parameter, rst As DAO.Recordset

Set dbs = CurrentDb
Set qdf = dbs.QueryDefs(strQuery)

For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

Set rst = qdf.OpenRecordset

With rst
Do While Not .EOF
Debug.Print .Fields(0); .Fields(1)
.MoveNext
Loop
End With

End Sub

Then in the debug window (aka immediate window) call it, passing it the name
of any simple query with at least two columns returned like so:

OpenRstFromQuery "NameOfQueryGoesHere"

You'll see that it lists the values from first two columns of the query to
the debug window.

Ken Sheridan
Stafford, England

:

oh wow. I understand what you're stating and the logic of it, but not all of
the code, i'll definately need some help with this. So the parameter i'm
speaking of is not one that pops up, requesting a parameter... its provided
by a second table, its never explicitly entered by the user. Does that
change anything? or perhaps i'm misunderstanding what you've stated.

Thanks for the info on the "having", i usually don't change the "group by"
unless i put in any criteria at all for that field, then i change it to a
where. What are the properties of a querydef object? what is its common
usage?



:

You need to loop through the query's parameters collection and evaluate the
parameters, e.g. with a procedure like this:

Public Sub OpenTempQuery(strSQL As String)

Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Dim prm As DAO.Parameter
Dim strQdf As String
Static n As Integer

Set dbs = CurrentDb

n = n + 1
strQdf = "qdfTemp" & n

' create temporary QueryDef object
Set qdf = dbs.CreateQueryDef(strQdf, strSQL)

' evaluate QueryDef object's parameters
For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

' open temporary query
DoCmd.OpenQuery strQdf

' delete temporary QueryDef object
dbs.QueryDefs.Delete qdf.Name

End Sub

You'd then call it, passing the SQL statement into the procedure as a string
expression, e.g.

Dim strSQL As String

strSQL = " SELECT COUNT(*) AS CountOfRows " & _
"FROM ReversalMemo, UserIDQuery_SN_to_ProcName " & _
"WHERE ReversalMemo.Signee1 = " & _
"UserIDQuery_SN_to_ProcName.ProcessorName"

OpenTempQuery strSQL

The parameters must be something which can be evaluated such as a reference
to a control on an open form such as Forms!MyForm!MyComboBox, and not a
simple system generated parameter prompt such as [Enter name:].

BTW note that in the SQL statement a WHERE clause should be used not a
HAVING clause, even though the latter will have the same effect in this case.
 
P

Pwyd

All right, i've got it. the Immediate window wasn't displayed. it's looking
for the following:

Forms!UserIDHiddenForm!Text0

which is a call to the GetUserID() function, which simply returns the
shortname of the user. the shortname gets checked on a table to see whether
it exists. So how do i feed that into Countofuserexistingrecords... as a
parameter so that it has what it needs to complete its recordset?

Incidentally, if i understood correctly, using dlookup would circumvent this
problem because it takes care of everything required to run the lookup?


Ken Sheridan said:
Your GetUserID function almost certainly calls the Widows API GetUserName
function, so I think we can rule that out as the mysterious parameter.

A query doesn't have to be open when the DLookup function (or any other
domain function such as DCount, DMax etc) is called.

If you create a recordset object based on a query then its Fields collection
is the set of columns returned by the query, so it does in effect "adopt the
field names". You cannot refer directly to the value of a field returned by
a query; you either have to create a recordset object (as my sample
OpenRstFromQuery procedure does) or use one of the domain function such as
DLookup.

But we are getting away from the original problem of what is the expected
parameter. You should be able to list the parameters for the SQL statement
with the following function:

Public Sub ListParameters(strSQL As String)

Dim dbs As DAO.Database, qdf As DAO.QueryDef
Dim prm As DAO.Parameter

Set dbs = CurrentDb
Set qdf = dbs.CreateQueryDef("", strSQL)

For Each prm In qdf.Parameters
Debug.Print prm.Name
Next prm

End Sub

Call it like so:

ListParameters "SELECT * FROM CountOfUserExistingRecords"

Any parameters expected by the CountOfUserExistingRecords query should be
listed to the debug window. This should point you in the direction of the
cause of the problem, and hopefully to a solution.

Ken Sheridan
Stafford, England

Pwyd said:
i'm using a GetUserID() public function built with the help of the users
here, and yes i believe it uses the windows API.

Does dlookup instantiate the query on its own, or does it require the query
is running when it is called?

as for the column name, yes, that is how its referred to by me, i was
unsure if that was correct. Does the recordset object adopt the field names
of the thing it contains, or is it still proper to refer to the name of the
query itself thats being run?

In addition, is the way i've contstructed it improper, or is dlookup just
more efficient, faster, etc?



Ken Sheridan said:
How are you getting the current user's login name for the query? If you are
calling the Windows API GetUserName function that's not a parameter so should
be OK, but if you are getting it in some other way its possible it could be
being treated as a parameter.

Rather than creating recordset objects why not use the DLookup function to
get the value of any non-Null column from a query? Test for
IsNull(DLookup("ColumnName","QueryName")) to identify no rows returned, and
for DLookup("ColumnName","QueryName") = 0 to identify where the Count
operator returns a 0.

BTW don't use recordcount as an object variable's name; it’s a property name
in both DAO and ADO so should be avoided.

I also note that you refer to recordCount!RecordNum <= 0, but in the SQL
statement the column name is CountOfRecordNum.

Ken Sheridan
Stafford, England

:

I've had the same trouble with the openquery method. Perhaps we could try
that instead. Here, let me post the code, and yes, the query i'm trying to
run does refer to a value based on a second table. Here:

Public Function testfunction()
Dim adminQuery As Recordset
Dim processorQuery As Recordset
Dim recordCount As Recordset

Set adminQuery = CurrentDb.OpenRecordset("IsAdminQuery")
Set processorQuery = CurrentDb.OpenRecordset("IsProcessorQuery")
'Set recordCount = CurrentDb.OpenRecordset("CountOfUserExistingRecords")

If (adminQuery.BOF And adminQuery.EOF) Then
If (processorQuery.BOF And processorQuery.EOF) Then
DoCmd.Close
ElseIf (recordCount!RecordNum <= 0) Then
DoCmd.OpenForm ("Costpoint Processor New Record")
Else: DoCmd.OpenForm ("ReversalCostpoint Processor")
End If
Else: DoCmd.OpenForm ("Costpoint Reversals")
End If


End Function

The count of user existing records query is the problem. It looks like this:
SELECT Count(ReversalMemo.RecordNum) AS CountOfRecordNum
FROM ReversalMemo, UserIDQuery_SN_to_ProcName
HAVING
(((ReversalMemo.Signee1)=[UserIDQuery_SN_to_ProcName].[ProcessorName]));

The user_sn... query produces a user's true name based on a shortname from
their login. It's later used to filter their records. The purpose of this
particular part of the function is, if the user is listed as a processor, but
has 0 records (null or 0 result) the form opens up the new record form
instead of the processor form, which would be a blank white page since its
not for data entry.

Its basically a very simple decision tree. Is the user an admin? if yes,
open admin version. if no, is the user a processor? if yes, open the
processor form, showing them only their own records. if no, close. if the
processor is valid but has no records, start a new record.

The count of user records query uses two seperate tables in the same query
to determine if the record count. Running it through VB, it expects a
parameter value for the criteria i've set, since its not: concurrently
running, can't be determined without checking the row value determined by the
other query (user_sn_to...), I suppose, or explicitly defined. What else
can i provide that might help you see what i'm trying to accomplish, and the
simplest way to do it?

I'd also been advised to try a make-table query, to put the two values in
one table so that they could be easily read, but i can't compact on exit,
because i do not own the drive priveledges where the database is served, so i
would be left with lots of bloating :(


:

A parameter is a variable which a query can use in a number of ways, but most
frequently it restricts the rows returned. This is what a simple prompt of
the type you are probably used to does, but it can also be a reference to a
control on a form. Take a look at the Sales by Year query in the sample
Northwind database which comes with Access; this includes the criterion on
the ShippedDate column:

Is Not Null And Between [Forms]![Sales by Year Dialog]![BeginningDate] And
[Forms]![Sales by Year Dialog]![EndingDate]

The references to the [Forms]![Sales by Year Dialog]![BeginningDate] and
[Forms]![Sales by Year Dialog]![EndingDate]controls are parameters. The
procedure I sent you will work with a query containing this type of parameter
because it can be evaluated by the Eval function. A simple prompt would not
work, however, because that cannot be evaluated. If you look at the Sales
by Year query in SQL view you'll notice that the parameters are declared as
DateTime at the start of the SQL statement. In my experience people often
omit to do this, but with dates its actually quite important as otherwise a
parameter value entered as a date in short date format could be
misinterpreted as an arithmetical expression.

I'm not sure what you have in mind when you say "its provided by a second
table". A value in a column in another table is not a parameter.

One common cause of a 'parameter expected' error is because users
incorporate a reference to a control on a form in a literal SQL string in
code, e.g "WHERE LastName = Forms!MyForm!txtLastName". What should be done
in a case like that is to concatenate the value of the control into the SQL
string; "WHERE LastName = """ & Forms!MyForm!txtLastName & """". That's not
the case with the SQL you posted, however. The most likely culprit is a
parameter in the UserIDQuery_SN_to_ProcName, and my function would handle
that. Sometimes the error is just a result of a simple typo. If a column
name is entered in the string incorrectly Access will treat it as a
parameter; essentially anything it doesn't recognize as an object is treated
as a parameter. However, you say that the query opens fine using the same
SQL as a query rather than in code, which suggests there is no typo.

The fact that its opening as a query does beg the question why you want to
build and open via VBA at all. Why not just save the query and open it with
the OpenQuery method?

A querydef object is an object which defines a query (its short for 'query
definition'). Saved queries are querydef objects in the current databases
querydefs collection. A querydef object does not have to be a saved query,
however. One can be temporarily created in code. This is commonly done and
its rows accessed by means of a recordset object created with the
OpenRecordset method of the querydef object. In my function a querydef
object is created with the CreateQueryDef method of the database object and
saved by giving it name. Its then opened in datasheet view with the
OpenQuery method and finally deleted from the current databases querydefs
collection. So its not quite as temporary as a true temporary querydef
object.

As a simple example paste the following procedure into a standard module:

Public Sub OpenRstFromQuery(strQuery As String)

Dim dbs As DAO.Database, qdf As DAO.QueryDef
Dim prm As DAO.Parameter, rst As DAO.Recordset

Set dbs = CurrentDb
Set qdf = dbs.QueryDefs(strQuery)

For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

Set rst = qdf.OpenRecordset

With rst
Do While Not .EOF
Debug.Print .Fields(0); .Fields(1)
.MoveNext
Loop
End With

End Sub

Then in the debug window (aka immediate window) call it, passing it the name
of any simple query with at least two columns returned like so:

OpenRstFromQuery "NameOfQueryGoesHere"

You'll see that it lists the values from first two columns of the query to
the debug window.

Ken Sheridan
Stafford, England

:

oh wow. I understand what you're stating and the logic of it, but not all of
the code, i'll definately need some help with this. So the parameter i'm
speaking of is not one that pops up, requesting a parameter... its provided
by a second table, its never explicitly entered by the user. Does that
change anything? or perhaps i'm misunderstanding what you've stated.

Thanks for the info on the "having", i usually don't change the "group by"
unless i put in any criteria at all for that field, then i change it to a
where. What are the properties of a querydef object? what is its common
usage?



:

You need to loop through the query's parameters collection and evaluate the
parameters, e.g. with a procedure like this:

Public Sub OpenTempQuery(strSQL As String)

Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Dim prm As DAO.Parameter
Dim strQdf As String
Static n As Integer

Set dbs = CurrentDb

n = n + 1
strQdf = "qdfTemp" & n

' create temporary QueryDef object
Set qdf = dbs.CreateQueryDef(strQdf, strSQL)

' evaluate QueryDef object's parameters
For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

' open temporary query
DoCmd.OpenQuery strQdf

' delete temporary QueryDef object
dbs.QueryDefs.Delete qdf.Name

End Sub

You'd then call it, passing the SQL statement into the procedure as a string
expression, e.g.

Dim strSQL As String

strSQL = " SELECT COUNT(*) AS CountOfRows " & _
"FROM ReversalMemo, UserIDQuery_SN_to_ProcName " & _
"WHERE ReversalMemo.Signee1 = " & _
"UserIDQuery_SN_to_ProcName.ProcessorName"

OpenTempQuery strSQL

The parameters must be something which can be evaluated such as a reference
to a control on an open form such as Forms!MyForm!MyComboBox, and not a
simple system generated parameter prompt such as [Enter name:].

BTW note that in the SQL statement a WHERE clause should be used not a
HAVING clause, even though the latter will have the same effect in this case.
 
K

Ken Sheridan

To create a recordset object from the query loop through the querydef
object's Parameters collection and evaluate each parameter. Then create the
recordset object by calling the querydef object's OpenRecordset method.
There's only one in this case so it’s a one iteration loop, but it’s the
standard approach which will handle zero to an arbitrary number of parameters:

For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

If using the DLookup, or any other domain function you are correct in saying
that this will handle the parameters automatically. The hidden form has to
be open (invisibly) of course.

BTW to display the debug window just press Ctrl+G.

Ken Sheridan
Stafford, England

Pwyd said:
All right, i've got it. the Immediate window wasn't displayed. it's looking
for the following:

Forms!UserIDHiddenForm!Text0

which is a call to the GetUserID() function, which simply returns the
shortname of the user. the shortname gets checked on a table to see whether
it exists. So how do i feed that into Countofuserexistingrecords... as a
parameter so that it has what it needs to complete its recordset?

Incidentally, if i understood correctly, using dlookup would circumvent this
problem because it takes care of everything required to run the lookup?


Ken Sheridan said:
Your GetUserID function almost certainly calls the Widows API GetUserName
function, so I think we can rule that out as the mysterious parameter.

A query doesn't have to be open when the DLookup function (or any other
domain function such as DCount, DMax etc) is called.

If you create a recordset object based on a query then its Fields collection
is the set of columns returned by the query, so it does in effect "adopt the
field names". You cannot refer directly to the value of a field returned by
a query; you either have to create a recordset object (as my sample
OpenRstFromQuery procedure does) or use one of the domain function such as
DLookup.

But we are getting away from the original problem of what is the expected
parameter. You should be able to list the parameters for the SQL statement
with the following function:

Public Sub ListParameters(strSQL As String)

Dim dbs As DAO.Database, qdf As DAO.QueryDef
Dim prm As DAO.Parameter

Set dbs = CurrentDb
Set qdf = dbs.CreateQueryDef("", strSQL)

For Each prm In qdf.Parameters
Debug.Print prm.Name
Next prm

End Sub

Call it like so:

ListParameters "SELECT * FROM CountOfUserExistingRecords"

Any parameters expected by the CountOfUserExistingRecords query should be
listed to the debug window. This should point you in the direction of the
cause of the problem, and hopefully to a solution.

Ken Sheridan
Stafford, England

Pwyd said:
i'm using a GetUserID() public function built with the help of the users
here, and yes i believe it uses the windows API.

Does dlookup instantiate the query on its own, or does it require the query
is running when it is called?

as for the column name, yes, that is how its referred to by me, i was
unsure if that was correct. Does the recordset object adopt the field names
of the thing it contains, or is it still proper to refer to the name of the
query itself thats being run?

In addition, is the way i've contstructed it improper, or is dlookup just
more efficient, faster, etc?



:

How are you getting the current user's login name for the query? If you are
calling the Windows API GetUserName function that's not a parameter so should
be OK, but if you are getting it in some other way its possible it could be
being treated as a parameter.

Rather than creating recordset objects why not use the DLookup function to
get the value of any non-Null column from a query? Test for
IsNull(DLookup("ColumnName","QueryName")) to identify no rows returned, and
for DLookup("ColumnName","QueryName") = 0 to identify where the Count
operator returns a 0.

BTW don't use recordcount as an object variable's name; it’s a property name
in both DAO and ADO so should be avoided.

I also note that you refer to recordCount!RecordNum <= 0, but in the SQL
statement the column name is CountOfRecordNum.

Ken Sheridan
Stafford, England

:

I've had the same trouble with the openquery method. Perhaps we could try
that instead. Here, let me post the code, and yes, the query i'm trying to
run does refer to a value based on a second table. Here:

Public Function testfunction()
Dim adminQuery As Recordset
Dim processorQuery As Recordset
Dim recordCount As Recordset

Set adminQuery = CurrentDb.OpenRecordset("IsAdminQuery")
Set processorQuery = CurrentDb.OpenRecordset("IsProcessorQuery")
'Set recordCount = CurrentDb.OpenRecordset("CountOfUserExistingRecords")

If (adminQuery.BOF And adminQuery.EOF) Then
If (processorQuery.BOF And processorQuery.EOF) Then
DoCmd.Close
ElseIf (recordCount!RecordNum <= 0) Then
DoCmd.OpenForm ("Costpoint Processor New Record")
Else: DoCmd.OpenForm ("ReversalCostpoint Processor")
End If
Else: DoCmd.OpenForm ("Costpoint Reversals")
End If


End Function

The count of user existing records query is the problem. It looks like this:
SELECT Count(ReversalMemo.RecordNum) AS CountOfRecordNum
FROM ReversalMemo, UserIDQuery_SN_to_ProcName
HAVING
(((ReversalMemo.Signee1)=[UserIDQuery_SN_to_ProcName].[ProcessorName]));

The user_sn... query produces a user's true name based on a shortname from
their login. It's later used to filter their records. The purpose of this
particular part of the function is, if the user is listed as a processor, but
has 0 records (null or 0 result) the form opens up the new record form
instead of the processor form, which would be a blank white page since its
not for data entry.

Its basically a very simple decision tree. Is the user an admin? if yes,
open admin version. if no, is the user a processor? if yes, open the
processor form, showing them only their own records. if no, close. if the
processor is valid but has no records, start a new record.

The count of user records query uses two seperate tables in the same query
to determine if the record count. Running it through VB, it expects a
parameter value for the criteria i've set, since its not: concurrently
running, can't be determined without checking the row value determined by the
other query (user_sn_to...), I suppose, or explicitly defined. What else
can i provide that might help you see what i'm trying to accomplish, and the
simplest way to do it?

I'd also been advised to try a make-table query, to put the two values in
one table so that they could be easily read, but i can't compact on exit,
because i do not own the drive priveledges where the database is served, so i
would be left with lots of bloating :(


:

A parameter is a variable which a query can use in a number of ways, but most
frequently it restricts the rows returned. This is what a simple prompt of
the type you are probably used to does, but it can also be a reference to a
control on a form. Take a look at the Sales by Year query in the sample
Northwind database which comes with Access; this includes the criterion on
the ShippedDate column:

Is Not Null And Between [Forms]![Sales by Year Dialog]![BeginningDate] And
[Forms]![Sales by Year Dialog]![EndingDate]

The references to the [Forms]![Sales by Year Dialog]![BeginningDate] and
[Forms]![Sales by Year Dialog]![EndingDate]controls are parameters. The
procedure I sent you will work with a query containing this type of parameter
because it can be evaluated by the Eval function. A simple prompt would not
work, however, because that cannot be evaluated. If you look at the Sales
by Year query in SQL view you'll notice that the parameters are declared as
DateTime at the start of the SQL statement. In my experience people often
omit to do this, but with dates its actually quite important as otherwise a
parameter value entered as a date in short date format could be
misinterpreted as an arithmetical expression.

I'm not sure what you have in mind when you say "its provided by a second
table". A value in a column in another table is not a parameter.

One common cause of a 'parameter expected' error is because users
incorporate a reference to a control on a form in a literal SQL string in
code, e.g "WHERE LastName = Forms!MyForm!txtLastName". What should be done
in a case like that is to concatenate the value of the control into the SQL
string; "WHERE LastName = """ & Forms!MyForm!txtLastName & """". That's not
the case with the SQL you posted, however. The most likely culprit is a
parameter in the UserIDQuery_SN_to_ProcName, and my function would handle
that. Sometimes the error is just a result of a simple typo. If a column
name is entered in the string incorrectly Access will treat it as a
parameter; essentially anything it doesn't recognize as an object is treated
as a parameter. However, you say that the query opens fine using the same
SQL as a query rather than in code, which suggests there is no typo.

The fact that its opening as a query does beg the question why you want to
build and open via VBA at all. Why not just save the query and open it with
the OpenQuery method?

A querydef object is an object which defines a query (its short for 'query
definition'). Saved queries are querydef objects in the current databases
querydefs collection. A querydef object does not have to be a saved query,
however. One can be temporarily created in code. This is commonly done and
its rows accessed by means of a recordset object created with the
OpenRecordset method of the querydef object. In my function a querydef
object is created with the CreateQueryDef method of the database object and
saved by giving it name. Its then opened in datasheet view with the
OpenQuery method and finally deleted from the current databases querydefs
collection. So its not quite as temporary as a true temporary querydef
object.

As a simple example paste the following procedure into a standard module:

Public Sub OpenRstFromQuery(strQuery As String)

Dim dbs As DAO.Database, qdf As DAO.QueryDef
Dim prm As DAO.Parameter, rst As DAO.Recordset

Set dbs = CurrentDb
Set qdf = dbs.QueryDefs(strQuery)

For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

Set rst = qdf.OpenRecordset

With rst
Do While Not .EOF
Debug.Print .Fields(0); .Fields(1)
.MoveNext
Loop
End With

End Sub

Then in the debug window (aka immediate window) call it, passing it the name
of any simple query with at least two columns returned like so:

OpenRstFromQuery "NameOfQueryGoesHere"

You'll see that it lists the values from first two columns of the query to
the debug window.

Ken Sheridan
Stafford, England

:

oh wow. I understand what you're stating and the logic of it, but not all of
the code, i'll definately need some help with this. So the parameter i'm
speaking of is not one that pops up, requesting a parameter... its provided
by a second table, its never explicitly entered by the user. Does that
change anything? or perhaps i'm misunderstanding what you've stated.

Thanks for the info on the "having", i usually don't change the "group by"
unless i put in any criteria at all for that field, then i change it to a
where. What are the properties of a querydef object? what is its common
usage?



:

You need to loop through the query's parameters collection and evaluate the
parameters, e.g. with a procedure like this:

Public Sub OpenTempQuery(strSQL As String)

Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Dim prm As DAO.Parameter
Dim strQdf As String
Static n As Integer

Set dbs = CurrentDb

n = n + 1
strQdf = "qdfTemp" & n

' create temporary QueryDef object
Set qdf = dbs.CreateQueryDef(strQdf, strSQL)

' evaluate QueryDef object's parameters
For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

' open temporary query
DoCmd.OpenQuery strQdf

' delete temporary QueryDef object
dbs.QueryDefs.Delete qdf.Name

End Sub

You'd then call it, passing the SQL statement into the procedure as a string
expression, e.g.
 
P

Pwyd

All right, so in the parameter situation, how would you then feed those
parameters to the object that requires them? i've tried using parens and
putting them as arguments, but thats not working.


As for dlookup, it keeps giving me "You cancelled the operation blah blah."
i checked all the spellings of the items, found no errors. Tried compiling
it instead of running it and it keeps telling me that i'm using an external
name :(
I must be referencing something it doesn't recognize.

blah = DLookup("[RecordNum]", "[ReversalMemo]", "[Signee1] =
[Forms]![UserIDHiddenForm]![Text0]")

I'm gonna write a function to perform the function of the query i'm putting
into the listbox, since i can't make a comparison to a listbox's contents :(


MsgBox (blah)

Ken Sheridan said:
To create a recordset object from the query loop through the querydef
object's Parameters collection and evaluate each parameter. Then create the
recordset object by calling the querydef object's OpenRecordset method.
There's only one in this case so it’s a one iteration loop, but it’s the
standard approach which will handle zero to an arbitrary number of parameters:

For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

If using the DLookup, or any other domain function you are correct in saying
that this will handle the parameters automatically. The hidden form has to
be open (invisibly) of course.

BTW to display the debug window just press Ctrl+G.

Ken Sheridan
Stafford, England

Pwyd said:
All right, i've got it. the Immediate window wasn't displayed. it's looking
for the following:

Forms!UserIDHiddenForm!Text0

which is a call to the GetUserID() function, which simply returns the
shortname of the user. the shortname gets checked on a table to see whether
it exists. So how do i feed that into Countofuserexistingrecords... as a
parameter so that it has what it needs to complete its recordset?

Incidentally, if i understood correctly, using dlookup would circumvent this
problem because it takes care of everything required to run the lookup?


Ken Sheridan said:
Your GetUserID function almost certainly calls the Widows API GetUserName
function, so I think we can rule that out as the mysterious parameter.

A query doesn't have to be open when the DLookup function (or any other
domain function such as DCount, DMax etc) is called.

If you create a recordset object based on a query then its Fields collection
is the set of columns returned by the query, so it does in effect "adopt the
field names". You cannot refer directly to the value of a field returned by
a query; you either have to create a recordset object (as my sample
OpenRstFromQuery procedure does) or use one of the domain function such as
DLookup.

But we are getting away from the original problem of what is the expected
parameter. You should be able to list the parameters for the SQL statement
with the following function:

Public Sub ListParameters(strSQL As String)

Dim dbs As DAO.Database, qdf As DAO.QueryDef
Dim prm As DAO.Parameter

Set dbs = CurrentDb
Set qdf = dbs.CreateQueryDef("", strSQL)

For Each prm In qdf.Parameters
Debug.Print prm.Name
Next prm

End Sub

Call it like so:

ListParameters "SELECT * FROM CountOfUserExistingRecords"

Any parameters expected by the CountOfUserExistingRecords query should be
listed to the debug window. This should point you in the direction of the
cause of the problem, and hopefully to a solution.

Ken Sheridan
Stafford, England

:

i'm using a GetUserID() public function built with the help of the users
here, and yes i believe it uses the windows API.

Does dlookup instantiate the query on its own, or does it require the query
is running when it is called?

as for the column name, yes, that is how its referred to by me, i was
unsure if that was correct. Does the recordset object adopt the field names
of the thing it contains, or is it still proper to refer to the name of the
query itself thats being run?

In addition, is the way i've contstructed it improper, or is dlookup just
more efficient, faster, etc?



:

How are you getting the current user's login name for the query? If you are
calling the Windows API GetUserName function that's not a parameter so should
be OK, but if you are getting it in some other way its possible it could be
being treated as a parameter.

Rather than creating recordset objects why not use the DLookup function to
get the value of any non-Null column from a query? Test for
IsNull(DLookup("ColumnName","QueryName")) to identify no rows returned, and
for DLookup("ColumnName","QueryName") = 0 to identify where the Count
operator returns a 0.

BTW don't use recordcount as an object variable's name; it’s a property name
in both DAO and ADO so should be avoided.

I also note that you refer to recordCount!RecordNum <= 0, but in the SQL
statement the column name is CountOfRecordNum.

Ken Sheridan
Stafford, England

:

I've had the same trouble with the openquery method. Perhaps we could try
that instead. Here, let me post the code, and yes, the query i'm trying to
run does refer to a value based on a second table. Here:

Public Function testfunction()
Dim adminQuery As Recordset
Dim processorQuery As Recordset
Dim recordCount As Recordset

Set adminQuery = CurrentDb.OpenRecordset("IsAdminQuery")
Set processorQuery = CurrentDb.OpenRecordset("IsProcessorQuery")
'Set recordCount = CurrentDb.OpenRecordset("CountOfUserExistingRecords")

If (adminQuery.BOF And adminQuery.EOF) Then
If (processorQuery.BOF And processorQuery.EOF) Then
DoCmd.Close
ElseIf (recordCount!RecordNum <= 0) Then
DoCmd.OpenForm ("Costpoint Processor New Record")
Else: DoCmd.OpenForm ("ReversalCostpoint Processor")
End If
Else: DoCmd.OpenForm ("Costpoint Reversals")
End If


End Function

The count of user existing records query is the problem. It looks like this:
SELECT Count(ReversalMemo.RecordNum) AS CountOfRecordNum
FROM ReversalMemo, UserIDQuery_SN_to_ProcName
HAVING
(((ReversalMemo.Signee1)=[UserIDQuery_SN_to_ProcName].[ProcessorName]));

The user_sn... query produces a user's true name based on a shortname from
their login. It's later used to filter their records. The purpose of this
particular part of the function is, if the user is listed as a processor, but
has 0 records (null or 0 result) the form opens up the new record form
instead of the processor form, which would be a blank white page since its
not for data entry.

Its basically a very simple decision tree. Is the user an admin? if yes,
open admin version. if no, is the user a processor? if yes, open the
processor form, showing them only their own records. if no, close. if the
processor is valid but has no records, start a new record.

The count of user records query uses two seperate tables in the same query
to determine if the record count. Running it through VB, it expects a
parameter value for the criteria i've set, since its not: concurrently
running, can't be determined without checking the row value determined by the
other query (user_sn_to...), I suppose, or explicitly defined. What else
can i provide that might help you see what i'm trying to accomplish, and the
simplest way to do it?

I'd also been advised to try a make-table query, to put the two values in
one table so that they could be easily read, but i can't compact on exit,
because i do not own the drive priveledges where the database is served, so i
would be left with lots of bloating :(


:

A parameter is a variable which a query can use in a number of ways, but most
frequently it restricts the rows returned. This is what a simple prompt of
the type you are probably used to does, but it can also be a reference to a
control on a form. Take a look at the Sales by Year query in the sample
Northwind database which comes with Access; this includes the criterion on
the ShippedDate column:

Is Not Null And Between [Forms]![Sales by Year Dialog]![BeginningDate] And
[Forms]![Sales by Year Dialog]![EndingDate]

The references to the [Forms]![Sales by Year Dialog]![BeginningDate] and
[Forms]![Sales by Year Dialog]![EndingDate]controls are parameters. The
procedure I sent you will work with a query containing this type of parameter
because it can be evaluated by the Eval function. A simple prompt would not
work, however, because that cannot be evaluated. If you look at the Sales
by Year query in SQL view you'll notice that the parameters are declared as
DateTime at the start of the SQL statement. In my experience people often
omit to do this, but with dates its actually quite important as otherwise a
parameter value entered as a date in short date format could be
misinterpreted as an arithmetical expression.

I'm not sure what you have in mind when you say "its provided by a second
table". A value in a column in another table is not a parameter.

One common cause of a 'parameter expected' error is because users
incorporate a reference to a control on a form in a literal SQL string in
code, e.g "WHERE LastName = Forms!MyForm!txtLastName". What should be done
in a case like that is to concatenate the value of the control into the SQL
string; "WHERE LastName = """ & Forms!MyForm!txtLastName & """". That's not
the case with the SQL you posted, however. The most likely culprit is a
parameter in the UserIDQuery_SN_to_ProcName, and my function would handle
that. Sometimes the error is just a result of a simple typo. If a column
name is entered in the string incorrectly Access will treat it as a
parameter; essentially anything it doesn't recognize as an object is treated
as a parameter. However, you say that the query opens fine using the same
SQL as a query rather than in code, which suggests there is no typo.

The fact that its opening as a query does beg the question why you want to
build and open via VBA at all. Why not just save the query and open it with
the OpenQuery method?

A querydef object is an object which defines a query (its short for 'query
definition'). Saved queries are querydef objects in the current databases
querydefs collection. A querydef object does not have to be a saved query,
however. One can be temporarily created in code. This is commonly done and
its rows accessed by means of a recordset object created with the
OpenRecordset method of the querydef object. In my function a querydef
object is created with the CreateQueryDef method of the database object and
saved by giving it name. Its then opened in datasheet view with the
OpenQuery method and finally deleted from the current databases querydefs
collection. So its not quite as temporary as a true temporary querydef
object.

As a simple example paste the following procedure into a standard module:

Public Sub OpenRstFromQuery(strQuery As String)

Dim dbs As DAO.Database, qdf As DAO.QueryDef
Dim prm As DAO.Parameter, rst As DAO.Recordset

Set dbs = CurrentDb
Set qdf = dbs.QueryDefs(strQuery)

For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

Set rst = qdf.OpenRecordset

With rst
Do While Not .EOF
Debug.Print .Fields(0); .Fields(1)
.MoveNext
Loop
End With

End Sub

Then in the debug window (aka immediate window) call it, passing it the name
of any simple query with at least two columns returned like so:

OpenRstFromQuery "NameOfQueryGoesHere"

You'll see that it lists the values from first two columns of the query to
the debug window.

Ken Sheridan
Stafford, England

:

oh wow. I understand what you're stating and the logic of it, but not all of
the code, i'll definately need some help with this. So the parameter i'm
speaking of is not one that pops up, requesting a parameter... its provided
by a second table, its never explicitly entered by the user. Does that
change anything? or perhaps i'm misunderstanding what you've stated.

Thanks for the info on the "having", i usually don't change the "group by"
unless i put in any criteria at all for that field, then i change it to a
where. What are the properties of a querydef object? what is its common
usage?



:

You need to loop through the query's parameters collection and evaluate the
parameters, e.g. with a procedure like this:

Public Sub OpenTempQuery(strSQL As String)

Dim dbs As DAO.Database
Dim qdf As DAO.QueryDef
Dim prm As DAO.Parameter
Dim strQdf As String
Static n As Integer

Set dbs = CurrentDb
 
K

Ken Sheridan

1. Take a look at the code in my sample procedure, adapted below:

Dim dbs As DAO.Database, qdf As DAO.QueryDef
Dim prm As DAO.Parameter, rst As DAO.Recordset

Set dbs = CurrentDb
Set qdf = dbs.QueryDefs("NameOfQuery")

For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

Set rst = qdf.OpenRecordset

You can then access the recordset's fields in the usual way.

2. Concatenate the value of the control into the string expression:

DLookup("[RecordNum]", "[ReversalMemo]", "[Signee] = """ &
[Forms]![UserIDHiddenForm]![Text0] & """")

Ken Sheridan
Stafford, England

Pwyd said:
All right, so in the parameter situation, how would you then feed those
parameters to the object that requires them? i've tried using parens and
putting them as arguments, but thats not working.


As for dlookup, it keeps giving me "You cancelled the operation blah blah."
i checked all the spellings of the items, found no errors. Tried compiling
it instead of running it and it keeps telling me that i'm using an external
name :(
I must be referencing something it doesn't recognize.

blah = DLookup("[RecordNum]", "[ReversalMemo]", "[Signee1] =
[Forms]![UserIDHiddenForm]![Text0]")

I'm gonna write a function to perform the function of the query i'm putting
into the listbox, since i can't make a comparison to a listbox's contents :(


MsgBox (blah)

Ken Sheridan said:
To create a recordset object from the query loop through the querydef
object's Parameters collection and evaluate each parameter. Then create the
recordset object by calling the querydef object's OpenRecordset method.
There's only one in this case so it’s a one iteration loop, but it’s the
standard approach which will handle zero to an arbitrary number of parameters:

For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

If using the DLookup, or any other domain function you are correct in saying
that this will handle the parameters automatically. The hidden form has to
be open (invisibly) of course.

BTW to display the debug window just press Ctrl+G.

Ken Sheridan
Stafford, England

Pwyd said:
All right, i've got it. the Immediate window wasn't displayed. it's looking
for the following:

Forms!UserIDHiddenForm!Text0

which is a call to the GetUserID() function, which simply returns the
shortname of the user. the shortname gets checked on a table to see whether
it exists. So how do i feed that into Countofuserexistingrecords... as a
parameter so that it has what it needs to complete its recordset?

Incidentally, if i understood correctly, using dlookup would circumvent this
problem because it takes care of everything required to run the lookup?


:

Your GetUserID function almost certainly calls the Widows API GetUserName
function, so I think we can rule that out as the mysterious parameter.

A query doesn't have to be open when the DLookup function (or any other
domain function such as DCount, DMax etc) is called.

If you create a recordset object based on a query then its Fields collection
is the set of columns returned by the query, so it does in effect "adopt the
field names". You cannot refer directly to the value of a field returned by
a query; you either have to create a recordset object (as my sample
OpenRstFromQuery procedure does) or use one of the domain function such as
DLookup.

But we are getting away from the original problem of what is the expected
parameter. You should be able to list the parameters for the SQL statement
with the following function:

Public Sub ListParameters(strSQL As String)

Dim dbs As DAO.Database, qdf As DAO.QueryDef
Dim prm As DAO.Parameter

Set dbs = CurrentDb
Set qdf = dbs.CreateQueryDef("", strSQL)

For Each prm In qdf.Parameters
Debug.Print prm.Name
Next prm

End Sub

Call it like so:

ListParameters "SELECT * FROM CountOfUserExistingRecords"

Any parameters expected by the CountOfUserExistingRecords query should be
listed to the debug window. This should point you in the direction of the
cause of the problem, and hopefully to a solution.

Ken Sheridan
Stafford, England

:

i'm using a GetUserID() public function built with the help of the users
here, and yes i believe it uses the windows API.

Does dlookup instantiate the query on its own, or does it require the query
is running when it is called?

as for the column name, yes, that is how its referred to by me, i was
unsure if that was correct. Does the recordset object adopt the field names
of the thing it contains, or is it still proper to refer to the name of the
query itself thats being run?

In addition, is the way i've contstructed it improper, or is dlookup just
more efficient, faster, etc?



:

How are you getting the current user's login name for the query? If you are
calling the Windows API GetUserName function that's not a parameter so should
be OK, but if you are getting it in some other way its possible it could be
being treated as a parameter.

Rather than creating recordset objects why not use the DLookup function to
get the value of any non-Null column from a query? Test for
IsNull(DLookup("ColumnName","QueryName")) to identify no rows returned, and
for DLookup("ColumnName","QueryName") = 0 to identify where the Count
operator returns a 0.

BTW don't use recordcount as an object variable's name; it’s a property name
in both DAO and ADO so should be avoided.

I also note that you refer to recordCount!RecordNum <= 0, but in the SQL
statement the column name is CountOfRecordNum.

Ken Sheridan
Stafford, England

:

I've had the same trouble with the openquery method. Perhaps we could try
that instead. Here, let me post the code, and yes, the query i'm trying to
run does refer to a value based on a second table. Here:

Public Function testfunction()
Dim adminQuery As Recordset
Dim processorQuery As Recordset
Dim recordCount As Recordset

Set adminQuery = CurrentDb.OpenRecordset("IsAdminQuery")
Set processorQuery = CurrentDb.OpenRecordset("IsProcessorQuery")
'Set recordCount = CurrentDb.OpenRecordset("CountOfUserExistingRecords")

If (adminQuery.BOF And adminQuery.EOF) Then
If (processorQuery.BOF And processorQuery.EOF) Then
DoCmd.Close
ElseIf (recordCount!RecordNum <= 0) Then
DoCmd.OpenForm ("Costpoint Processor New Record")
Else: DoCmd.OpenForm ("ReversalCostpoint Processor")
End If
Else: DoCmd.OpenForm ("Costpoint Reversals")
End If


End Function

The count of user existing records query is the problem. It looks like this:
SELECT Count(ReversalMemo.RecordNum) AS CountOfRecordNum
FROM ReversalMemo, UserIDQuery_SN_to_ProcName
HAVING
(((ReversalMemo.Signee1)=[UserIDQuery_SN_to_ProcName].[ProcessorName]));

The user_sn... query produces a user's true name based on a shortname from
their login. It's later used to filter their records. The purpose of this
particular part of the function is, if the user is listed as a processor, but
has 0 records (null or 0 result) the form opens up the new record form
instead of the processor form, which would be a blank white page since its
not for data entry.

Its basically a very simple decision tree. Is the user an admin? if yes,
open admin version. if no, is the user a processor? if yes, open the
processor form, showing them only their own records. if no, close. if the
processor is valid but has no records, start a new record.

The count of user records query uses two seperate tables in the same query
to determine if the record count. Running it through VB, it expects a
parameter value for the criteria i've set, since its not: concurrently
running, can't be determined without checking the row value determined by the
other query (user_sn_to...), I suppose, or explicitly defined. What else
can i provide that might help you see what i'm trying to accomplish, and the
simplest way to do it?

I'd also been advised to try a make-table query, to put the two values in
one table so that they could be easily read, but i can't compact on exit,
because i do not own the drive priveledges where the database is served, so i
would be left with lots of bloating :(


:

A parameter is a variable which a query can use in a number of ways, but most
frequently it restricts the rows returned. This is what a simple prompt of
the type you are probably used to does, but it can also be a reference to a
control on a form. Take a look at the Sales by Year query in the sample
Northwind database which comes with Access; this includes the criterion on
the ShippedDate column:

Is Not Null And Between [Forms]![Sales by Year Dialog]![BeginningDate] And
[Forms]![Sales by Year Dialog]![EndingDate]

The references to the [Forms]![Sales by Year Dialog]![BeginningDate] and
[Forms]![Sales by Year Dialog]![EndingDate]controls are parameters. The
procedure I sent you will work with a query containing this type of parameter
because it can be evaluated by the Eval function. A simple prompt would not
work, however, because that cannot be evaluated. If you look at the Sales
by Year query in SQL view you'll notice that the parameters are declared as
DateTime at the start of the SQL statement. In my experience people often
omit to do this, but with dates its actually quite important as otherwise a
parameter value entered as a date in short date format could be
misinterpreted as an arithmetical expression.

I'm not sure what you have in mind when you say "its provided by a second
table". A value in a column in another table is not a parameter.

One common cause of a 'parameter expected' error is because users
incorporate a reference to a control on a form in a literal SQL string in
code, e.g "WHERE LastName = Forms!MyForm!txtLastName". What should be done
in a case like that is to concatenate the value of the control into the SQL
string; "WHERE LastName = """ & Forms!MyForm!txtLastName & """". That's not
the case with the SQL you posted, however. The most likely culprit is a
parameter in the UserIDQuery_SN_to_ProcName, and my function would handle
that. Sometimes the error is just a result of a simple typo. If a column
name is entered in the string incorrectly Access will treat it as a
parameter; essentially anything it doesn't recognize as an object is treated
as a parameter. However, you say that the query opens fine using the same
SQL as a query rather than in code, which suggests there is no typo.

The fact that its opening as a query does beg the question why you want to
build and open via VBA at all. Why not just save the query and open it with
the OpenQuery method?

A querydef object is an object which defines a query (its short for 'query
definition'). Saved queries are querydef objects in the current databases
querydefs collection. A querydef object does not have to be a saved query,
however. One can be temporarily created in code. This is commonly done and
its rows accessed by means of a recordset object created with the
OpenRecordset method of the querydef object. In my function a querydef
object is created with the CreateQueryDef method of the database object and
saved by giving it name. Its then opened in datasheet view with the
OpenQuery method and finally deleted from the current databases querydefs
collection. So its not quite as temporary as a true temporary querydef
object.

As a simple example paste the following procedure into a standard module:

Public Sub OpenRstFromQuery(strQuery As String)

Dim dbs As DAO.Database, qdf As DAO.QueryDef
Dim prm As DAO.Parameter, rst As DAO.Recordset

Set dbs = CurrentDb
Set qdf = dbs.QueryDefs(strQuery)

For Each prm In qdf.Parameters
prm = Eval(prm.Name)
Next prm

Set rst = qdf.OpenRecordset

With rst
Do While Not .EOF
Debug.Print .Fields(0); .Fields(1)
.MoveNext
Loop
End With

End Sub

Then in the debug window (aka immediate window) call it, passing it the name
of any simple query with at least two columns returned like so:

OpenRstFromQuery "NameOfQueryGoesHere"

You'll see that it lists the values from first two columns of the query to
the debug window.

Ken Sheridan
Stafford, England

:

oh wow. I understand what you're stating and the logic of it, but not all of
the code, i'll definately need some help with this. So the parameter i'm
speaking of is not one that pops up, requesting a parameter... its provided
by a second table, its never explicitly entered by the user. Does that
change anything? or perhaps i'm misunderstanding what you've stated.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top