SQL - selecting multiple dates from a table for unique ID's - part 2 - sql

Gordon got me pretty straight on my former question and now I've learned a lot more :). my query now looks like this:
select ident, min(input_date) as 'date requested',
min(case when op='u' and user IN (lists tech's names)) as 'date assigned'
max(case when status='closed' then date end) as 'date completed'
max(case when op='u' then user end) as 'tech'
from table
so this give me all that i want except the column that's labelled as 'tech'. for the majority of the results, i get the correct results but for a handful it gives me either the wrong tech or the 'user'
so I've added a field so you can better understand - it's field called 'role'. when i run this query, i never want the person listed as 'user' in the role column to be listed as 'tech'. in query above, it lists the user instead of tech and it's always in cases where the user updated their request. so if you look at the table, in ident 1, tom is user and harry is tech. harry assigned it to himself on 2/2; however, tom updated request (to give more info) after harry assigned it. running the above query, will result in tom being listed as the tech in the tech field. my guess is b/c of max and tom is after harry alphabetically.
so i have 5 tech's to track (only 2 listed in table - bertha & harry). if i add an IN clause so that it reads like this:
max(case when op='u' and user IN ('bertha','harry',etc) then user end) as 'tech'
the results is it always gives me the last tech alphabetically rather than the one who assigned it/closed it.
my goal is to have the tech who assigns & closes it to be listed. if the request has not been assigned then the 'tech' field should read null.
the date fields all have time stamps so i could go about it that way and i guess i could target their role as well.
i know that max is probably the problem as the tech could assign it then the user could update but if i put IN statement in there I don't get why it lists a tech who's not associated w/that ident.
finally, the other scenario, in ident 2, bertha actually takes the assignment away from harry so i need to say bertha; however, harry will always be the one reported in field - probably due to max()
thanks

Related

Ordering based on one value of many

I have three SQL tables. Users, Registration Field Values, and Registration Fields.
Name
zip code
favorite food
Sue
55555
sushi
Gary
12345
eggs
Where zip code and favorite food are different registration fields.
The relationship is a user has many registration field values, and those values belong to the registration field.
I'm wondering how I can order my table based on a certain registration field. For example, selecting "favorite food", I would want "eggs" before "sushi".
This is confusing to me because I've only seen ORDER BY for an individual column or series of columns. I can't just ORDER BY registration_field_value.value because it needs to be based on only one of those registration fields.
This is like "ORDER BY field value where the associated field id is 'favorite food'", although I don't want to filter anything out.
I'm using Postgres if that makes a difference.
EDIT, adding a
:
You can use case to order based on specific value.
For eg:
ORDER BY
CASE "favorite food"
WHEN 'eggs' THEN 1
ELSE 2
END
The above query will move row with eggs to start and all other value will be moved to bottom.

What are the cases whereby EXCEPT and DISTINCT are different?

Looking into my notes for introduction to databases, I have stumbled upon a case that i do not understand (Between except and distinct).
It says so in my notes that:
The two queries below have the same results, but this will not be the case in general.
First query:
Select c.first_name,c.last_name,c.email
FROM customers as c
WHERE c.country = 'Japan'
EXCEPT
Select c.first_name,c.last_name,c.email
FROM customers as c
WHERE c.last_name LIKE 'D%';
Second query:
Select DISTINCT c.first_name,c.last_name,c.email
FROM customers as c
WHERE c.country = 'Japan' AND NOT (c.last_name LIKE 'D%');
Could anyone provide me some insights as to what are cases whereby the results would differ?
Number 1 selects first, last & email from customers who are from Japan and whose last names do not start with D.
Number 2 selects first, last & email, where no two records have all 3 fields the same, where the customers are from Singapore and their last names do not begin with D.
I suppose I can imagine a table where these would yield the same results, but I don't think it would ever appear except in very contrived circumstances.
Joe Smith jsmith#abc.com Japan
Joe Smith jsmith#abc.com Singapore
Would be one of them. Both queries would yield Joe Smith jsmith#abc.com. Another case would be if no-one was from either country or everyone's last name started with D, then they would both yield nothing.
None of this is tested, and the EXCEPT statement is something I've read about but never had occasion to use.
The first is looking at Japan, the second at Singapore, so I don't see why these would generally -- or specifically -- return the same data.
Even if the countries were the same you have another issue with NULL values. So, if your data looks like this:
first_name last_name email country
xxx NULL a Japan
Your first query would return the row. The second would not.

using MS Access 2010, editing a form, 3 tables, one table is "one to many" and want to have query across one row

Example:
(using a comma, to show columns & "A_", "B_", "C_" to show tables)
I currently have...
A_John, A_Doe, B_MemStartDate, C_Date,
A_John, A_Doe, B_MemStartDate, C_Date,
A_John, A_Doe, B_MemStartDate, C_Date,
I would like Table "C" columns to be pulled by Course but shown as the "Date" column... in other words, example, where Course = 1, use Date... etc...
The table would have each member's course history.
MemberID, CourseID, Date
JohnDoe, Course1, 10-10-13
JohnDoe, Course2, 10-11-13
JohnDoe, Course3, 10-12-13
The table C is the one to many of course, with the goal to show the date that the course was taken as I would title the columns with the 3 different courses I want to show... (I want to pull only 3 different courses)
I would like to have them in a row...
A_John, A_Doe, B_Start Date, C_Course1Date, C_Course2Date, C_Course3Date
Sorry for the lack of experience in my question, but I usually get by with "copy/paste"...LOL
Keeping in mind I am using Access... can I do this?
Clarifying... sorry don't know how to do tables in basic html so have commas
Have this. Pulling from 3 different tables where the member# is unique and joined.
MEMBER, STARTDATE, STATUS, COURSE, COURSEDATE
JohnSmith, 08-01-2013, Active, Workshop1, 10-20-2013
JohnSmith, 08-01-2013, Active, Workshop2, 10-13-2013
JohnSmith, 08-01-2013, Active, Workshop3, 10-28-2013
LaraBentt, 12-01-2012, Inactive, Workshop1, 02-20-2012
LaraBentt, 12-01-2012, Inactive, Workshop2, 02-13-2012
LaraBentt, 12-01-2012, Inactive, Workshop3, 02-28-2012
Want this...
MEMBER, STARTDATE, STATUS, WORKSHOP1, WORKSHOP2, WORKSHOP3
JohnSmith, 08-01-2013, Active, 10-20-2013, 10-13-2013, 10-28-2013
LaraBentt, 12-01-2012, Inactive, 02-20-2012, 02-13-2012, 02-28-2012
Tables columns are basically like this...
Table 1 - tblMember (one - one)
MEMBER, STARTDATE
Table 2 - tblRegStatus (one - one)
MEMBER, STATUS
Table 3 - tblCourses (one to many)
MEMBER, COURSE, COURSEDATE
Hope this explains it better!
Your amendment to your question made it much more clear.
One of the problems you are going to encounter is if you have someone that has registered for the same course, twice. There has to be the understanding that you are only showing the most recent registration per person. Now that we understand that caveat - lets look at how you can build this.
The simplest way of building this is to change the query type from Select to Crosstab. This is done through the Access interface.
For each of the fields in the query, set the Total and Crosstab values to:
MemberName: Total = *Group By* Crosstab = *Row Heading*
StartDate: Total = *Group By* Crosstab = *Row Heading*
Status: Total = *Group By* Crosstab = *Row Heading*
CourseName: Total = *Group By* Crosstab = *Column Heading*
CourseDate: Total = *Max* Crosstab = *Value*
This will result in query results such as:
MemberName StartDate Status Biology English Math
---------- --------- ------ ------- ------- ----
John 10/1/2013 Active 11/14/2013 12/1/2013
Matthew 9/1/2013 Inactive 1/1/2013
Peter 8/7/2013 Active 1/1/2013 4/1/2013
Sam 1/14/2013 Inactive 11/14/2013
William 5/19/2013 Active 1/1/2013 4/1/2013
The advantage to the crosstab is that it will automatically add columns as there is data.
You have to construct your query (the joins) to give you the data that you want however. For example - do you want to show members that have no courses on the list? Do you want to show courses that have no enrollment?
Microsoft has a page on using crosstab queries:
http://office.microsoft.com/en-us/access-help/make-summary-data-easier-to-read-by-using-a-crosstab-query-HA010229577.aspx
If you want to hard code your three columns, you can also manually pivot the data. Again - keep in mind that we are using the Max aggregate, so only the latest courses will show. With a manual pivot you also need to remember that it will be limited to the data you are requesting.
Take your select query, right click and click "Totals" to show the Totals row. You can also click on the Totals icon in the ribbon under Query Tools / Design.
Set up your three columns:
MemberName: Total = *Group By*
StartDate: Total = *Group By*
Status: Total = *Group By*
Now add in a separate field for each course you wish to include. In the "Field" definition enter the following.
Science: IIf([CourseName]="Science",[CourseDate],Null)
Biology: IIf([CourseName]="Biology",[CourseDate],Null)
Math: IIf([CourseName]="Math",[CourseDate],Null)
English: IIf([CourseName]="English",[CourseDate],Null)
Set the "Total" definition value to "Max".
That should get you the results you are looking for.

Best way to filter data by criteria, and display description using SQL in MsAccess?

I have a table in MS Access of data containing results from a survey, and I have a look up table of Risk Ids and descriptions of the sort of risk based on the survey results.
What I've tried so far is selecting distinct entries from my survey table, and inputing a new field into my query for the Risk Code whose number will depend on criteria that I determine, which I will then use to look up the risk.
My table for the survey looks like so:
Name | Location | Days spent eating IceCream | Icecream eating location
John Smith | London | 30 | Hull
My Risk ID table looks like so:
RiskID | RiskBool | Description
1 | Yes | At risk - This person eats too much icecream
2 | Yes | Risk - This person does not eat enough icecream
3 | No | Sensible amount of icecream eaten
4 | Yes | It is illegal to eat icecream in Hull
And my query looks (something) like this in access design view
Name | Location | Risk Code | RiskID | Description
I want to write SQL to change the Risk Code to 1, 2, 3, 4 (up to 15 in my real case) and then I will tell it to only display the person and the description for when the Risk ID and Code match. I haven't written this yet.
What is the best way to achieve this?
I see two possibilities:
Set up 15 queries one for each risk ID, add the descriptions to
those and then join those 15 sets of results together. This is what
I know how to do, but could end up quite messily.
Set up some 'check' using if statements, and then some how setting
the Risk Code field for that entry.
My current SQL looks like this, but it doesn't make any checks yet, I'm worried the if statment will be very, very long.
SELECT DISTINCT
[At Risk Employee List].Employee AS Name,
[At Risk Employee List].[DaysIceCream] AS [Days spent eating Icecream],
[At Risk Employee List].[Base Location],
[RiskCode] AS [Risk Code], <----is this where the check would need to go?
RiskDescLookup.RiskBoolean,
RiskDescLookup.RiskExplanation
FROM RiskDescLookup,
[Survey Raw Data]
INNER JOIN
[At Risk Employee List]
ON
[Survey Raw Data].ResID = [At Risk Employee List].[Staff ID]
GROUP BY
[At Risk Employee List].Employee,
[At Risk Employee List].[DaysIceCream],
[At Risk Employee List].[Base Location],
RiskDescLookup.RiskID,
[RiskCode] AS [Risk Code], <----is this where the check would need to go?
RiskDescLookup.RiskBoolean,
RiskDescLookup.RiskExplanation
I imagine the check done by if statements to be Very long and look something like (in pseudocode):
if ( [At Risk Employee List].[Base Location] = Hull, then [RiskCode]=4...., else if (DaysIceCream>42) then....
Is that the best way to do this? Do I even need to have a Risk Code?
I'm a bit lost as to how to produce this 'check' in the best possible way.
I am not entirely certain of your intent, but from what you've posted and the follow up comments it would appear that the process of joining the Risk Code to Risk ID is relatively simple once you have the Risk Code identified for each survey result.
The real issue it seems is how to encapsulate the logic to identify the Risk Code for each survey result. I would suggest "calculating" the risk code value for each survey result externally to your query and then join to those results before finally joining to the Risk ID.
For example, I might add a third table to the design SurveyRisk that contains Name and Risk Code.
Use whatever criteria and logic you need to use to identify the risk for each survey response. Enter these values into the SurveyRisk table. Then, you can simply join Survey to SurveyRisk to Risk to summarize your results.
Feel free to clarify where I'm misunderstanding what you are trying to accomplish and I'll edit my post accordingly.
The best way to do this is to use a look up table that emulates the structure of your data.
Add a row for every 'case', and in MS Access link the corresponding fields together.
Here is a few of the links:
Then alter the SQL to pair up any options that need to go together. For instance each of the checks I make are duplicated for two seperate locations.
Here is an example:
FROM RiskDescLookupReg
INNER JOIN ([Survey Raw Data]
INNER JOIN [At Risk Employee List]
ON [Survey Raw Data].ResID=[At Risk Employee List].[Staff ID])
ON (RiskDescLookupReg.RegTravelChoice=[Survey Raw Data].RegTravelChoice)
And (RiskDescLookupReg.MonthChoice2=[Survey Raw Data].MonthChoice2
And RiskDescLookupReg.PercentageTimeChoice2=[Survey Raw Data].PercentageTimeChoice2
And RiskDescLookupReg.LimitedDurationChoice2=[Survey Raw Data].LimitedDurationChoice2
And RiskDescLookupReg.TemporaryPurposeChoice2=[Survey Raw Data].TemporaryPurposeChoice2)
Or (
RiskDescLookupReg.MonthChoice1=[Survey Raw Data].MonthChoice1
And RiskDescLookupReg.PercentageTimeChoice1=[Survey Raw Data].PercentageTimeChoice1
And RiskDescLookupReg.LimitedDurationChoice1=[Survey Raw Data].LimitedDurationChoice1
And RiskDescLookupReg.TemporaryPurposeChoice1=[Survey Raw Data].TemporaryPurposeChoice1)
Not how there are two blocks for each location. If I only had one location of interest, I could drop the last block.
If you get duplicates because of the way your lookup table is arranged, you need to specify that the parts from the lookup table are enclosed in a LAST, and the parts from the survey in FIRST. Here is an example:
SELECT
[At Risk Employee List].Number,
FIRST([At Risk Employee List].Employee) AS Name,
FIRST([At Risk Employee List].[Base Location]) AS BaseLocation,
LAST(RiskDescLookupReg.RiskBool) AS RiskBool,
LAST(RiskDescLookupReg.RiskDesc) AS RiskDesc,
The use of LAST ensures that if someone would come up as at risk and not at risk, only the LAST at risk case is displayed (those entries come later in the field). This is counter to the fact when duplicates are displayed the at risk ones come first.

Complex SQL query with group by and two rows in one

Okay, I need help. I'm usually pretty good at SQL queries but this one baffles me. By the way, this is not a homework assignment, it's a real situation in an Access database and I've written the requirements below myself.
Here is my table layout. It's in Access 2007 if that matters; I'm writing the query using SQL.
Id (primary key)
PersonID (foreign key)
EventDate
NumberOfCredits
SuperCredits (boolean)
There are events that people go to. They can earn normal credits, or super credits, or both at one event. The SuperCredits column is true if the row represents a number of super credits earned at the event, or false if it represents normal credits.
So for example, if there is an event which person 174 attends, and they earn 3 normal credits and 1 super credit at the event, the following two rows would be added to the table:
ID PersonID EventDate NumberOfCredits SuperCredits
1 174 1/1/2010 3 false
2 174 1/1/2010 1 true
It is also possible that the person could have done two separate things at the event, so there might be more than two columns for one event, and it might look like this:
ID PersonID EventDate NumberOfCredits SuperCredits
1 174 1/1/2010 1 false
2 174 1/1/2010 2 false
3 174 1/1/2010 1 true
Now we want to print out a report. Here will be the columns of the report:
PersonID
LastEventDate
NumberOfNormalCredits
NumberOfSuperCredits
The report will have one row per person. The row will show the latest event that the person attended, and the normal and super credits that the person earned at that event.
What I am asking of you is to write, or help me write, the SQL query to SELECT the data and GROUP BY and SUM() and whatnot. Or, let me know if this is for some reason not possible, and how to organize my data to make it possible.
This is extremely confusing and I understand if you do not take the time to puzzle through it. I've tried to simplify it as much as possible, but definitely ask any questions if you give it a shot and need clarification. I'll be trying to figure it out but I'm having a real hard time with it, this is grouping beyond my experience...
Create a query/view of the following (say its called PersonLastEvents):
select PersonId, max(EventDate) as LastEventDate
from Events
group by PersonId
Then you can get the data you need with the following. I'm not fully familiar with Access, so you may need to modify some of the syntax, but hopefully this will give you an approach.
select l.PersonId, l.LastEventDate,
sum(case when e.SuperCredits = 'false' then e.NumberOfCredits end)
as NumberOfNormalCredits
sum(case when e.SuperCredits = 'true' then e.NumberOfCredits end)
as NumberOfSuperCredits
from PersonLastEvents l
join Events e on l.PersonId = e.PersonId and l.LastEventDate = l.EventDate
group by l.PersonId, l.LastEventDate
As an aside, it may be easier to change your table to have two number columns (NormalCredits, SuperCredits) as it allows you to simply sum() the columns as required.