Access Select Query - Include blank values - ms-access

Good afternoon,
I am creating a database for handling safety at my workplace.
I want to create a system where the user can raise a toolbox talk and through filters produce a list of employees who need to participate because they either belong to a certain department, certain competency or both.
To set the scene, an employee can be assigned multiple departments and multiple competencies in which case my select query: Query:ToolboxTalk works fine.
However there are occasions where an employee will be assigned to a department but maybe no competency. When this happens, my query does not return that employee because of the null value.
My question is this:
How can I make my query include null values or should I be going about this a different way?
Please see screenshot of my query in design view and SQL below:
SELECT tblEmployees.EmployeeID, IIf(IsNull([LastName]),IIf(IsNull([FirstName]),[FirstName]),IIf(IsNull([FirstName]),[LastName],[FirstName] & " " & [LastName])) AS [Employee Name], tblEmployees.Gender, tblDepartments.DepartmentID, tblDepartments.Department, tblCompetencies.CompetencyID, tblCompetencies.CompetencyDescription, tblEmployeesDepartments.EmployeesDepartmentID, tblEmployeesCompetencies.EmployeeCompetencyID
FROM (tblEmployees INNER JOIN (tblCompetencies INNER JOIN tblEmployeesCompetencies ON tblCompetencies.CompetencyID = tblEmployeesCompetencies.CompetencyID) ON tblEmployees.EmployeeID = tblEmployeesCompetencies.EmployeeID) INNER JOIN (tblDepartments INNER JOIN tblEmployeesDepartments ON tblDepartments.DepartmentID = tblEmployeesDepartments.DepartmentID) ON tblEmployees.EmployeeID = tblEmployeesDepartments.EmployeeID
WHERE (((tblEmployees.EmploymentStatus)="Active"));
Query-Design View Image
If there is any info I have neglected to include, please let me know and Ill endeavour to add it.
Thank you in advance!

Solution was to adjust query to left join with normalised table structure.
Refer picture and sql.
Query design view
Query SQL view

Related

Access SQL Append/Update Distinct

I'm trying to update a query I have, Access SQL (for now). It is currently a make table, but I want to change it into an update query, but only if the information isn't already in the end table.
Here is the current "Make Table" Query:
SELECT DISTINCT dbo_Us_postal_codes.[City]
,dbo_Us_postal_codes.[State] INTO dbo_ActiveZipCodes
FROM dbo_General_Client_List INNER JOIN dbo_Us_postal_codes ON
dbo_General_Client_List.ZipCode = dbo_Us_postal_codes.[Zip Code]
WHERE (((dbo_General_Client_List.ActiveCompany)=Yes));
US Postal Codes is a list of all US zipcodes, and their corresponding City, State.
What I want to do, is as General_Client_List is updated, run this query and add to ActiveZipCodes if the city/state combo isn't already there.
Add dbo_ActiveZipCodes to your query window. Join the City and state fields from the source table with an Left join to the matching fields in the dbo_ActiveZipCodes. Make the criteria for both Is Null.
Obviously test it as as select query first.

LEFT JOIN - narrow things down

I'm currently having a problem with a legacy app I just inherited on my new job. I have a SQL query that's way too long to respond and I need to find a way to fasten it.
This query acts on 3 tables:
SESSION contains all users visits
CONTACT contains all the messages people have been sending through a form and contains a "session_id" field that links back to the SESSION id field
ACCOUNT contains users accounts (people who registered on the website) and whose "id" field is linked back in SESSION (through a "SESSION.account_id" field). ACCOUNT and CONTACT are no linked in any way, besides the SESSION table (legacy app...).
I can't change this structure unfortunately.
My query tries to recover ALL the interesting sessions to serve to the administrator. I need to find all sessions that links back to an account OR a contact form.
Currently, the query is structured like that :
SELECT s.id
/* a few fields from ACCOUNT and CONTACT tables */
FROM session s
LEFT JOIN account act ON act.id = s.account_id
LEFT JOIN contact c on c.session_id = s.id
WHERE s.programme_id = :program_id
AND (
c.id IS NOT NULL
OR
act.id IS NOT NULL
)
Problem is, the SESSION table is growing pretty fast (as you can expect) and with 400k records it slows things down for some programs ( :programme_id in the query).
I tried to use an UNION query with two INNER JOIN query, one between SESSION and ACCOUNT and the other one between SESSION and CONTACT, but it doesn't give me the same number of records and I don't really understand why.
Can somebody help me to find a better way to make this query ?
Thanks a lot in advance.
I think you just need indexes. For this query:
SELECT s.id
/* a few fields from ACCOUNT and CONTACT tables */
FROM session s LEFT JOIN
account act
ON act.id = s.account_id LEFT JOIN
contact c
ON c.session_id = s.id
WHERE s.programme_id = :program_id AND
(c.id IS NOT NULL OR act.id IS NOT NULL);
You want indexes on session(programme_id, account_id, id), account(id) and contact(session_id).
It is important that programme_id be the first column in the index on session.
#Gordon already suggested you add an index, which is generally the easy and effective solution, so I'm going to answer a different part of your question.
I tried to use an UNION query with two INNER JOIN query, one between
SESSION and ACCOUNT and the other one between SESSION and CONTACT, but
it doesn't give me the same number of records and I don't really
understand why.
That part is rather simple: the JOIN returns a result set that contains the rows of both tables joined together. So in the first case you would end up with a result that looks like
session.id, session.column2, session.column3, ..., account.id, account.column2, account.column3, ....
and a second where
session.id, session.column2, session.column3, ..., contact.id, contact.column2, contact.column3, ....
Then an UNION will faill unless the contact and account tables have the same number of columns with correspoding types, which is unlikely. Otherwise, the database will be unable to perform a UNION. From the docs (emphasis mine):
The column names from the first SELECT statement are used as the column names for the results returned. Selected columns listed in corresponding positions of each SELECT statement should have the same data type. (For example, the first column selected by the first statement should have the same type as the first column selected by the other statements.)
Just perform both INNER JOINs seperately and compare the results if you're unsure.
If you want to stick to an UNION solution, make sure to perform a SELECT only on corresponding columns : doing SELECT s.id would be trivial but it should work, for instance.

Dynamically added column to sql results

I'd like to have sql results containing dynamically added fields. The picture I attached explains what I aim at.
As far I've made up something like below, yet it doesn't work.
SELECT s.num, s.name ( SELECT g.grade AS g.subject FROM grades g WHERE g.student = s.id ) FROM students s;
Note that I want to have column name corresponding to value of column subject.
I have no idea how to get it.
You will need to construct Dynamic Sql with prepared statements.
http://blog.sqlauthority.com/2014/06/20/mysql-dynamic-sql-with-execute-and-prepare-statements/
The columns would come back like "as history" ... "as math"
As far as the nulls that is just common outer join action

SQL to retrieve related records with many to many relation

I am building an application for registration of agreements between institutes. These agreements may include more than 2 partners. As such, I quickly dropped the idea of having part1 and partner2 in a contracts table.
Current design is (Note: simplified for question):
Table Institutes: ID, Name , ..
Table Contract_institutes: ContractID, InstituteID
Table Contracts: ID, Title, ...
How would I go about showing a list of all contracts including the involved partners, assuming you know one partner: A user is logged in, and wants to see all the contracts that his institute has, and all the partners in the contract; e.g.:
Contract1: (Title) Institute1Name, Institute2Name
Contract2: (Title) Institute1Name, Institute2Name, Institute3Name
Contract3: (Title) Institute1Name
I could first get all the contracts IDs
select *fields*
from Contracts
left join Contract_institutes on Contracts.ID = Contract_institutes.ContractID
where Contract_institutes.InstituteID = *SomeValue*
And then get all the related institutes with a separate query for each contract (Or using an IN statement in the query), and use a lot of foreach php loops to format. Not pretty, and probably not efficient.
There must be a better way to do this, and get the list in a single sql statement. Can someone help me?
Ideally, I get output rows with: [contract ID][InstituteID][Institute.Name]. I can easily modify this in a per-contract view in the output.
PS:
- This is design phase of the application: The database is empty and can be modified to needs.
select C.ID, I.ID, I.Name
from Contracts C
join Contract_institutes CI on C.ID = CI.ContractID
join Institutes I on I.ID=CI.InstituteId
where CI.InstituteID <> *SomeValue*
and CI.ContractID in (select CI2.ContractId
from Contract_institutes CI2
where CI2.InstituteID = *SomeValue*)

query that will pull data where a certain document type is not found

I am writing a query for a health organization. The query is to pull patient data, where an encounter/appointment was completed but a chart note was not generated. I have the query pulling patients and their appointments; is there a way to basically say "only show the patients where 'master_im' document was not generated"?
I am using Microsoft SQL Server Management Studio.
Without seeing your table structures, etc. your could do a check to see if the master_im IS NULL.
SELECT *
FROM yourTable
WHERE appointment = 'completed'
AND master_im IS NULL
I would advise posting some additional details on your tables.
If the data is stored in separate tables, then you will want to JOIN the tables together to get the results you want.
EDIT #1 based on your comment you could do something like this:
select *
from person p
inner join appointments a
on p.enc_id = a.encid
left join patient_documents pd
on p.enc_id = pd.enc_id
where a.status = 'completed'
and pd.document_desc != 'master_im'