Aliases for 2 joins on one table in Microsoft Access - sql

I have a table that shows relationships between items and another table with the items themselves:
articles_to_articles
-------------------------
|articleID_1|articleID_2|
-------------------------
|12345 |67890 |
|23442 |343243 |
-------------------------
articles
-----------------------------------------------------
|article_id | article_name|lots | of | other | stuff|
-----------------------------------------------------
I am attempting to generate a file with that consists of the relationships from articles_to_articles but with the names in addition to the ids.
What I have so far is:
SELECT
a2a.articleID_1,
key_articles.article_name,
a2a.articleID_2,
val_articles.article_name
FROM
articles_to_articles a2a
INNER JOIN
articles key_articles
ON key_articles.articleID = articles_to_articles.articleID_1
INNER JOIN
articles val_articles
ON val_articles.articleID = articles_to_articles.articleID_2;
Access gives me a "missing operator" error but I can't seem to find the missing operator. What basic thing am I missing?

When joining more than two tables in MS Access, you must enclose each join within separate groups of parentheses, for example:
SELECT
a2a.articleID_1,
key_articles.article_name,
a2a.articleID_2,
val_articles.article_name
FROM
(
articles_to_articles a2a
INNER JOIN
articles key_articles
ON
key_articles.articleID = a2a.articleID_1
)
INNER JOIN
articles val_articles
ON
val_articles.articleID = a2a.articleID_2

Related

Stuck on beginner SQL practice. Multiple table where columns use same id

I'm very sorry to bother with minor problem, but I tried to search old answers for this one and since my skills in SQL are complete 0, I didn't even understand the answers :/! Neither is my English terminology great enough for properly searching.
I have these 2 tables: Cities and Flights.
Cities
+----+-------------+
|id | name |
+----+-------------+
|1 | Oslo |
|2 | New York |
|3 | Hong Kong |
+----+-------------+
Flights
+----+--------------------+-------------------+
|id | wherefrom_id | whereto_id |
+----+--------------------+-------------------+
|1 | 3 | 2 |
|2 | 3 | 1 |
|3 | 1 | 3 |
+----+--------------------+-------------------+
Now I have to write code where I need to make city ID's merge to wherefrom_id and whereto_id, in that manner that the answer shows table where you can see list of Flights (FROM/TO).
Example:
ANSWER:
+-----------+----------------+
|HONG KONG | NEW YORK |
+-----------+----------------+
|HONG KONG | OSLO |
+-----------+----------------+
|OSLO | HONG KONG |
+-----------+----------------+
This is what I wrote:
SELECT C.name, C.name
FROM Cities C, Flights F
WHERE C.id = F.wherefrom_id AND C.id = F.whereto_id;
For some reason this doesnt seem to work and I get nothing showing on my practice program. There is no error or anything it just doesnt show anything on the test answer. I really hope you get what I mean, English is not my first language and I truly tried my best to make it clear as possible :S
First things first - it's a lot easier to code in standard SQL join syntax. Converting your above to that is
SELECT C.name, C.name
FROM Cities C
INNER JOIN Flights F ON C.id = F.wherefrom_id AND C.id = F.whereto_id;
The question you've been asked requires logic people don't usually use at first so it can be confusing the first time you encounter it.
I will run through the logic jump in a moment.
Imagine your Flights table has the City names in it (not IDs).
It would have columns, say, FlightID, From_City_Name, To_City_Name.
An example row would be 1, 'Oslo', 'Prague'.
Getting the data for this would be easy e.g., SELECT Flight_ID, From_City_Name, To_City_name FROM Flights.
However, this has many problems. As your question has done, you decide to pull out the cities into their own reference tables.
For this first example, however, you decided to have two extra tables as reference tables: From_City and To_City. These would both have an ID and city name. You then change your Flights to refer to these.
Your code would look like
SELECT F.ID, FC.Name AS From_City, TC.Name AS To_City
FROM Flights
INNER JOIN From_City AS FC ON Flights.From_City_ID = FC.ID
INNER JOIN To_City AS TC ON Flights.To_City_ID = TC.ID
Notice how there are two joins there - one to From_City and one to To_City? That is because the From and To cities are referring to different things in the data.
So, then the final part of the issue: why have two city tables (from and to). Why not have one? Well, you can. If you create just one table, and modify the above, you get something like this:
SELECT F.ID, FC.Name AS From_City, TC.Name AS To_City
FROM Flights
INNER JOIN City AS FC ON Flights.From_City_ID = FC.ID
INNER JOIN City AS TC ON Flights.To_City_ID = TC.ID
Note that all that has changed is that the From_City and To_City references have been pointed to a different table City. However, the rest is the same.
And that, actually, would be your answer. The complex part that most people don't get to straight away, is having two joins to the same table.
As an aside, your original code is technically valid.
SELECT C.name, C.name
FROM Cities C
INNER JOIN Flights F ON C.id = F.wherefrom_id AND C.id = F.whereto_id;
However, what it's effectively saying is to get the city names where the From_City is the same as the To_City - which is obviously not what you want (unless you're looking for turnbacks).
What you're doing is an old SQL way of expressing joins. The standard now has better ways to declare the relationships within the from clause and I take it that your material has postponed that slightly:
There are people who will yell at you for using this ancient syntax but the answer is easy enough:
SELECT C1.name, C2.name
FROM Cities C1, Cities C2, Flights F
WHERE C1.id = F.wherefrom_id AND C2.id = F.whereto_id
You can think of this as creating a "cross product" of all city-pair combinations and matching up the ones that match actual flights. The key is to references Cities twice by using different aliases (or correlation names.)
I think this is what you are looking for..
SELECT wf.name "wherefrom", wt.name "whereto"
FROM Flights f
JOIN Cities wf
ON f.wherefrom_id = wf.id
JOIN Cities wt
ON f.whereto_id = wt.id
order by f.id

SQL Query to Show When Golfer Not Attached to an Event/Year

I am working on a school assignment that has downright stumped me for days. The task is to, using a view (VAvailableGolfers), populate a list box with Golfers who are not tied to a given event/year selected from a combo box. Here is the data in the tables:
The expected output on the form, then, would be:
2015 shows Goldstein available
2016 shows no one available
2017 shows both Goldstein and Everett available
so, in other words, where there isn't a record in TGolferEventYears for a golfer for a particular year
I have tried left joins, full outer joins, exists, not in, not exists, etc and I cannot seem to nail down the SQL to make it happen.
Here is the VB Form and the SQL backing it. I cannot figure out what to code in the view:
"SELECT intGolferID, strLastName FROM vAvailableGolfers WHERE intEventYearID = " & cboEvents.SelectedValue.ToString
Here is the view, which I know isn't giving correct output:
select tg.intGolferID, strLastName, intEventYearID
from TGolferEventYears TGEY, TGolfers TG
Where tgey.intGolferID = tg.intGolferID
and intEventYearID not IN
(select intEventYearID
from TEventYears
where intEventYearID not in
(select intEventYearID
from TGolferEventYears))
Appreciate any help
I usually approach this type of question by using a cross join to generate all possibly combination and then a left join/where to filter out the ones that already exist:
select g.intGolferID, g.strLastName, ey.intEventYearID
from TEventYears ey cross join
TGolfers g left join
TGolferEventYears gey
on gey.intGolferID = g.intGolferID and
gey.intEventYearID = ey.intEventYearID
where gey.intGolferID is null;
Try this query:
SELECT tg.intGolferID, strLastName, tey.intEventYearID, tey.intEventYear
FROM TGolfers tg, TEventYears tey
WHERE tg.intGolferID NOT IN (
SELECT DISTINCT tgey.intGolferID
FROM TGolferEventYears tgey
WHERE tgey.intEventYearID = tey.intEventYearID
)
Explanation
Since you are trying to get combinations of data that is not in TGolferEventYears, you cannot use it in your outer-most SELECT as any of its columns would be NULL. Therefore, you need to SELECT FROM the tables that are the sources of that data, and going through each joined record, filter out the combinations that are in TGolferEventYears.
Main query
Select the data you need:
SELECT tg.intGolferID, strLastName, tey.intEventYearID, tey.intEventYear
...from TGolfers, cross join with TEventYears:
FROM TGolfers tg, TEventYears tey
...where the golfer ID does not exist in the following collection:
WHERE tg.intGolferID NOT IN ( ... )
Subquery
Select unique golfer IDs:
SELECT DISTINCT tgey.intGolferID
...from TGolferEventYears:
FROM TGolferEventYears tgey
...where the year is the current year of the outer query:
WHERE tgey.intEventYearID = tey.intEventYearID
Result
+-------------+-------------+----------------+--------------+
| intGolferID | strLastName | intEventYearID | intEventYear |
+-------------+-------------+----------------+--------------+
| 1 | Goldstein | 1 | 2015 |
| 1 | Goldstein | 3 | 2017 |
| 2 | Everett | 3 | 2017 |
+-------------+-------------+----------------+--------------+

Trying to display id of table 1 as it relates to table 3

I have three tables: exfillocation, phishkit, snapshot. We need to be able to query exfillocation.filename and print the related snapshot.id, which requires traversing the phishkit table.
exfillocation.phishkit_id is related to phishkit.id as a foreign key.
Table exfillocation schema:
id exfil_location phishkit_id
== ========= ============
1 ['open.txt'] 7442
2 ['bot.txt'] 9931
phishkit.snapshot_id is related to snapshot.id as a foreign key.
Phishkit schema:
id snapshot_id md5
=== ============ =====
7442 1492 f4a3954e39b90c02f4a3954e39b90c02
9931 1661 e048f240ad0845b50abe8df9124ce3fb
Snapshot schema:
id asn url
=== ====== =============
1661 123 badwebsite.malicious.com
1492 31 haxx0rs.hacking.com
I've tried reading postgresql's four different JOIN methods as well as the UNION method, but I don't seem to get the snapshot_id column returned.
I tried something awkward this this:
SELECT exfil_location, found_in_file, phishkit_id
FROM public.lookup_exfillocation
FULL OUTER JOIN public.lookup_phishkit
ON public.lookup_exfillocation.phishkit_id = public.lookup_phishkit.id
FULL OUTER JOIN public.lookup_snapshot
ON public.lookup_phishkit.snapshot_id = public.lookup_snapshot.id WHERE exfil_location::text NOT LIKE ('__script.txt__') ORDER BY phishkit_id;
I expected to see the related lookup_snapshot.id and the related lookup_phishkit.id, which neither showed.
I accidentally found the solution. It came down to what columns I was SELECTing. Using a * showed all columns in the JOIN statements. Then I picked from the columns needed. The query looks like:
FROM public.lookup_exfillocation
FULL OUTER JOIN public.lookup_phishkit
ON public.lookup_exfillocation.phishkit_id = public.lookup_phishkit.id
FULL OUTER JOIN public.lookup_snapshot
ON public.lookup_phishkit.snapshot_id = public.lookup_snapshot.id WHERE exfil_location::text NOT LIKE ('__script.txt__') ORDER BY phishkit_id;```

SQL Query - Join the same column twice

I'm having trouble to achieve the result I want trying join a column from a table twice.
My first table is "dbo.Sessions", which contains basic session info like the user ID, the project ID, login/logout date and times, etc.
I need to join to that the user names and project names. However, these are found in another table, but in the same column (dbo.tblObjects.Name).
Example:
+------+---------------+
| k_Id | Name |
+------+---------------+
| 1 | AgentName1 |
| 2 | ProjectNameX |
| 3 | ProjectNameY |
| 4 | AgentName2 |
| 5 | ProjectNameZ |
| 6 | AgentName3 |
+------+---------------+
To try and achieve my goal, I used two "LEFT JOIN". However, I get duplicate results in both. I'll either get both columns to display either the project names or the user names (depending on which "LEFT JOIN" is first).
This is what I have at this point:
SELECT SysDB.dbo.Sessions.*, SysDB.dbo.tblObjects.Name AS AgentName, SysDB.dbo.tblObjects.Name AS ProjectName
FROM SysDB.dbo.Sessions
LEFT JOIN SysDB.dbo.tblObjects ON SysDB.dbo.Sessions.userId = SysDB.dbo.Objects.k_Id
LEFT JOIN SysDB.dbo.tblObjects ON SysDB.dbo.Sessions.projectId = SysDB.dbo.Objects.k_Id
WHERE (SysDB.dbo.Sessions.loginDate BETWEEN 'm/d/yyyy' AND 'm/d/yyyy')
Note: SysDB is the name of the database that I identify every time because this query is to be run externally. I also don't use "USE SysDB" before my selection because it doesn't work from the VBA macro this will run from.
Note 2: I have found a thread on this site that addresses this exact issue, but I can't understand what is being done, and it dates back in 2012. Something about aliases. The solution offers to add "ls." and "lt." before the table names, but that doesn't work for me. Says the table doesn't exist.
SQL Query Join Same Column Twice
Note 3: I have tried many different things, such as:
LEFT JOIN SysDB.dbo.tblObjects AS AgentName ON SysDB.dbo.Sessions.userId = SysDB.dbo.tblObjects.k_Id
LEFT JOIN SysDB.dbo.tblObjects AS ProjectName ON SysDB.dbo.Sessions.projectId = SysDB.dbo.tblObjects.k_Id
Any insights would be greatly appreciated. Thanks!
You may find it much easier to see what you are doing by giving each table an alias (session, agent, project below)
SELECT session.*, agent.Name AS AgentName, project.Name AS ProjectName
FROM SysDB.dbo.Sessions session
LEFT JOIN SysDB.dbo.tblObjects agent
ON session.userId = agent.k_Id
LEFT JOIN SysDB.dbo.tblObjects project
ON project.projectId = session.k_Id
WHERE (session.loginDate BETWEEN 'm/d/yyyy' AND 'm/d/yyyy')

Access join on first record

I have two tables in an Access database, tblProducts and tblProductGroups.
I am trying to run a query that joins both of these tables, and brings back a single record for each product. The problem is that the current design allows for a product to be listed in the tblProductGroups table more than 1 - i.e. a product can be a member of more than one group (i didnt design this!)
The query is this:
select tblProducts.intID, tblProducts.strTitle, tblProductGroups.intGroup
from tblProducts
inner join tblProductGroups on tblProducts.intID = tblProductGroups.intProduct
where tblProductGroups.intGroup = 56
and tblProducts.blnActive
order by tblProducts.intSort asc, tblProducts.curPrice asc
At the moment this returns results such as:
intID | strTitle | intGroup
1 | Product 1 | 1
1 | Product 1 | 2
2 | Product 2 | 1
2 | Product 2 | 2
Whereas I only want the join to be based on the first matching record, so that would return:
intID | strTitle | intGroup
1 | Product 1 | 1
2 | Product 2 | 1
Is this possible in Access?
Thanks in advance
Al
This option runs a subquery to find the minimum intGoup for each tblProducts.intID.
SELECT tblProducts.intID
, tblProducts.strTitle
, (SELECT TOP 1 intGroup
FROM tblProductGroups
WHERE intProduct=tblProducts.intID
ORDER BY intGroup ASC) AS intGroup
FROM tblProducts
WHERE tblProducts.blnActive
ORDER BY tblProducts.intSort ASC, tblProducts.curPrice ASC
This works for me. Maybe this helps someone:
SELECT
a.Lagerort_ID,
FIRST(a.Regal) AS frstRegal,
FIRST(a.Fachboden) AS frstFachboden,
FIRST(a.xOffset) AS frstxOffset,
FIRST(a.yOffset) AS frstyOffset,
FIRST(a.xSize) AS frstxSize,
FIRST(a.ySize) AS frstySize,
FIRST(a.Platzgr) AS frstyPlatzgr,
FIRST(b.Artikel_ID) AS frstArtikel_ID,
FIRST(b.Menge) AS frstMenge,
FIRST(c.Breite) AS frstBreite,
FIRST(c.Tiefe) AS frstTiefe,
FIRST(a.Fachboden_ID) AS frstFachboden_ID,
FIRST(b.BewegungsDatum) AS frstBewegungsDatum,
FIRST(b.ErzeugungsDatum) AS frstErzeugungsDatum
FROM ((Lagerort AS a)
LEFT JOIN LO_zu_ART AS b ON a.Lagerort_ID = b.Lagerort_ID)
LEFT JOIN Regal AS c ON a.Regal = c.Regal
GROUP BY a.Lagerort_ID
ORDER BY FIRST(a.Regal), FIRST(a.Fachboden), FIRST(a.xOffset), FIRST(a.yOffset);
I have non unique entries for Lagerort_ID on the table LO_zu_ART. My goal was to only use the first found entry from LO_zu_ART to match into Lagerort.
The trick is to use FIRST() an any column but the grouped one. This may also work with MIN() or MAX(), but I have not tested it.
Also make sure to call the Fields with the "AS" statement different than the original field. I used frstFIELDNAME. This is important, otherwise I got errors.
Create a new query, qryFirstGroupPerProduct:
SELECT intProduct, Min(intGroup) AS lowest_group
FROM tblProductGroups
GROUP BY intProduct;
Then JOIN qryFirstGroupPerProduct (instead of tblProductsGroups) to tblProducts.
Or you could do it as a subquery instead of a separate saved query, if you prefer.
It's not very optimal, but if you're bringing in a few thousand records this will work:
Create a query that gets the max of tblProducts.intID from one table and call it qry_Temp.
Create another query and join qry_temp to the table you are trying to join against, and you should get your results.