Join two queries in Access - sql-server

I have four tables:
Clients
ID | CLIENT_NUM | Year
Farms
ID | ClientID | FARM_NUM
Fields
ID | FarmID | FIELD_NUM | RANK
SoilSheet
ID | FarmID | FieldID | SAMP_NUM | Year
I need to get data from Soilsheet ordered by Rank, FIELD_NUM, and Year. So, I tried this:
SELECT FL.Rank,FL.FIELD_NUM, S.Year, SAMP_NUM
FROM (((SoilSheet S
INNER JOIN Farms F ON F.ID = S.FarmID)
INNER JOIN Clients C ON C.ID = F.ClientID)
INNER JOIN Fields FL ON FL.ID = S.FieldID)
WHERE C.CLIENT_NUM = 1 AND F.FARM_NUM = 1
ORDER BY C.CLIENT_NUM, F.FARM_NUM, FL.Rank, FL.FIELD_NUM, S.Year
The problem is RANK can be different in every year so it doesn't order properly. I need to have it order by RANK of the current year, 2015 for instance.
With the above, I get results like:
RANK | FIELD_NUM | Year | SAMP_NUM
0 | 19-2 | 2015 | 3000
2 | 20-1 | 2015 | 3001
2 | 20-1 | 2014 | 2002
2 | 20-1 | 2015 | 1003
20 | 19-2 | 2014 | 2001
19-2 is RANK 0 in 2015 but 20 in 2014. So, I tried this:
SELECT FL.Rank,FL.FIELD_NUM, S.Year, SAMP_NUM
FROM ((Clients C
INNER JOIN Farms F ON F.ClientID = C.ID)
INNER JOIN Fields FL ON FL.FarmID = F.ID)
LEFT JOIN (((SoilSheet S
INNER JOIN Fields FLS ON FLS.ID = S.FieldID)
INNER JOIN Farms FS ON FS.ID = S.FarmID)
INNER JOIN Clients CS ON CS.ID = FS.ClientID)
ON
(FLS.FIELD_NUM = FL.FIELD_NUM
AND FS.FARM_NUM = F.FARM_NUM
AND CS.CLIENT_NUM = C.CLIENT_NUM)
WHERE C.CLIENT_NUM = 1 AND F.FARM_NUM = 1 AND C.Year = 2015
ORDER BY C.CLIENT_NUM, F.FARM_NUM, FL.Rank, FL.FIELD_NUM, S.Year
This works fine in SQL Server:
RANK | FIELD_NUM | Year | SAMP_NUM
0 | 19-2 | 2015 | 3000
0 | 19-2 | 2014 | 2001
2 | 20-1 | 2015 | 3001
2 | 20-1 | 2014 | 2002
2 | 20-1 | 2015 | 1003
In Access, it gives 'Join expression not supported.'. I'm guessing it's because Access doesn't like joining on INNER joins but I'm just guessing.
Any Ideas? Another way to order or another way to join?

I finally got it to work
SELECT C1.Year,C1.RANK, C1.FIELD_NUM, C.CLIENT_NUM, F.FARM_NUM FROM
(SELECT C.Year,FL.RANK, FL.FIELD_NUM, C.CLIENT_NUM, F.FARM_NUM FROM
((Clients C
INNER JOIN Farms F ON F.ClientID = C.ID)
INNER JOIN Fields FL ON FL.FarmID = F.ID)) C1
LEFT JOIN
(SELECT S.Year, SAMP_NUM,FLS.FIELD_NUM, FS.FARM_NUM, CS.CLIENT_NUM
FROM ((SoilSheet S
INNER JOIN Fields FLS ON FLS.ID = S.FieldID)
INNER JOIN Farms FS ON FS.ID = S.FarmID)
INNER JOIN Clients CS ON CS.ID = FS.ClientID) S1
ON (S1.FIELD_NUM = C1.FIELD_NUM AND S1.FARM_NUM = C1.FARM_NUM
AND S1.CLIENT_NUM = C1.CLIENT_NUM)
WHERE C.CLIENT_NUM = 1 AND F.FARM_NUM = 1 AND C.Year = 2015
ORDER BY FL.Rank, FL.FIELD_NUM

Are you trying to copy the TSQL from SQL Server into Access? Unfortunately that won't work without modification to the Joins. Specifically, you will need to put parentheses around the join clauses.
Check out this for more information:
SQL Inner Joins with multiple tables

Related

SQL (SSMS) join all records from one table and second table but exclude 'duplicates' from second

I'm having an issue where I went all records in Table B and any non matching records in Table A but it's bringing back the matching records in Table A. There is another left join to an additional table which is brought in for reference only.
I'm using SSMS v18.
So ID will be on Table A and Table B. There will be multiple records of this ID on A and B but I don't want the duplicate records if date/time and ID is the same in Table A and in Table B.
e.g. - I've simplified the query I'm using below.
Select
a.id
a.datetime
a.emp_id
c.team_id
From
table_a as a
Left Join
table_b as b On a.id = b.id
And a.datetime <> b.datetime
Left Join
table_c On a.emp_id = c.emp_id
As there isn't NULLs I don't think I can use that. I don't believe a full outer join will return what I need.
Is there a method is solve this? A union query solution will not work as Table A and Table B do not have the same columns/column names.
Please let me know if more information is required.
EDIT - Additional
Apologies but now there's been a change of requirement where I now need to remove the matching records rather than remove just the duplicates. Is there a way around this?
Additional - Data Examples
Table A:
+----+------------------+--------+
| Id | Datetime | emp_id |
+----+------------------+--------+
| 1 | 20/04/2021 10:30 | a |
| 1 | 20/04/2021 11:15 | a |
| 2 | 21/04/2021 12:10 | b |
| 2 | 21/04/2021 13:20 | b |
| 2 | 22/04/2021 15:30 | c |
| 3 | 23/04/2021 09:45 | d |
| 4 | 23/04/2021 14:35 | e |
+----+------------------+--------+
Table B:
+----+------------------+-------------+
| Id | Datetime | other_field |
+----+------------------+-------------+
| 1 | 20/04/2021 10:30 | x |
| 2 | 21/04/2021 13:20 | y |
| 4 | 23/04/2021 14:35 | z |
+----+------------------+-------------+
Desired Output:
+----+------------------+--------+---------+
| Id | Datetime | emp_id | team_id |
+----+------------------+--------+---------+
| 1 | 20/04/2021 11:15 | a | team_01 |
| 2 | 21/04/2021 12:10 | b | team_02 |
| 2 | 22/04/2021 15:30 | c | team_01 |
| 3 | 23/04/2021 09:45 | d | team_02 |
+----+------------------+--------+---------+
So the duplicate ID & Datetime in Table B does not show in final output (regardless of any other fields)
You seem to need a right join instead of a left join. A left join will bring back all rows in table A, and all rows in table B which match the condition which you provided. You seem to want all in table B, which requires a right join.
I know some developers who have an aversion to right joins, if you feel that way, you can simply switch the order of the tables in your query to have table B listed first, left join to table A. I feel that the first solution is the easier one, though you need to be comfortable with it.
Here are my solutions, listed in the order in which I mentioned above.
Select
a.id
,a.datetime
,a.emp_id
,c.team_id
From
table_a as a
RIGHT Join -- here is my change
table_b as b On a.id = b.id
And a.datetime <> b.datetime
Left Join
table_c On a.emp_id = c.emp_id;
/*solution II*/
Select
a.id
,a.datetime
,a.emp_id
,c.team_id
From
table_b as b
Left Join
table_a as a On a.id = b.id
And a.datetime <> b.datetime
Left Join
table_c On a.emp_id = c.emp_id;
/*Updated solution, based on the comments (requirements seem to have changed)*/
Select
a.id
,a.datetime
,a.emp_id
,c.team_id
From
table_b as b
Left Join
table_a as a On a.id = b.id
Left Join
table_c On a.emp_id = c.emp_id
WHERE (a.datetime <> b.datetime OR b.datetime IS NULL);
Explanation of the updated solution: there was nothing to take into account the rows which would not match, hence the OR in the join
Please see Microsoft documentation on joins below.
https://learn.microsoft.com/en-us/sql/relational-databases/performance/joins?view=sql-server-ver15#:~:text=Joins%20indicate%20how%20SQL%20Server,be%20used%20for%20the%20join.

Combine two queries with different 'FROM' tables but similar 'JOIN' tables

I have two queries that I'm trying to combine into one result set.
Query 1:
SELECT t1.evalID, t2.[Order], COUNT(t2.StepID) AS 'Total Categories'
FROM Evals t1
JOIN Steps t2 ON t1.TemplateID = t2.TemplateID
JOIN GradingCats t3 ON t2.StepID = t3.StepID
GROUP BY t1.EvalID, t2.[Order]
ORDER BY t2.[Order]
Query 2:
SELECT t4.EvaluatorID, t6.StepID, t6.[Order], COUNT(t4.Grade) AS 'Grades Entered'
FROM Grading t4
JOIN GradingCats t5 ON t4.GradingCatID = t5.GradingCatID
JOIN Steps t6 ON t5.StepID = t6.StepID
GROUP BY t6.StepID, t4.EvaluatorID, t6.[Order]
My end goal is to locate which steps of an evaluation have missing grades.
edit (sample data):
Query #1
|---------------------|------------------|---------------------|
| evalID | Order | Total Categories |
|---------------------|------------------|---------------------|
| 81 | 01.00 | 17 |
|---------------------|------------------|---------------------|
| 81 | 02.00 | 17 |
|---------------------|------------------|---------------------|
| 81 | 03.00 | 17 |
|---------------------|------------------|---------------------|
Query #2
|---------------------|------------------|---------------------|------------------|
| evaluatorID | Step | Order | Grades Entered |
|---------------------|------------------|---------------------|------------------|
| 1178 | 609 | 01.00 | 2 |
|---------------------|------------------|---------------------|------------------|
| 1178 | 615 | 02.00 | 3 |
|---------------------|------------------|---------------------|------------------|
| 9441 | 609 | 01.00 | 17 |
|---------------------|------------------|---------------------|------------------|
| 9441 | 609 | 02.00 | 17 |
|---------------------|------------------|---------------------|------------------|
| 9441 | 609 | 03.00 | 17 |
|---------------------|------------------|---------------------|------------------|
Starting with the first query which shows all the steps associated with an EVAL, you can LEFT OUTER JOIN the second query, and the steps that are NULL on the right side of the query will be the ones that are missing grades.
In order to do this, there must be some way in your tables to link Grading to Evals. This column is not evident from the code you posted, but I will assume it is there. Maybe it's through GradingCats.
In shortened psuedo-code, just to show what I mean:
SELECT ...
FROM Evals e
INNER JOIN Steps s ON e.TemplateID = s.TemplateID
LEFT OUTER JOIN Grading g ON g.EvalID = e.EvalID --use whatever means you have to show which Eval a Grade is from
LEFT OUTER JOIN Steps gs ON {join to Grading through GradingCats as in your second query}
WHERE gs.StepID IS NULL
In analyzing the result of this query, all the Steps of every Eval will be in s.StepID, and when the same row has a NULL for gs.StepID, that means that step did not get a grade.
Note that you won't want to do any GROUP BY in this query, since you want a row-level analysis.
A coworker (with more knowledge of the data than I) slightly modified my query:
SELECT query1.stepID, Categories, Graded
FROM
(
SELECT rs.stepid, COUNT(c.category) AS 'Categories'
FROM Evals e
JOIN RunScriptSteps rs ON e.TemplateID = rs.TemplateID
JOIN GradingCats c ON rs.StepID = c.StepID
WHERE EvalID = *(someNumber)*
GROUP BY rs.stepid
)AS query1
LEFT JOIN
(
SELECT s.StepID, COUNT(Grade) AS 'Graded'
FROM Grading g
JOIN GradingCats c ON g.GradingCatID = c.GradingCatID
JOIN Steps s ON c.StepID = s.StepID
WHERE EvalID = *(someNumber)*
GROUP BY s.stepid
) AS query2
ON query1.stepid = query2.stepid
ORDER BY stepid ASC

How to represent aggregated null values as 0 in sqlserver [duplicate]

I feel like I was always taught to use LEFT JOINs and I often see them mixed with INNERs to accomplish the same type of query throughout several pieces of code that are supposed to do the same thing on different pages. Here goes:
SELECT ac.reac, pt.pt_name, soc.soc_name, pt.pt_soc_code
FROM
AECounts ac
INNER JOIN 1_low_level_term llt on ac.reac = llt.llt_name
LEFT JOIN 1_pref_term pt ON llt.pt_code = pt.pt_code
LEFT JOIN 1_soc_term soc ON pt.pt_soc_code = soc.soc_code
LIMIT 100,10000
Thats one I am working on:
I see a lot like:
SELECT COUNT(DISTINCT p.`case`) as count
FROM FDA_CaseReports cr
INNER JOIN ae_indi i ON i.isr = cr.isr
LEFT JOIN ae_case_profile p ON cr.isr = p.isr
This seems like the LEFT may as well be INNER is there any catch?
Is there any catch? Yes there is -- left joins are a form of outer join, while inner joins are a form of, well, inner join.
Here's examples that show the difference. We'll start with the base data:
mysql> select * from j1;
+----+------------+
| id | thing |
+----+------------+
| 1 | hi |
| 2 | hello |
| 3 | guten tag |
| 4 | ciao |
| 5 | buongiorno |
+----+------------+
mysql> select * from j2;
+----+-----------+
| id | thing |
+----+-----------+
| 1 | bye |
| 3 | tschau |
| 4 | au revoir |
| 6 | so long |
| 7 | tschuessi |
+----+-----------+
And here we'll see the difference between an inner join and a left join:
mysql> select * from j1 inner join j2 on j1.id = j2.id;
+----+-----------+----+-----------+
| id | thing | id | thing |
+----+-----------+----+-----------+
| 1 | hi | 1 | bye |
| 3 | guten tag | 3 | tschau |
| 4 | ciao | 4 | au revoir |
+----+-----------+----+-----------+
Hmm, 3 rows.
mysql> select * from j1 left join j2 on j1.id = j2.id;
+----+------------+------+-----------+
| id | thing | id | thing |
+----+------------+------+-----------+
| 1 | hi | 1 | bye |
| 2 | hello | NULL | NULL |
| 3 | guten tag | 3 | tschau |
| 4 | ciao | 4 | au revoir |
| 5 | buongiorno | NULL | NULL |
+----+------------+------+-----------+
Wow, 5 rows! What happened?
Outer joins such as left join preserve rows that don't match -- so rows with id 2 and 5 are preserved by the left join query. The remaining columns are filled in with NULL.
In other words, left and inner joins are not interchangeable.
Here's a rough answer, that is sort of how I think about joins. Hoping this will be more helpful than a very precise answer due to the aforementioned math issues... ;-)
Inner joins narrow down the set of rows returns. Outer joins (left or right) don't change number of rows returned, but just "pick up" additional columns if possible.
In your first example, the result will be rows from AECounts that match the conditions specified to the 1_low_level_term table. Then for those rows, it tries to join to 1_pref_term and 1_soc_term. But if there's no match, the rows remain and the joined in columns are null.
An INNER JOIN will only return the rows where there are matching values in both tables, whereas a LEFT JOIN will return ALL the rows from the LEFT table even if there is no matching row in the RIGHT table
A quick example
TableA
ID Value
1 TableA.Value1
2 TableA.Value2
3 TableA.Value3
TableB
ID Value
2 TableB.ValueB
3 TableB.ValueC
An INNER JOIN produces:
SELECT a.ID,a.Value,b.ID,b.Value
FROM TableA a INNER JOIN TableB b ON b.ID = a.ID
a.ID a.Value b.ID b.Value
2 TableA.Value2 2 TableB.ValueB
3 TableA.Value3 3 TableB.ValueC
A LEFT JOIN produces:
SELECT a.ID,a.Value,b.ID,b.Value
FROM TableA a LEFT JOIN TableB b ON b.ID = a.ID
a.ID a.Value b.ID b.Value
1 TableA.Value1 NULL NULL
2 TableA.Value2 2 TableB.ValueB
3 TableA.Value3 3 TableB.ValueC
As you can see, the LEFT JOIN includes the row from TableA where ID = 1 even though there's no matching row in TableB where ID = 1, whereas the INNER JOIN excludes the row specifically because there's no matching row in TableB
HTH
Use an inner join when you want only the results that appear in both tables that matches the Join condition.
Use a left join when you want all the results from Table A, but if Table B has data relevant to some of Table A's records, then you also want to use that data in the same query.
Use a full join when you want all the results from both Tables.
For newbies, because it helped me when I was one: an INNER JOIN is always a subset of a LEFT or RIGHT JOIN, and all of these are always subsets of a FULL JOIN. It helped me understand the basic idea.

Many-to-many that shows columns with null

I'm having some difficulty trying to figure how to adjust my query. I'm not very good at SQL queries as it's not my forte. Anyway, I'm not sure what I'm doing wrong. Here's my table setup.
ID | Customer
---+-------------
1 | John
2 | Jane
3 | Steve
ID | Assets
---+-------------
1 | RealEstate
2 | Currency
3 | Stocks
CustomerID | AssetConfigurationId | Status
-----------+----------------------+-------
1 | 1 | E
1 | 2 | F
1 | 3 | X
2 | 3 | X
And if I query customer = 3, I want to get the following
AssetConfigurationId | Status
---------------------+------------
1 | null
2 | null
3 | X
Currently have this. I'm trying to understand how I can use left join to show all the assets and just have the values of the statuses to null for a specific customer. Right now it only shows the 3rd row. Trying to do this in a SQL Server stored procedure so that my .net application can get a list of the assets already and I'll just modify the statuses when it comes to converting them to objects.
select
ac.Id,
r.Status
from
assets ac
left join
assets_ref r on r.AssetConfigurationId = ac.Id
where
r.CustomerID = 3
Move your WHERE condition in the inner query.
select
ac.Id,
r.Status
from assets ac
left join
(select * from assets_ref where CustomerID = 3) r
on r.AssetConfigurationId = ac.Id;
You can use multiple conditions in JOINs:
select
ac.Id,
r.Status
from assets ac
left join assets_ref r
on r.AssetConfigurationId = ac.Id
and CustomerID = 3;

SQL Query Error

Please check the SQL schema and query on SQL Fiddle
I'm getting repetitive records with NULL values, if anyone can rectify the problem.
Regards
This is what I am getting:
| MEM_ID | MEM_EMAIL | GENDER | EDUCATION | PROFESSION |
|--------|----------------|--------|-----------|-------------|
| 1 | it#email.com | Male | (null) | (null) |
| 1 | it#email.com | (null) | Graduate | (null) |
| 1 | it#email.com | (null) | (null) | Engineer |
| 2 | info#email.com | Female | (null) | (null) |
| 2 | info#email.com | (null) | Graduate | (null) |
| 2 | info#email.com | (null) | (null) | Not Working |
but I need
| MEM_ID | MEM_EMAIL | GENDER | EDUCATION | PROFESSION |
|--------|----------------|--------|-----------|-------------|
| 1 | it#email.com | Male | Graduate | Engineer |
| 2 | info#email.com | Female | Graduate | Not Working |
|
Ah yes, the famous Inner-Platform effect, where you try to implement relations by creating "attribute-value" tables and assigning magic strings for data types and values, then try to retrieve values with massive self-joins at runtime.
Only madness lies down this road. SQL already includes features for enforcing key values and referential integrity; don't try to implement this yourself. It's especially frustrating because your schema is actually quite simple:
CREATE TABLE [dbo].Member(
ID INT PRIMARY KEY,
Email Varchar(50) NOT NULL,
GenderID INT NOT NULL,
EducationID INT,
ProfessionID INT
)
CREATE TABLE [dbo].Gender(
GenderID INT PRIMARY KEY,
GenderName Varchar(50) NOT NULL
)
CREATE TABLE [dbo].Education(
EducationID INT PRIMARY KEY,
EducationName Varchar(50) NOT NULL
)
CREATE TABLE [dbo].Profession(
ProfessionID INT PRIMARY KEY,
ProfessionName Varchar(50) NOT NULL
)
Assign your magic values to Gender, Education, and Profession rows and assign their IDs to Member. You can perform full lookups with a simple:
SELECT ID, Email, GenderName, EducationName, ProfessionName
FROM Member m
JOIN Gender g ON g.GenderID=m.GenderID
LEFT JOIN Education e ON e.EducationID=m.EducationID
LEFT JOIN Profession p ON p.ProfessionID=m.ProfessionID
WHERE ...
You want to enforce values? Make the Member columns NOT NULL. Want to allow, say, only a single instance of each Education row per member? Foreign-key constraints already support this, no need to invent your own query language.
I think you are basically trying to do a pivot on your data. This is one way to accomplish that.
SELECT M.mem_Id,
M.mem_email,
[Gender] = (select max( A.att_value)
from tbl_attributes A
inner join tbl_mem_att_values MAV
on MAV.att_id = A.att_id
inner join tbl_types T
on T.type_id = A.type_id
where T.type_name = 'Gender'
and MAV.mem_Id = M.mem_Id),
[Education] = (select max( A.att_value)
from tbl_attributes A
inner join tbl_mem_att_values MAV
on MAV.att_id = A.att_id
inner join tbl_types T
on T.type_id = A.type_id
where T.type_name = 'Education'
and MAV.mem_Id = M.mem_Id),
[Profession] = (select max( A.att_value)
from tbl_attributes A
inner join tbl_mem_att_values MAV
on MAV.att_id = A.att_id
inner join tbl_types T
on T.type_id = A.type_id
where T.type_name = 'Profession'
and MAV.mem_Id = M.mem_Id)
FROM tbl_members M
The result looks like this
EM_ID MEM_EMAIL GENDER EDUCATION PROFESSION
1 it#email.com Male Graduate Engineer
2 info#email.com Female Graduate Not Working
Here's what you need...
SQL Fiddle
SELECT M.mem_Id,
M.mem_email,
( SELECT AA.att_value
FROM tbl_mem_att_values mv
JOIN tbl_attributes AA ON AA.att_id = mv.att_id
JOIN tbl_types TG ON TG.type_name = 'Gender' AND TG.type_id = aa.type_id
WHERE mv.mem_id = M.mem_Id) AS Gender,
( SELECT AA.att_value
FROM tbl_mem_att_values mv
JOIN tbl_attributes AA ON AA.att_id = mv.att_id
JOIN tbl_types TG ON TG.type_name = 'Education' AND TG.type_id = aa.type_id
WHERE mv.mem_id = M.mem_Id) AS Education,
( SELECT AA.att_value
FROM tbl_mem_att_values mv
JOIN tbl_attributes AA ON AA.att_id = mv.att_id
JOIN tbl_types TG ON TG.type_name = 'Profession' AND TG.type_id = aa.type_id
WHERE mv.mem_id = M.mem_Id) AS Profession
FROM tbl_members M
Brad beat me by 6 seconds
Just as an alternative is you want to stick to the joins:
SELECT M.mem_Id,
M.mem_email,
AA.att_value AS Gender,
AB.att_value AS Education,
AC.att_value AS Profession
FROM tbl_members M
JOIN tbl_mem_att_values mavA ON M.mem_Id = mavA.mem_id
JOIN tbl_mem_att_values mavB ON M.mem_Id = mavB.mem_id
JOIN tbl_mem_att_values mavC ON M.mem_Id = mavC.mem_id
JOIN tbl_types TA ON TA.type_name = 'Gender'
JOIN tbl_types TB ON TB.type_name = 'Education'
JOIN tbl_types TC ON TC.type_name = 'Profession'
LEFT JOIN tbl_attributes AA ON mavA.att_id = AA.att_id AND TA.type_id = AA.type_id
LEFT JOIN tbl_attributes AB ON mavB.att_id = AB.att_id AND TB.type_id = AB.type_id
LEFT JOIN tbl_attributes AC ON mavC.att_id = AC.att_id AND TC.type_id = AC.type_id
WHERE AA.type_id IN (TA.type_id, TB.type_id, TC.type_id)
AND AB.type_id IN (TA.type_id, TB.type_id, TC.type_id)
AND AC.type_id IN (TA.type_id, TB.type_id, TC.type_id)

Resources