Provided by Allen Browne, July 2006. Updated June 2007
Yes/No fields in Access tables cannot be Null. When necessary, you can work around this limitation it by using a Number field instead, permitting the values -1 (for true), 0 (for false), and Null.
But Yes/No fields do contain Nulls in queries that use outer joins. JET (the query engine in Access) cannot handle these. Simple queries fail with an error such as, "No current record." More complex queries (particularly those with subqueries where this occurs) can crash Access.
The sample database (10KB zipped) contains just two tables: companies, and their employees. IsInvited is a Yes/No field, so choose the people to include in a mailing. The query counts the number of people from each company who are invited or not invited:
Design View | SQL View |
SELECT tblCompany.CompanyID, tblEmployee.IsInvited, Count(tblEmployee.EmployeeID) AS CountOfEmployeeID FROM tblCompany LEFT JOIN tblEmployee ON tblCompany.CompanyID = tblEmployee.CompanyID GROUP BY tblCompany.CompanyID, tblEmployee.IsInvited; |
Access cannot run this query. The only result is a dialog reading "No current record."
The error occurs because we asked Access to group by a Yes/No field that contains nulls. To verify that this is the cause, choose First in the Total row under IsInvited. This removes the Yes/No field from the GROUP BY clause, and the query runs (though it does not achieve what we needed.)
Clearly, there is a basic flaw in the way JET processes Yes/No fields that contains nulls. The bug is present in all versions of Access.
May 2007 update
As a workaround for this example, use Int() around the Yes/No field, like this:
SELECT tblCompany.CompanyID,
Int([IsInvited]) AS WhetherInvited,
Count(tblEmployee.EmployeeID) AS CountOfEmployeeID
FROM tblCompany LEFT JOIN tblEmployee
ON tblCompany.CompanyID = tblEmployee.CompanyID
GROUP BY tblCompany.CompanyID, Int([IsInvited]);
Using Int() around the yes/no field has the effect of converting it into a Number field. JET handles Null in numeric fields, so the bug with the Yes/No type it averted.
Unfortunately, the workaround slows the query down since JET cannot use any index on the Yes/No field. Even with moderately sized tables (tens of thousands of records), it may be an order of magnitude slower.
An alternative that does not have the performance penalty is to use a Number field in your table instead of a Yes/No field. Assuming that you do not want Nulls stored in the fields (i.e. they arise only in outer join queries), you can set the Field properties as shown for the FakeYN field shown at right.
The field is a Number type, with Field Size set to Integer. Access uses 0 for False and -1 for True, so we set the Validation Rule so the field accepts only these two values. We set the Default Value to zero, so it defaults to False like a yes/no field. We set the Required property to Yes, so nulls are not stored in the field.
Now we need to get Access to display the field as a check box. The Lookup tab lets you set the Display Control property, but Access does not offer Check Box as an option. We therefore have to set the property programmatically. If the property has never been set, it must be created. If it has been used, we just need to set it. This custom function handles both cases: SetPropertyDAO()
After copying the function into your database, open the Immediate Window (Ctrl+G) and use it like this:
Call SetPropertyDAO(CurrentDb.TableDefs("tblEmployee").Fields("FakeYN"), "DisplayControl", dbInteger, CInt(acCheckbox))
The table will now display the field as a check box, and it will behave as a yes/no field, without the bug in outer-join queries.
Display Control | Combo Box |
Row Source Type | Value List |
Row Source | -1;"Yes";0;"No" |
Bound Column | 1 |
Column Count | 2 |
Column Widths | 0 |
A new article - Why I stopped using Yes/No fields - describes this technique in more detail.
Home | Index of tips | Top |