Range in Query Design View Access - ms-access

I have a 'student' table with details of id,name,sex,age,year,gpa of students.
I have another table 'major' table with the ids of students and the course which the major in(dname).
I would like to find the difference in age between the oldest student and the youngest student majoring in 'Computer Sciences'. This has proved difficult for me since the total function does not include 'range'.
When I try to find the difference between the maximum and minimum age using 'expression' in the total function I get this error....."Your query does not include the specified expression 'age' as part of an aggregate function"
How should i go about this to find the difference between the maximum and minimum age?

Change Total: from Group By to Expression and define the criteria in Field: (see below).

Change the first field of the query as follows:
Remove the criteria
Replace the field name (age) with the formula Max([age])-Min([age])

Related

How to get reliable results from first() and last()?

EDIT: See edit below for explanation of why min() and max() are NOT adequate.
=========================
The MS documentation on the functions first() and last() says “Because records are usually returned in no particular order (unless the query includes an ORDER BY clause), the records returned by these functions will be arbitrary.”
Obviously, that makes these functions pretty useless for their intended purpose unless the query includes an ORDER BY. But including that in the query is not a straightforward thing to do because these are "aggregate" functions, so a query that SELECTs on them cannot ORDER BY any other field that is not also submitted to an aggregate function.
I have found that a query based on a single table generally returns results in the order of that table’s primary key. But apparently, that cannot be relied on to always be true and may fail under certain circumstances. There's an excellent discussion of this issue in an article, DFirst/DLast and the Myth of the Sorted Result Set.
That article offers two solutions to this problem:
Option one; you first use the DMin/DMax-Function to retrieve the value from the “sortable” column ... and use this as an additional criterion to your query to retrieve the target record.
Second option; you first create a query just containing the primary key and the max value of the sortable column (e.g. CustomerId and maximum of order date). Then you create a second query on the orders and join the first query in there on these two fields. The results will be all column from the orders table but only for the most recent order of each customer.
Those instructions are pretty complicated, so I'd need to see an example of them implemented in code in order to trust myself to use them myself.
This issue has got to be very common because a lot of businesses need to know the first or last order by a customer that meets some condition. But when I Google "Access query first last "order by"", there are several results that explain the problem, including on StackOverflow, but none that lay out a solution with sample SQL code.
What is the right way to do this, including sample code of doing it?
=========================
Edit:
Many sources online, as well as the comment below by Gustav and the proposed answer by Albert D. Kallal, say you can just use min() and max() instead of first() and last(). Obviously, that's okay if what you want is the value of a field in the record in which that field has the smallest or largest value. That's a trivial problem. What I'm talking about is how to get the value of a field in the record in which some other field has the smallest or largest value.
For example, in the answer by Albert D. Kallal, he wants the first and last tour for each customer, so he can just use min() and max() on the dates of the tours. But what if I want to know the location of the first tour for each customer? Obviously, I can't use min(location). If first() would work in a sensible way and if table [Tours] has the primary key [Date], I should be able to use something like:
(SELECT first(location) from [Tours] where [Customer] = ID_Customer)
I am using code like that and it usually gives me the right answer, but not always. So that is what I need to fix. I understand that I may need to use min() instead of first(). But how do I use min() for this since, as I said, I obviously can't just use min(location)?
Never really grasped what first() and last() does in Access.
As you note, rather common to want say last invoice or whatever.
So, say we have a table of Tours. I want the first tour date, and the last tour date.
Well, this query works:
SELECT MAX(FromDate) as LastTourDate, min(FromDate) as FirstTourDate
FROM tblTours
WHERE FromDate is not null
When I run above, I get this:
So, that gets you the min, and max - and gets you this in one query.
No real need for a order by.
However, often there are more then one table involved.
So, I might in place of JUST the first and last tour date?
I probably want a list of customers, and their first tour they took, and say their last tour. But, then again, that's a different question.
But, you again can order your main table ANY way you want, and still pluck out
(pull the min and max).
So, you can do it this way:
Say, tblMain client (people - customers whatever).
Say, tblMyTours - a list of tours they took (child table).
So, the query can look like this:
SELECT tblMainClient.FirstName, tblMainClient.LastName,
(SELECT Min(FromDate) FROM tblMyTours
WHERE tblMyTours.main_id = tblMainClient.id)
AS FirstTourDate,
(SELECT MAX(FromDate) FROM tblMyTours
WHERE tblMyTours.main_id = tblMainClient.id)
AS LastTourDate
FROM tblMainClient
so, the main query is still tblMainClient - I can order, filter, sort by any column in that main table, but we used two sub-query to get the first tour date and the last tour date. So, it will look say like this:
So, typical, we can use a sub-query, pull the max (or min) value, but restrict the sub query to the one row from our parent/main table.
edit: Get last reocrd, but SOME OTHER column
Ok, so say in our simple example, we want the last tour, but NOT the date, but say some other column - like say the last Tour name.
Ok, so we just modify the sub query to return ONLY the last reocrd, but a different column.
And since dates (say 2 invoices on the same day, or yearly tours might have the SAME name, then we need to ensure that ONLY one reocrd is returned. We do this by using top 1, but ALSO add a order by to be 100%, 200%, 300% sure that ONLY ONE top record is returned.
So, our query to get the last tour name, but based on say most recent tour date?
We can do this:
SELECT FirstName, LastName,
(SELECT TOP 1 TourName FROM tblMyTours
WHERE tblMyTours.main_id = tblMainClient.id
ORDER BY tblMyTours.FromDate DESC, tblMyTours.ID DESC)
AS LastTour
FROM tblMainClient
And that will give us the tour name, but the last one.
This:
So, you ceratinly not limited to using "max()" in that sub query.
However, what happens if we want the Tour Name, Hotel Name, and City of that tour?
In other words, it certainly reasonable that we may well want multiple columns.
There are more ways to do this then flavors of ice cream.
However, I like using the query builder for the first part.
What I do is use the standard query builder, do a join to the table and simple slect all the columns I need.
So, for above tblMainClient, and their tours from tblMyTours?
I build a join - use query builder like this:
So, note how I added the columns TourName, FromDate, HotelName and city from that child table (tblMyTours).
Now, of course the above will return 10 rows for anyone who gone on 10 trips.
So, what we do is add a WHERE clause to the child table, get the LAST pk "id" from tblMyTours, and restrict that child table to the ONE row.
So, the above query builder gives us this:
SELECT tblMainClient.ID, tblMainClient.FirstName, tblMainClient.LastName,
tblMyTours.TourName, tblMyTours.FromDate, tblMyTours.HotelName, tblMyTours.City
FROM tblMainClient
INNER JOIN tblMyTours ON
tblMainClient.ID = tblMyTours.Main_id;
(but, I did not have to write above).
So, we add a where clause to that child table join - get the CHILD table "id" in place of TourName, or Tourdate).
So above becomes this:
SELECT tblMainClient.ID, tblMainClient.FirstName, tblMainClient.LastName,
tblMyTours.TourName, tblMyTours.FromDate, tblMyTours.HotelName,
tblMyTours.City
FROM tblMainClient
INNER JOIN tblMyTours ON tblMainClient.ID = tblMyTours.Main_id
WHERE tblMyTours.ID =
(SELECT TOP 1 ID FROM tblMyTours
WHERE tblMyTours.Main_id = tblMainClient.id
ORDER BY tblMyTours.FromDate DESC, tblMyTours.ID DESC)
Now, above is a bit advanced, but OFTEN we want SEVERAL columns. But, at least the first part of the query, the two tables, and the join was done using the query builder - I did not have to type that part in.
so, if you want JUST one column - differnt then the max() critera, then use top 1 with a order by. Do keep in mind that ONLY ONE RECORD can EVER be retunred by that query - if more then one reocrd is returned, the query enginer will fail and you get a message to this fact.
So, for a produce bought, invoice date? They could by the 1 product 2 times, or 2 invoices on the same day might occur. So, by introduction of the 2nd ORDER BY clause (by ID DESC), then that top 1 will ONLY ever return one row.
So, which of the above two?
Well, if just one column from the child table - easy. But, if you want multiple columns? Then you could probably write up a "messy" solution, but I perfect to just fire up query builder, join in the child table, click on the "several" child values I want. Get the query working - and hey, it all up to this point 100% GUI.
Then we toss in the EXTRA criteria to restrict that child table row to the ONE last row, be it simple last one based on ID DESC, or say TourDate, or whatever.
And now we get this:

Plot total number of users by date joined

Is there any function or node which will add the number of elements in a set chronologically?
I would like to create a simple line graph of "total number of users" over time, but what I have is "user_email" (unique) and "date_created" for the date the user joined.
What is the easiest way to sum the number of users at any given time from their creation date and plot it in a graph according to time?
I tried searching for this, but didn't find anything related. New to KNIME. Thanks.
If you are sure that user_email only contains unique values, you can sort the table by date_created (if it isn't already sorted) then use a Counter Generation node to add a column containing a counter value.
For a more general solution, if you want to count the cumulative total of unique values in a table column, you can use this sequence:
GroupBy configured to group by the column whose unique values you want to count and to aggregate on the column you want to plot this against - for example, your timestamp column, probably with either the First or Last aggregation method
Sorter to sort on the aggregation column from GroupBy
then Moving Aggregation with the Cumulative computation box checked, and configured to aggregate on Count of the grouped column from GroupBy.

How to use the where clause where the values in a columns are not explicit in SQL?

I have a restriction column that contains both gender and age range.
I could use the WHERE clause for "MEN" and "WOMEN" to return only the rows that satisfy the condition. But this restriction column also contains the age range like "Teenager", "Adult" and "Senior" so I'm not sure how to specify the WHERE clause. Sometimes the gender and age range restrictions are unspecified so for example for a list of row that satisfies the condition "MEN", all unspecified columns should also be returned.
Example of the column:
Restriction:
-MEN
-WOMEN
-WOMEN/Teenager
-Anyone
-Men/Senior
-Senior
restriction columns
I am suggesting to use LIKE operator. The following are some examples for the WHERE clause:
To get all Men fields : WHERE YOUR_CLM LIKE 'MEN%'
To get all Adults fields : WHERE YOUR_CLM LIKE '%Adult'
To get all non-Senior Women fields : WHERE YOUR_CLM NOT LIKE '%Senior' AND YOUR_CLM LIKE 'WOMEN%'
NOTE: if you are allowed to make changes on the database schema, then I would recommend to split this column into two columns one for age and the other for gender, each column is represented by an ID which represents a value stored in a list-of-values table (ex; GendersLOV table, AgeLOV table)

Adding Columns in Access

Is there a way to add all the marks under total Column?
This is my school project work.
The Question is to create a query and show ROll Number and Name and total marks
obtained by each student using both the table while adding fields in query.
There is no intrinsic function to sum multiple fields. If your table structure were normalized could do a CROSSTAB query. Otherwise, build a simple SELECT query that constructs the Total field with an expression:
[Bengali]+[English]+[Maths]+[Physics]+[Chemistry].
Be aware that if any field is Null the expression will return Null. If needed, deal with possible Null in each field: Nz([Bengali],0) or some recommend Iif([Bengali] Is Null, 0, [Bengali]).
There is no need to have Total field in table. Calculate the value when needed. However, starting with Access2010 could use a Calculated type field in table. Some developers despise their use but they do work. They do have limitations, for instance the Nz() will not work nor will Is Null. IIf(IsNull([Bengali]),0,[Bengali]).

Get AVG() of values from table with different names

I have a table :
CREATE TABLE data
(
value integer,
name varchar(100)
)
In my table there can be duplicate values of name possible with different value of value. Now I want to get DISTINCT name and there avg() value from the Table data.
I am able to get DISTINCT value of name but unable to get avg() of there values.
Now with following Query I get avg() of all data :
select floor(avg(value)) from data
I know this is incorrect but I am new to SQL. I want this select floor(avg(value)) for distinct values of name.
Data :
insert into data values(10, 'mnciitbhu')
insert into data values(20, 'mnciitbhu')
insert into data values(40, 'mafiya69')
insert into data values(20, 'mafiya69')
insert into data values(0, 'mafiya69')
Output :
mnciitbhu 15
mafiya69 20
Adding this because the other answers while accurate, are not detailed.
What you want to do here, are use the grouping and aggregation features of SQL.
grouping your results by particular fields, will divide your result set into discrete sections, which you can operate on with aggregate functions, to get averages, sums, counts etc, per group.
For a full list of aggregate functions, and other miscellaneous information about group by, you can read 12.16.1 GROUP BY (Aggregate) Functions.
In your instance, since you want the average per name, you will need to group by name. This would give the following query:
select name, avg(value)
from `data`
group by name; -- this is the important line
And this query will calculate the average of value, for each group of names in your table, returning one row per group.
One very important consideration when using group by, is that all fields contained in the select, must either be contained in the group by clause, or used in aggregate functions. If you refer to a field that isn't covered by this, you may end up with undesired indeterminate results.
From the manual 12.16.3 MySQL Handling of GROUP BY
MySQL extends the use of GROUP BY so that the select list can refer to
nonaggregated columns not named in the GROUP BY clause. This means
that the preceding query is legal in MySQL. You can use this feature
to get better performance by avoiding unnecessary column sorting and
grouping. However, this is useful primarily when all values in each
nonaggregated column not named in the GROUP BY are the same for each
group. The server is free to choose any value from each group, so
unless they are the same, the values chosen are indeterminate.
The importance of that paragraph cannot be overstated. It is very easy to mis-understand how this works, arrive at a query that seems to give the desired result, but will occasionally give incorrect/undesired data.
Use this code:
select name,AVG(value) as Average from data
group by name
order by name desc
OUTPUT:
name Average
mnciitbhu 15
mafiya69 20
Try this
select name,avg(value) from data group by name