Allen Browne's Filter - Modification troubles

M

markmarko

I have built a filtering system for our sales order modeled after Allen
Browne's Search procedure (http://www.allenbrowne.com/ser-62.html).

Some of the criteria are applied to the forms filter, and others to the
forms recordsource.

I originally made a handful of queries and based on criteria, the code
selected the correct query for the recordsource. I'm going to have more
options, so I decided it would be better to just have the code 'build' an sql
statement for the recordsource.

I'm having a problem creating the sql expression. But first I need to find
out how to apply it...

Is it possible to make a form's recordsource an sql statement? If not, if I
make a query the recordsource, can I programatically change the sql?
 
J

Jeanette Cunningham

Hi,
you can go
Dim strSQL as String
strSQL = "SELECT . . ."
Me.RecordSource = strSQL

the above sets the forms recordsource
you can programmatically change the sql and then you just go
Me.RecordSource = strSQL
again

Jeanette Cunningham
 
M

markmarko

Thanks Jeanette,

I was hoping that was possible... I tested putting an sql statement (copied
from a query) in the recordsource property directly, and it crashed access.
So, I'll take your word for it that vba can do it.

Now on to the tricky part....
In order to 'filter' by Salesperson (Salesperson is related to Sales Order
via a junction table, since a Sales Order may have more than one
Salesperson). Here's the sql:

SELECT [Record-Orders-Sales].*, [Query-Junction-Cust_Account].*,
[Junction-SalesOrder_Contractors].AssociatedContractorID
FROM ([Record-Orders-Sales] INNER JOIN [Query-Junction-Cust_Account] ON
[Record-Orders-Sales].[AssociatedCustAcct#Junction] =
[Query-Junction-Cust_Account].JunctionID) INNER JOIN
[Junction-SalesOrder_Contractors] ON [Record-Orders-Sales].SalesOrderID =
[Junction-SalesOrder_Contractors].AssociatedSalesOrderID
WHERE
((([Junction-SalesOrder_Contractors].AssociatedContractorID)=[Forms]![Entry-SalesOrder]![Filter-SalesRep1]))
ORDER BY [Record-Orders-Sales].SalesOrderID;

In order to filter by 2 sales peeps, I'd want to add to the string ala Allen
Browne's filter model, but the a bit I'd need to add would be 'injected' into
the above sql, not at the end.

Here's what the sql would look like for filter by 2 sales peeps, (again,
from a query (that works)).
SELECT [Record-Orders-Sales].*, [Query-Junction-Cust_Account].*,
[Junction-SalesOrder_Contractors].AssociatedContractorID,
[Junction-SalesOrder_Contractors_1].AssociatedContractorID
FROM ([Junction-SalesOrder_Contractors] AS
[Junction-SalesOrder_Contractors_1] INNER JOIN ([Record-Orders-Sales] INNER
JOIN [Query-Junction-Cust_Account] ON
[Record-Orders-Sales].[AssociatedCustAcct#Junction] =
[Query-Junction-Cust_Account].JunctionID) ON
[Junction-SalesOrder_Contractors_1].AssociatedSalesOrderID =
[Record-Orders-Sales].SalesOrderID) INNER JOIN
[Junction-SalesOrder_Contractors] ON [Record-Orders-Sales].SalesOrderID =
[Junction-SalesOrder_Contractors].AssociatedSalesOrderID
WHERE
((([Junction-SalesOrder_Contractors].AssociatedContractorID)=[Forms]![Entry-SalesOrder]![Filter-SalesRep1])
AND
(([Junction-SalesOrder_Contractors_1].AssociatedContractorID)=[Forms]![Entry-SalesOrder]![Filter-SalesRep2]))
ORDER BY [Record-Orders-Sales].SalesOrderID;

It's the same as the first version, with the
[Junction-SalesOrder_Contractors_1] bits added. The trick is that it's added
kind of in the middle. For this one instance, I'm sure I could make it work,
but I want to make a model that I could scale to allow building the
recordsource sql to include other factors, and need a systematic way to add
to the sqlString.

Any advice?
 
A

Allen Browne

Open the form in design view
Open the properties box to look at the properties of the form.
Click in the RecordSource property (Data tab.)
Click the Build button (...) beside this.
Access opens the query builder, so you can build the SQL statement.
(If the RecordSource was a saved query it opens that.
If it was a table, it asks if you want to build a query instead.)
Behind the scenes, it saves this as a hidden query anyway, but it does work
as a SQL statement.

If it crashed Access, then something else is wrong.
Perhaps you tried to do this in Form view (not design view.)
Perhaps you omitted a field: Access can crash if a field suddenly disappears
or changes data type while the form is open.
Or perhaps it's a Name AutoCorrect error:
http://allenbrowne.com/bug-03.html
Or it could be a corrupt form. (Try a compact/repair.)

Once you have this working, your next question was about how to
programmatically change the RecordSource while the form is running. You
don't want to change the stub of the SQL statement (the SELECT and FROM
clauses), and probably not the tail either (ORDER BY, GROUP BY, etc.)
Therefore you just need to patch in the WHERE clause between the stub and
the tail. The code to do that looks like this:
Dim strWhere As String
Const strcStub = "SELECT [Record-Orders-Sales].*, ... FROM ... " &
vbCrLf
Const strcTail = "ORDER BY [Record-Orders-Sales].SalesOrderID;"

'Save any changes in progress
If Me.Dirty Then Me.Dirty = False
'Build the filter string as shown in the example database.
strWhere = "WHERE SomeField = 999 " & vbCrLf
'Now assign this to the form's RecordSource
Me.RecordSource = strcStub & strWhere & strcTail

Since you now have 2 techniques, the final question is which is better? To
apply a Filter, or to change the RecordSource? There are specific cases
where each is useful, but as a rule of thumb for starting out, apply a
filter if you want the user to be able to remove the filter easily, and
change the RecordSource if the user should not be able to get at the other
records, or if there are performance reasons for not loading the other
records.

--
Allen Browne - Microsoft MVP. Perth, Western Australia

Reply to group, rather than allenbrowne at mvps dot org.

markmarko said:
Thanks Jeanette,

I was hoping that was possible... I tested putting an sql statement
(copied
from a query) in the recordsource property directly, and it crashed
access.
So, I'll take your word for it that vba can do it.

Now on to the tricky part....
In order to 'filter' by Salesperson (Salesperson is related to Sales Order
via a junction table, since a Sales Order may have more than one
Salesperson). Here's the sql:

SELECT [Record-Orders-Sales].*, [Query-Junction-Cust_Account].*,
[Junction-SalesOrder_Contractors].AssociatedContractorID
FROM ([Record-Orders-Sales] INNER JOIN [Query-Junction-Cust_Account] ON
[Record-Orders-Sales].[AssociatedCustAcct#Junction] =
[Query-Junction-Cust_Account].JunctionID) INNER JOIN
[Junction-SalesOrder_Contractors] ON [Record-Orders-Sales].SalesOrderID =
[Junction-SalesOrder_Contractors].AssociatedSalesOrderID
WHERE
((([Junction-SalesOrder_Contractors].AssociatedContractorID)=[Forms]![Entry-SalesOrder]![Filter-SalesRep1]))
ORDER BY [Record-Orders-Sales].SalesOrderID;

In order to filter by 2 sales peeps, I'd want to add to the string ala
Allen
Browne's filter model, but the a bit I'd need to add would be 'injected'
into
the above sql, not at the end.

Here's what the sql would look like for filter by 2 sales peeps, (again,
from a query (that works)).
SELECT [Record-Orders-Sales].*, [Query-Junction-Cust_Account].*,
[Junction-SalesOrder_Contractors].AssociatedContractorID,
[Junction-SalesOrder_Contractors_1].AssociatedContractorID
FROM ([Junction-SalesOrder_Contractors] AS
[Junction-SalesOrder_Contractors_1] INNER JOIN ([Record-Orders-Sales]
INNER
JOIN [Query-Junction-Cust_Account] ON
[Record-Orders-Sales].[AssociatedCustAcct#Junction] =
[Query-Junction-Cust_Account].JunctionID) ON
[Junction-SalesOrder_Contractors_1].AssociatedSalesOrderID =
[Record-Orders-Sales].SalesOrderID) INNER JOIN
[Junction-SalesOrder_Contractors] ON [Record-Orders-Sales].SalesOrderID =
[Junction-SalesOrder_Contractors].AssociatedSalesOrderID
WHERE
((([Junction-SalesOrder_Contractors].AssociatedContractorID)=[Forms]![Entry-SalesOrder]![Filter-SalesRep1])
AND
(([Junction-SalesOrder_Contractors_1].AssociatedContractorID)=[Forms]![Entry-SalesOrder]![Filter-SalesRep2]))
ORDER BY [Record-Orders-Sales].SalesOrderID;

It's the same as the first version, with the
[Junction-SalesOrder_Contractors_1] bits added. The trick is that it's
added
kind of in the middle. For this one instance, I'm sure I could make it
work,
but I want to make a model that I could scale to allow building the
recordsource sql to include other factors, and need a systematic way to
add
to the sqlString.

Any advice?
 
M

markmarko

Regarding using recrodsource, some of my criteria are on a continuous
subform, and as I understand it, this means I cannot simply filter on that
criteria.

I'll explain the scenario more fully...

The main form is a Sales Order, and the subform indicates which Sales Reps
made the sale. Sales Reps are connected to the order via a junction table,
since there may be 1 or 2 Reps on a Sales Order.

I'd like the user to be able to filter by 1 or 2 reps, using 2 unbound combo
boxes.

As I mentioned, I had built queries originally. The queries for filter by 1
rep was different from filtering by 2 in the SELECT clause, the FROM clause,
and the WHERE clause. Basically, it references a 'copy' of the SalesRep
Junction (Same table name followed by '_1'.

Is that needed? Is there a way to filter by 2 reps without that 'copy' table
reference?

Also, in your example, is '999' a placeholder? And why is the vbCrlLf needed?
 
A

Allen Browne

Replies in-line

--
Allen Browne - Microsoft MVP. Perth, Western Australia

Reply to group, rather than allenbrowne at mvps dot org.

markmarko said:
Regarding using recrodsource, some of my criteria are on a continuous
subform, and as I understand it, this means I cannot simply filter on that
criteria.

No, that's not correct. You can filter a continuous form, and you can change
the RecordSource of a continuous form.
I'll explain the scenario more fully...

The main form is a Sales Order, and the subform indicates which Sales
Reps
made the sale. Sales Reps are connected to the order via a junction table,
since there may be 1 or 2 Reps on a Sales Order.

I'd like the user to be able to filter by 1 or 2 reps, using 2 unbound
combo
boxes.

So one [Sales Order] can be shared by multiple [Sales Reps]. I take it you
have a main form bound to the [Sales Order] table, with a subform bound to a
related table where you enter the sales resps for that order, one per row.

If so, you can filter the main form so it shows only the sales orders where
a particular rep was invovled like this:
Filter a Form on a Field in a Subform
at:
http://allenbrowne.com/ser-28.html

To filter by multiple reps, you would need to be clear about whether you
want only sales where *both* reps were involved (an AND), or sales where
*either* rep was involved (an OR.) Either way, you will end up using a
subquery to achieve that. If subqueries are new, here's a starting point:
http://allenbrowne.com/subquery-01.html
As I mentioned, I had built queries originally. The queries for filter by
1
rep was different from filtering by 2 in the SELECT clause, the FROM
clause,
and the WHERE clause. Basically, it references a 'copy' of the SalesRep
Junction (Same table name followed by '_1'.

Is that needed? Is there a way to filter by 2 reps without that 'copy'
table
reference?

I'm not sure of the context here, but it seems like you have 2 copies of hte
same table in your query? A subquery would need to alias the table.
Also, in your example, is '999' a placeholder? And why is the vbCrlLf
needed?

Yes: it's an example.

The carriage return/line feed is not needed; it just makes the SQL statement
more readable when you are trying to debug it.
 
M

markmarko

So, what I've ended up doing is to have all filter criteria concatenate into
the WHERE clause, instead of some going into the 'filter' and others going to
WHERE of the recordsource. This greatly simplifies my code.
 

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