Setting a default value in a blank field in an Access query - ms-access

This may have been answered elsewhere, but I can't find it!
I'm combining 2 queries from different sources onto a single query for calculation and reporting purposes. Fields which are not common to both sources show as blank if there is no data, and can't then be used in arithmetic.
I want to set a default of zero where blank, instead of having to knife and fork the query into another table and run an update query over all the blank fields.
There's got to be something simpler! HELP!

Give this a try:
SELECT
nz(value1, 0), nz(value2,0), nz(value3,0)
FROM table1 left outer join table2 on table1.column = table2.column
unless your query resembles what John answered with (a cartesian) then his answer is more appropriate.... (cartesians are dangerous if not used correctly... depending on how big the individual tables are allowed to become you can kill an access application by using them)

You'll either want to use Nz() or iif(), depending on whether that "blank" really is just a blank (empty string) or Null.
So your options are:
SELECT Nz(source1.a + source2.b,0) FROM source1, source2
or:
SELECT iif(source1.a + source2.b <> "", source1.a + source2.b, 0) FROM source1, source2

Related

Testing if a row exists in a table for a report

I'm trying to write a report that lists all the rows from a master table and inserts a field ("Y"/"N") if the key exists in a current-use table. For example, the COLOR_MASTER table has 256 colors by COLOR_NAME (field/key). The CURRENT_PROJECTS has a row for each project and the COLOR_NAME (field). I want to list all the COLOR_MASTER rows and then on the same print line a "Y" or "N" if that COLOR_NAME is used in the CURRENT_PROJECTS table.
I've tried to mess around with it in the Design View and have had no luck. The JOIN that was created looks basically like this and how I want the report is following the dashes:
RIGHT JOIN COLOR_MASTER ON CURRENT_PROJECTS.COLOR_NAME =
COLOR_MASTER.COLOR_NAME ON CURRENT_PROJECTS.COLOR_NAME =
COLOR_MASTER.COLOR_NAME;
--------
Color Used
BLUE
RED Y
YELLOW
I have no expertise in JOINs and I don't understand why this JOIN was created or what I need to do to fix it. Based on my reading, I guess it's trying to do an outer join.
Currently, I'm just trying to show the COLOR_NAME if used as I don't know how to test that it is used and convert it to "Y". I don't care if the color is used once or twenty times and I don't really want any data from the CURRENT_PROJECTS table.
Under the "Used" column I now have "#Error" on all lines. So, I'm figuring that the RIGHT JOIN has an error.
Any guidance is appreciated.
Thanks
I expect the RIGHT JOIN is defined in the table Relationships builder and a query using the tables adopts the established link. If you want to change the join type in a query then with query in design view, double click on the line linking tables to open the Join Properties dialog.
If there is only one record per project in Current_Projects and a color can be used only once, simply:
SELECT Color_Master.Color_Name, IIf([Current_Projects].[Color_Name] Is Null,"N","Y") AS Used
FROM Color_Master LEFT JOIN Current_Projects ON Color_Master.Color_Name = Current_Projects.Color_Name;
However, it sounds like each color can be used for multiple projects. So will need to use GROUP BY or DISTINCT query joined to the Master_Colors table:
SELECT DISTINCT Current_Projects.Color_Name FROM Current_Projects;
SELECT Color_Master.Color_Name, IIf([Query1].[Color_Name] Is Null,"N","Y") AS Used
FROM Query1 RIGHT JOIN Color_Master ON Query1.Color_Name = Color_Master.Color_Name;
Here is all-in-one SQL:
SELECT Color_Master.Color_Name, IIf([Query1].[Color_Name] Is Null,"N","Y") AS Used
FROM (SELECT DISTINCT Current_Projects.Color_Name FROM Current_Projects) AS Query1
RIGHT JOIN Color_Master ON Query1.Color_Name = Color_Master.Color_Name;
If you don't want anything displayed for the N result then use Null without quote marks in the expression.
An alternative is DLookup() but domain aggregate functions can perform slowly in query or textbox expression.
Advise not to save the calculated value to table, just calculate when needed.
You don't have to do it all in one query. Start by building a query that as the IDs of all the colors currently in use. Name it something like ColorsInUse and save it.
It could be something as simple as:
SELECT [COLOR_NAME] FROM [CURRENT_PROJECTS] GROUP BY [COLOR_NAME]
Then all you have to do is run the following queries:
UPDATE [COLOR_MASTER] SET [MyField]="N"
UPDATE [COLOR_MASTER] SET [MyField]="Y" WHERE [COLOR_NAME] IN [ColorsInUse]
Another way of doing it is by using DLookup. Something like this:
UPDATE [COLOR_MASTER] SET [MyField]=IIF(NZ(DLookup("[COLOR_NAME]","[CURRENT_PROJECTS]","[COLOR_NAME]='" & [COLOR_NAME] & "'"),"")="","N","Y")
Some notes: You should not use a text field (like a name) as your key. You should always use numerical IDs. You should also never use a text Y/N. Use an actual Yes/No field instead.

MS Access Query using IFF to compare values

I am trying to build a query which will look at the data in two fields in two different tables and check to see if the data is the same, if it is I want it to return the number of times it is matched, if it isn't I simply want it to return the text saying "No viewings".
I have constructed this query in my access database which has the field from the first table "Property" and the second field I want it to compare the data with, "Viewings". I have build the following expression using the build tool, however I am stuck to make it work since every time I get this error message when trying to run the query: "Your query does not include the specified expression 'Property Viewed' as part of an aggregate function."
totalViewings: IIf([Viewings]![Property Viewed]=[Property]![ID],Count([Viewings]![Property Viewed]=[Property]![ID]),"No Viewings")
Any help how to overcome this error would be very appreciated.
Thanks
I would suggest doing something like this:
1) Assuming this is something you are developing yourself, make sure your data structure is all in order first. Since I dislike relatively code-hostile identifiers, I'd have the tables as so -
Properties - PropertyID (AutoNumber, primary key), HouseNumberOrName, Street, etc.
Viewings - ViewingID (AutoNumber, primary key), PropertyID (Number/Long Integer), ViewingDate, etc.
In the Relationships view, Properties.PropertyID would then be set up to point to Viewings.PropertyID in a one-to-many relation.
2) Your actual query I would then break into two, the first to compile the data and the second to format it for display. The first would go like this, saved as ViewingCounts...
SELECT Properties.PropertyID, Count(Viewings.PropertyID) As ViewingCount
FROM Properties LEFT JOIN Viewings ON Properties.PropertyID = Viewings.PropertyID
GROUP BY Properties.PropertyID;
... and the second like this, saved as ViewingCountsForDisplay:
SELECT Properties.*, IIf(ViewingCount = 0, 'No viewings', ViewingCount) AS Viewings
FROM Properties INNER JOIN ViewingCounts ON Properties.PropertyID = ViewingCounts.PropertyID
ORDER BY Properties.PropertyID;

Access - Left Join returns #Error instead of Null

I've asked a similar question already, but I've now simplified the tables/queries enough to put up an example database with (hopefully) descriptive naming:
https://docs.google.com/file/d/0B2PZcGkhNyd4THpWa01fTjVvSWM/edit?usp=sharing
There is one query, ChainsCasesPerMonthPerStorePreviousMonthRange, which works fine. It takes data from two tables and the QueryDatesPrevious query, to return data for the previous period to the one specified in the QueryDates table. Everything seems okay up to this stage.
But when I run the query LeftJoinReturnsError, the three extra chains in the Chains table return #Error instead of returning the expected Null.
If I change QueryDatesPrevious from a query to a table everything works fine, so this seems to be where the problem lies, but I can't seem to solve it, even using an Iif(IsNull, Null, 0) condition.
An extra 50 rep points to the person who solves it, as long as I can work out how to transfer them across. :)
(previous question if you're interested: Access 2007 - Left Join to a query returns #Error instead of Null)
-- EDIT UPDATE --
Output would look something like this, although I don't remember the exact data I put in the test database:
Chain CasesPerMonthPerStore
AgriStore 2.33
Agricultural Export
2B Pencils 3.6
Bob's Markets
So basically, any chain in the Chain table that isn't in the other tables should return Null as part of the left join.
This is rather ugly too, but it seems to work:
Run the following query to create a table named [tblChainsCasesPerMonthPerStorePreviousMonthRange]:
SELECT ChainsCasesPerMonthPerStorePreviousMonthRange.*
INTO tblChainsCasesPerMonthPerStorePreviousMonthRange
FROM ChainsCasesPerMonthPerStorePreviousMonthRange;
Create a query named [updChainsCasesPerMonthPerStorePreviousMonthRange] as an Update query that saves the results of [ChainsCasesPerMonthPerStorePreviousMonthRange] into [tblChainsCasesPerMonthPerStorePreviousMonthRange]:
INSERT INTO tblChainsCasesPerMonthPerStorePreviousMonthRange
SELECT ChainsCasesPerMonthPerStorePreviousMonthRange.*
FROM ChainsCasesPerMonthPerStorePreviousMonthRange;
Paste the following Function into a standard VBA Module
Public Function RemakeTable() As Variant
Dim qdf As DAO.QueryDef
Debug.Print "Executing RemakeTable()..."
CurrentDb.Execute "DELETE FROM tblChainsCasesPerMonthPerStorePreviousMonthRange", dbFailOnError
Set qdf = CurrentDb.QueryDefs("updChainsCasesPerMonthPerStorePreviousMonthRange")
qdf.Execute
Set qdf = Nothing
RemakeTable = Null
End Function
Update your [LeftJoinReturnsError] query to
SELECT
Chains.Chain,
tblChainsCasesPerMonthPerStorePreviousMonthRange.CasesPerMonthPerStore,
RemakeTable() AS Junk
FROM
Chains
LEFT JOIN
tblChainsCasesPerMonthPerStorePreviousMonthRange
ON Chains.Chain=tblChainsCasesPerMonthPerStorePreviousMonthRange.Chain;
The final query has an extra column named [Junk], but at least we get the desired result.
Note: I put the Debug.Print in the VBA Function to verify that it only gets called once, not once for each row in the query.
Great post on the bug. I hadn't seen that, but it's quite obvious that's exactly what's happening. The only way I can see to deal with it since Access doesn't support lateral joins or SQL Server's APPLY clause, is to elevate the expression to the select clause in the top level query. The following gives you the output you want:
SELECT Chains.Chain, (SELECT ChainsCasesPerMonthPerStorePreviousMonthRange.CasesShipped/(DateDiff("m",ChainsCasesPerMonthPerStorePreviousMonthRange.StartDatePrevious,ChainsCasesPerMonthPerStorePreviousMonthRange.EndDatePrevious)+1)/ChainsCasesPerMonthPerStorePreviousMonthRange.NumberOfStores AS Expr1 from ChainsCasesPerMonthPerStorePreviousMonthRange WHERE ChainsCasesPerMonthPerStorePreviousMonthRange.Chain = Chains.Chain) as Expr2
FROM Chains;
It's pretty ugly, but it works. Of course in SQL Server or another DB, you wouldn't need lateral joins because the process outer joins with expressions correctly.
Okay, so I set up another query that just returns the chains that should return a Null value for CasesPerMonthPerStore, but are actually returning #Error in my test database:
SELECT Chains.Chain
FROM Chains LEFT JOIN ChainsNumStoresPreviousMonthRange ON Chains.Chain = ChainsNumStoresPreviousMonthRange.Chain
WHERE ChainsNumStoresPreviousMonthRange.NumberOfStores Is Null;
So in my written example in the question above this query would return:
Chain
Agricultural Export
Bob's Markets
I then UNION these chains, along with a Null field, to the query that returns all of the chains that do return a value for CasesPerMonthPerStore:
SELECT *
FROM (SELECT ChainsCasesPerMonthPerStorePreviousMonthRange.Chain, ChainsCasesPerMonthPerStorePreviousMonthRange.CasesPerMonthPerStore
FROM ChainsCasesPerMonthPerStorePreviousMonthRange
UNION ALL
SELECT ChainsNotInPreviousPeriod.Chain, NULL
FROM ChainsNotInPreviousPeriod) AS UnionQuery
ORDER BY Chain;
So the secret seems to be to separate the calculated field away from the left join. It's the first time I've come across this error, and I don't know whether this approach would work in every situation, but it worked for me. :)
Thanks for your time Brian & Gord,

ms access complicated query. querying last two entries by date for each ID. very slow

I have two tables. Widgets, which has information about each widget (Color, size, etc); each widget has a unique ID, WidgetID.
The other table is Tests, and it contains information about multiple tests that were run on each widget. This table, therefore, has multiple rows for each WidgetID. It contains information that we can call (WidgetID, Date, Param1, Param2, etc); test information.
I have written a query that finds, for each WidgetID, the most recent two tests by date. The SQL looks like this:
SELECT Widgets.WidgetID, Widgets.Color, Widgets.Size, T.Date, T.Param1, T.Param2,*
FROM Tests AS T INNER JOIN Widgets ON T.WidgetID=Widgets.WidgetID
WHERE (((Select COUNT(*) FROM Tests
WHERE WidgetID = T.WidgetID AND Date > T.Date)) < 2 );
This works very well. However, it gives me too many widgets. I have created a query that filters the widgets called WidgetFilter. It basically just chooses the ones i want based on whatever i choose. The idea was that I would run the same query replacing "Widgets" in the code above with "WidgetFilter." However, when I do this it takes forever. In fact, it just freezes. I left it for an hour and a half and it just sat there and i had to alt ctl delete. My only thought is that it is querying the WidgetFilter query for every row of Tests (and thats a lot of rows). I also tried applying the filter criteria within the original query. I get the same result.
Is there a better way to do this? Either a single query that does it all and possibly doesnt even look like this or what i was thinking is that there should be a way to run the WidgetFilter query once and make that data look to access like a table (isnt there such a thing as a temp table). That way it doesnt run WidgetFilter for every item in Tests.
EDIT:
WidgetFilter is actually pretty complex. I have created these GUI pick tables where the user sees two columns. The choices on the left and the list he/she is creating on the right and in the middle there is an add button and a delete button. They then call the report which executes WidgetFilter, yadda yadda yadda. Anyway, when the user adds an item, it adds that item to a table. So for the Widget category, Color, there will be a table called ColorList. The user constructs this list through the gui. There are three of these guis (Color, Size, Type). For each of them there is a table and there is then a Global boolean (eg, ColorFlag) that tells which filter to use (Color, Size, or Type).
So, in the WidgetFilter query, the criteria box under Color will have this:
In(Select Color From ColorList)
and there is an expression column Expr1: getColorFlag() which is a module that returns the value of the Global variable ColorFlag. and it is true at the same time the color table is applied. So, when all is said and done there are three rows of criteria. The code looks like this:
Select Widgets.WidgetID, Widgets.Color, Widgets.Size, Widgets.Type
From Widgets
WHERE (getColorFlag() = True AND (Widgets.Color) In(Select Color FROM ColorList))
OR (getSizeFlag() = True AND (Widgets.Size) In(Select Size FROM SizeList))
OR (getTypeFlag() = True AND (Widgets.Type) In(Select Type FROM TypeList))
One thing you may not be aware of. I believe that most versions of Access (don't know about 2007) will go off to loopy land if you have a sub-select that returns a NULL value for any row. So, if there is a NULL Color, Size, or Type in any of those tables that would cause the symptoms you're seeing.
Ok. Here's what I ended up doing. I created a temp table through VBA code like this:
Function createWidgetFilterTemp()
Dim mySQL
Dim deleteSQL
deleteSQL = "DELETE * FROM TempWidgetFilter"
mySQL = "SELECT * INTO TempWidgetFilter FROM WidgetFilter"
DoCmd.SetWarnings False
DoCmd.RunSQL deleteSQL
DoCmd.RunSQL mySQL
DoCmd.SetWarnings True
End Function
I Inner Joined TempWidgetFilter and Tests in a query called WidgetCombo. This gave me access to all the Test information and all the Widgets information for only the WidgetIDs that i was interested in. I tried to do the same query with the Select Count(*) statement, but i got the same problem with Access freezing up. So, I then created another temp table through VBA using code like this:
Function createWidgetTemp()
Dim mySQL
Dim deleteSQL
deleteSQL = "DELETE * FROM TempWidgetCombo"
mySQL = "SELECT * INTO TempWidgetCombo FROM WidgetCombo"
DoCmd.SetWarnings False
DoCmd.RunSQL deleteSQL
DoCmd.RunSQL mySQL
DoCmd.SetWarnings True
End Function
I ran both of these functions whenever I called for the querys from the form. This worked!! It worked fairly quickly, actually. I think it would have worked without the first temp table, but having that as a temp table rather than just a query made another part of my app run faster so i left it. By the way, I had to run it once with the delete line commented off so that it would create the table. I think i might have been able to just create the table by hand, but this way it got all the fields in there in the correct way. Thanks for the help, I hope this helps someone else who comes by in the future looking for temp table help.
Simple, really.
SELECT TOP 2 Widgets.WidgetID, Widgets.Color, Widgets.Size, T.Date, T.Param1, T.Param2,*
FROM Tests AS T INNER JOIN Widgets ON T.WidgetID=Widgets.WidgetID
ORDER BY T.Date DESC

How do you comment an MS-access Query?

How does one add a comment to an MS Access Query, to provide a description of what it does?
Once added, how can one retrieve such comments programmatically?
I decided to add a condition to the Where Clause that always evaluates true but allows the coder to find your comment.
Select
...
From
...
Where
....
And "Comment: FYI, Access doesn't support normal comments!"<>""
The last line always evaluates to true so it doesn't affect the data returned but allows you to leave a comment for the next guy.
It is not possible to add comments to 'normal' Access queries, that is, a QueryDef in an mdb, which is why a number of people recommend storing the sql for queries in a table.
NOTE: Confirmed with Access 2003, don't know about earlier versions.
For a query in an MDB you can right-click in the query designer (anywhere in the empty space where the tables are), select Properties from the context menu, and enter text in the Description property.
You're limited to 256 characters, but it's better than nothing.
You can get at the description programatically with something like this:
Dim db As Database
Dim qry As QueryDef
Set db = Application.CurrentDb
Set qry = db.QueryDefs("myQuery")
Debug.Print qry.Properties("Description")
I know this question is very old, but I would like to add a few points, strangely omitted:
you can right-click the query in the container, and click properties, and fill that with your description. The text you input that way is also accessible in design view, in the Descrption property
Each field can be documented as well. Just make sure the properties window is open, then click the query field you want to document, and fill the Description (just above the too little known Format property)
It's a bit sad that no product (I know of) documents these query fields descriptions and expressions.
You can add a comment to an MSAccess query as follows: Create a dummy field in the query. Not elegant but is self-documentating and contained in the query, which makes cheking it into source code control alot more feasible! Jere's an example. Go into SQL view and add the dummy field (you can do from design view too):
SELECT "2011-01-21;JTR;Added FIELD02;;2011-01-20;JTR;Added qryHISTORY;;" as qryHISTORY, ...rest of query here...
Run the query:
qryHISTORY FIELD01 FIELD02 ...
2011-01-21;JTR;Added FIELD02;;2011-01-20;JTR;Added qryHISTORY;;" 0000001 ABCDEF ...
Note the use of ";" as field delimiter in qryHISTORY field, and ";;" as an end of comment, and use of ISO date format and intials, as well as comment. Have tested this with up to 646 characters in the qryHISTORY field.
The first answer mentioned how to get the description property programatically. If you're going to bother with program anyway, since the comments in the query are so kludgy, instead of trying to put the comments in the query, maybe it's better to put them in a program and use the program to make all your queries
Dim dbs As DAO.Database
Dim qry As DAO.QueryDef
Set dbs = CurrentDb
'put your comments wherever in your program makes the most sense
dbs.QueryDefs("qryName").SQL = "SELECT whatever.fields FROM whatever_table;"
DoCmd.OpenQuery "qryname"
If you have a query with a lot of criteria, it can be tricky to remember what each one does.
I add a text field into the original table - call it "comments" or "documentation".
Then I include it in the query with a comment for each criteria.
Comments need to be written like like this so that all relevant rows are returned.
Unfortunately, as I'm a new poster, I can't add a screenshot!
So here goes without
Field: | Comment |ContractStatus | ProblemDealtWith | ...... |
Table: | ElecContracts |ElecContracts | ElecContracts | ...... |
Sort:
Show:
Criteria | <> "all problems are | "objection" Or |
| picked up with this | "rejected" Or |
| criteria" OR Is Null | "rolled" |
| OR ""
<> tells the query to choose rows that are not equal to the text you entered, otherwise it will only pick up fields that have text equal to your comment i.e. none!
" " enclose your comment in quotes
OR Is Null OR "" tells your query to include any rows that have no data in the comments field , otherwise it won't return anything!
I've been using the method in the answer by #Dan above for five years, and now realized there's a similar way that allows a comment adjacent to the relevant code, not separated away in a WHERE clause:
A comment can be added in the field code using iif(). For example, if the field code is:
A / B
then it can be commented like so:
iif("Comment: B is never 0." = "", "", A / B)
or
iif(false, "Comment: B is never 0.", A / B)
or
iif(0, "Comment: B is never 0.", A / B)
Like Dan's solution, it's kludgy, but that's SQL's fault.
if you are trying to add a general note to the overall object (query or table etc..)
Access 2016
go to navigation pane, highlight object, right click, select object / table properties, add a note in the description window i.e. inventory "table last last updated 05/31/17"
In the query design:
add a column
enter your comment (in quotes) in the field
uncheck Show
sort in assending.
Note:
If you don't sort, the field will be removed by access. So, make sure you've unchecked show and sorted the column.