MS Access SQL - How do I fix my Joins? - sql

I'm not familiar with MS Access 2003-2007 SQL, but I have to maintain/extend a project that uses it. (The original author has left the company. Hooray for legacy code.) What I'm trying to do is join a number of related tables so that the query gives me the number of transactions for a particular user within a particular time range. The end result of this is that I want to see how many hours passed between an OUT event and the previous IN event for that user.
The code I have so far is as follows:
SELECT Directions.DirectionText, Transactions.Timestamp
FROM Users
LEFT JOIN AccessNumbers ON Users.AccessNumberID = AccessNumbers.AccessNumberID
LEFT JOIN Transactions ON AccessNumbers.Number = Transactions.Number
LEFT JOIN Events ON Transactions.Event = Events.EventNumber
LEFT JOIN Readers ON Transactions.ReaderID = Readers.ReaderID
LEFT JOIN Directions ON Readers.Direction = Direction.Direction
WHERE
(Events.EventNum IN (1, 22)) AND
(Users.[Name] = "firstName") AND
(Users.Surname = "Surname") AND
(Transactions.Timestamp >=#2017-04-10 01:00:00#) AND
(Transactions.Timestamp <=#2017-05-09 14:57:30#)
ORDER BY Transactions.Timestamp
The error I receive is "Syntax Error (missing operator in query expression 'Users.AccessNumberID = ... Direction.Direction'
I have also tried the following, to receive "Syntax Error on JOIN operation':
SELECT Directions.DirectionText, Transactions.Timestamp
FROM Users
LEFT JOIN (AccessNumbers ON Users.AccessNumberID =
AccessNumbers.AccessNumberID)
ON (AccessNumbers.Number = Transactions.Number)
ON (Transactions.Event = Events.EventNumber)
ON (Transactions.ReaderID = Readers.ReaderID)
ON (Readers.Direction = Direction.Direction)
WHERE ...
I'm aware that the SQL needs parentheses, but I don't know where to place them.

I've had issues with Access trying to put in my parentheses for me in the past... especially with LEFT joins. I'd normally parenthesize your first query like so:
SELECT Directions.DirectionText, Transactions.Timestamp
FROM ((((Users
LEFT JOIN AccessNumbers ON Users.AccessNumberID = AccessNumbers.AccessNumberID)
LEFT JOIN Transactions ON AccessNumbers.Number = Transactions.Number)
LEFT JOIN Events ON Transactions.Event = Events.EventNumber)
LEFT JOIN Readers ON Transactions.ReaderID = Readers.ReaderID)
LEFT JOIN Directions ON Readers.Direction = Directions.Direction
WHERE
(Events.EventNum IN (1, 22)) AND
(Users.[Name] = "firstName") AND
(Users.Surname = "Surname") AND
(Transactions.Timestamp >=#2017-04-10 01:00:00#) AND
(Transactions.Timestamp <=#2017-05-09 14:57:30#)
ORDER BY Transactions.Timestamp
Your second query would need more LEFT JOIN clauses thrown in to function.

A simple trick is the following:
Add a closing parenthesis before each LEFT beyond the first.
Add an opening parenthesis after FROM for each LEFT beyond the first.
Result:
SELECT Directions.DirectionText, Transactions.Timestamp
FROM ((((Users
LEFT JOIN AccessNumbers ON Users.AccessNumberID = AccessNumbers.AccessNumberID
) LEFT JOIN Transactions ON AccessNumbers.Number = Transactions.Number
) LEFT JOIN Events ON Transactions.Event = Events.EventNumber
) LEFT JOIN Readers ON Transactions.ReaderID = Readers.ReaderID
) LEFT JOIN Directions ON Readers.Direction = Direction.Direction
WHERE
(Events.EventNum IN (1, 22)) AND
(Users.[Name] = "firstName") AND
(Users.Surname = "Surname") AND
(Transactions.Timestamp >=#2017-04-10 01:00:00#) AND
(Transactions.Timestamp <=#2017-05-09 14:57:30#)
ORDER BY Transactions.Timestamp
The result is the same as Sturgus says, but adding them this way makes sense to me. For layout purposes, you might move each closing parenthesis to the line above it.

Related

'or' operator in left join

select
evm.case_id, evm.close_ts, evm.cm_promise_utc_ts, evm.cm11, evm.cm13, evm.creat_id, evm.creat_ts, evm.creat_type,
evm.event_act, evm.event_act_perf, evm.event_dt, evm.event_id, evm.event_orig, evm.event_srce, evm.event_sta, evm.event_sub_type, evm.event_type,
evm.fol_pref_mthd, evm.incdnt_no,
evm.lst_updt_id, evm.lst_updt_ts, evm.lst_updt_type,
evm.main_sta, evm.new_act_note, evm.orig_id, evm.own_type, evm.prod_type, evm.req_chan, evm.srvc_arm,
crm.case_nm,
evg.req_add_info as req_add_info_general, evg.rslt_add_info,
evt.add_info, evt.other_ds, evt.req_add_info as req_add_info_ticketing,
eve.event_iss, eve.event_rev,
eve.req_cm_commentary, eve.req_event_nm, eve.rslt_err_dt, eve.rslt_root_cause, eve.rslt_rslv_dtl_cnfr, eve.rslt_rslv_dtl_outcm,
tlsopus.agnt_emply_sta, tlsopus.dlvr_mthd, tlsopus.dm_ctry,
tlsopus.event_subtype as event_sub_type_tlsarpt,
tlsopus.mkt_alpha_cd, tlsopus.own_id, tlsopus.prod_full_nm,
tlsopus.repr_locat, tlsopus.req_pref_spec_dt, tlsopus.req_vend, tlsopus.rslt_rec_loctr,
tlsopus.trvl_prod, tlsopus.trvl_type, tlsopus.user_role,
tlssales.trip_id, tlssales.inv_dt, tlssales.refd_exch_in,
tlssales.trip_type, tlssales.trans_usd_am, tlssales.mkt_cd, tlssales.chan_type,tlssales.exp_trip_id
from cstonedb3.opus_event_master as evm
left join cstonedb3.opus_crm_cases as crm
on evm.case_id = crm.case_id
left join cstonedb3.opus_event_general as evg
on evm.event_id = evg.event_id
left join cstonedb3.opus_event_ticketing as evt
on evm.event_id = evt.event_id
left join cstonedb3.opus_event_escalation as eve
on evm.event_id = eve.event_id
left join cstonedb3.tlsarpt_opus_case_detail as tlsopus
on evm.event_id = tlsopus.event_id
left join cstonedb3.tlsarpt_travel_sales as tlssales
on tlsopus.rslt_rec_loctr = tlssales.trip_id
or tlsopus.rslt_rec_loctr = tlssales.exp_trip_id
where evm.creat_ts between '2022-07-01' and '2022-07-31'
and tlsopus.mkt_alpha_cd = 'US';
tlssales table has two columns that correspond to one column in tlsopus
i.e the values for "tlsopus.rslt_rec_loctr" can be present in either "tlssales.trip_id" or "tlssales.exp_trip_id".
I want the join to first search through the "tlssales.trip_id" column. If there's no match here in this column, then look for a match in "tlssales.exp_trip_id" column.
For this I applied the or condition in the last join statement. But this query gives the following error :
FAILED: SemanticException Cartesian products are disabled for safety
reasons. If you know what you are doing, please
sethive.strict.checks.cartesian.product to false and that
hive.mapred.mode is not set to 'strict' to proceed. Note that if you
may get errors or incorrect results if you make a mistake while using
some of the unsafe features.
Can someone please explain where I'm going wrong? I tried to look through other pages and found out that we can apply or in join.

Syntax error in complicated JOIN clause of SQL query through Excel VBA

I have 4 tables I need to join w/ the following fields that can be linked:
tblModels (TRADE_DATE, MODEL)
tblModelDef (MODEL, ASSET)
tblFutPx (TRADE_DATE, ASSET)
tblTA (TRADE_DATE, ASSET)
I want to "LEFT JOIN" the tblModels- i.e. I want to display all of the data for tblModels and show data for tblFutPx and tblTA when available for the corresponding [TRADE_DATE]. So the link is [TRADE_DATE] and [ASSET] for the relevant data, but I need to use tblModelDef as a bridge between tblModels and tblFutPx & tblTA.
I keep getting "Syntax error in JOIN operation" errors.
I've been trying to get this to work for way too long, but this is where I'm at. I've tried simplifying the FROM clause to ensure the error is coming from it, and it is.
FROM ((tblModels INNER JOIN tblModelDef ON tblModels.[MODEL] = tblModelDef.[MODEL]) LEFT JOIN tblFutPx ON (tblModelDef.[ASSET] = tblFutPx.[ASSET]) AND (tblModels.[TRADE_DATE] = tblFutPx.[TRADE_DATE])) LEFT JOIN (tblTA ON (tblModels.[TRADE_DATE] = tblTA.[TRADE_DATE]) AND (tblModelDef.[ASSET] = tblTA.[ASSET]))
Probably need multiple queries to accomplish. Built tables and used Query Builder to design query. Did not assign any primary keys, just set links to 'show all records from tblModels...'. Access used RIGHT JOIN.
Query1:
SELECT tblModelDef.Model, tblModelDef.Asset, tblModels.Trade_Date
FROM tblModelDef RIGHT JOIN tblModels ON tblModelDef.Model = tblModels.Model;
Query2:
SELECT Query1.Model, tblFutPx.Trade_Date, tblFutPx.Asset, tblTA.Trade_Date, tblTA.Asset
FROM tblFutPx RIGHT JOIN (tblTA RIGHT JOIN Query1 ON (tblTA.Asset = Query1.Asset) AND (tblTA.Trade_Date = Query1.Trade_Date)) ON (tblFutPx.Asset = Query1.Asset) AND (tblFutPx.Trade_Date = Query1.Trade_Date);
All in one nested:
SELECT Query1.Model, tblFutPx.Trade_Date, tblFutPx.Asset, tblTA.Trade_Date, tblTA.Asset
FROM tblFutPx RIGHT JOIN (tblTA RIGHT JOIN
(SELECT tblModelDef.Model, tblModelDef.Asset, tblModels.Trade_Date
FROM tblModelDef RIGHT JOIN tblModels ON tblModelDef.Model = tblModels.Model) AS Query1
ON (tblTA.Asset = Query1.Asset) AND (tblTA.Trade_Date = Query1.Trade_Date)) ON (tblFutPx.Asset = Query1.Asset) AND (tblFutPx.Trade_Date = Query1.Trade_Date);

SQL syntax - running stored procedure repeats some rows

I'm very confused with a stored procedure.
The stored procedure - regardless of how I try to use the joins - seems to repeat certain records. It's always the same user every time this procedure is run within the date range 11-1-2017 to 11-30-2017. I'm seriously at a loss here.
I've attached a screenshot showing 1 record repeating at the bottom of the screenshot.
The code here is where I believe the issue lies:
Code:
FROM
tbl_Claims
LEFT JOIN
tbl_Users ON tbl_Claims.ProvidedBy = tbl_Users.UserID
--***12-15-2017***
LEFT JOIN
tbl_ClaimDetails ON tbl_Claims.ClaimDetailID = tbl_ClaimDetails.ClaimDetailID
LEFT JOIN
tbl_Individuals ON tbl_ClaimDetails.IndID = tbl_Individuals.IndID
--***12-15-2017***
LEFT JOIN
tbl_Users ApprovedByJoin ON tbl_ClaimDetails.ApprovedBy = ApprovedByJoin.UserID
LEFT JOIN
tbl_Events ON tbl_Claims.InternalEventID = tbl_Events.EventID
LEFT JOIN
tbl_Events ted ON tbl_Events.EventID = tbl_Events.EventID
LEFT JOIN
tbl_ExpenseType ON tbl_Claims.ExpenseTypeID = tbl_ExpenseType.ExpenseTypeID
LEFT JOIN
tbl_Services ON tbl_Claims.ServiceID = tbl_Services.ServiceID

Order of join operations: would these two FROM clauses produce the same results? [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
Would these two FROM clauses produce the same results? And if not, is there a way to write the the first one so that no parenthesis are needed?
FROM SALESTAX
RIGHT JOIN ( ITEMS
RIGHT JOIN ( PINVOICE
INNER JOIN PINVDET ON PINVOICE.PNV_INVOICENO = PINVDET.PND_INVOICENO AND PINVOICE.PNV_Site = PINVDET.PND_Site
) ON ITEMS.ITE_INVNO = PINVDET.PND_INVNO
) ON SALESTAX.STX_GroupID = PINVDET.PND_TAX1
FULL JOIN ( CUSTMS
RIGHT JOIN CUSMER ON CUSTMS.TMS_CODE = CUSMER.CUS_TERM
) ON PINVDET.PND_CUSTID = CUSMER.CUS_CustID
FROM CUSTMS RIGHT JOIN
CUSMER ON TMS_CODE = CUS_TERM FULL JOIN
PINVDET ON PND_CUSTID = CUS_CustID LEFT JOIN
PINVOICE ON PNV_INVOICENO = PND_INVOICENO AND PNV_Site = PND_Site LEFT JOIN
SALESTAX on STX_GROUPID = PND_TAX1 left join
ITEMS on ITE_INVNO = PND_INVNO
EDIT: While I'd like to know the answer to the first question, I'm more immediately interested in just having a more straightforward version of the first FROM clause that doesn't need parenthesis so if you'd rather just rewrite it than compare the two then feel free to just do that.
I have no idea if the 1st is equivalent to the 2nd (first because the queries are unfriendly formatted, to say the least and second because RIGHT joins are kind of confusing, since many are used to write using LEFT joins.) But to answer the question:
Is there a way to write the the first one so that no parenthesis are needed?
Yes, you can simply remove the parentheses from the 1st query.
Keeping the parentheses and formatted with some white space:
FROM
SALESTAX
RIGHT JOIN
( ITEMS
RIGHT JOIN
( PINVOICE
INNER JOIN
PINVDET
ON PINVOICE.PNV_INVOICENO = PINVDET.PND_INVOICENO
AND PINVOICE.PNV_Site = PINVDET.PND_Site
)
ON ITEMS.ITE_INVNO = PINVDET.PND_INVNO
)
ON SALESTAX.STX_GroupID = PINVDET.PND_TAX1
FULL JOIN
( CUSTMS
RIGHT JOIN
CUSMER
ON CUSTMS.TMS_CODE = CUSMER.CUS_TERM
)
ON PINVDET.PND_CUSTID = CUSMER.CUS_CustID
Without parentheses and white-space formatted:
FROM
SALESTAX
RIGHT JOIN
ITEMS
RIGHT JOIN
PINVOICE
INNER JOIN
PINVDET
ON PINVOICE.PNV_INVOICENO = PINVDET.PND_INVOICENO
AND PINVOICE.PNV_Site = PINVDET.PND_Site
ON ITEMS.ITE_INVNO = PINVDET.PND_INVNO
ON SALESTAX.STX_GroupID = PINVDET.PND_TAX1
FULL JOIN
CUSTMS
RIGHT JOIN
CUSMER
ON CUSTMS.TMS_CODE = CUSMER.CUS_TERM
ON PINVDET.PND_CUSTID = CUSMER.CUS_CustID
To answer the other question, about the 2nd query, no it isn't equivalent. You missed the table aliases and changed an inner join to left join. This is equivalent to the 1st:
FROM CUSMER
LEFT JOIN
CUSTMS ON CUSTMS.TMS_CODE = CUSMER.CUS_TERM
FULL JOIN
PINVDET
INNER JOIN -- this is changed
PINVOICE ON PINVOICE.PNV_INVOICENO = PINVDET.PND_INVOICENO
AND PINVOICE.PNV_Site = PINVDET.PND_Site
LEFT JOIN
ITEMS ON ITEMS.ITE_INVNO = PINVDET.PND_INVNO
LEFT JOIN
SALESTAX ON SALESTAX.STX_GroupID = PINVDET.PND_TAX1
ON PINVDET.PND_CUSTID = CUSMER.CUS_CustID
I encourage you to write your from clauses by placing all left joins first followed by inner joins. This greatly simplifies trying to figure out what queries are doing. A series of left joins says "keep all the rows in the first table". A series of inner joins says "keep only rows where there are matches between tables." (Occasionally, you might need subqueries as in the first example.)
It is unlikely in this example that the two are the same. In the first, the full join is the "outermost" join. In the second, the full join is embedded in a series of joins. These are interpreted sequentially from the first to the last. One of these is probably converting the full join to an inner join or left join. Of course, the two could produce equivalent results if all the tables match.
The second example could probably be written as:
FROM CUSMER LEFT JOIN
CUSTMS ON TMS_CODE = CUS_TERM LEFT JOIN
PINVDET ON PND_CUSTID = CUS_CustID LEFT JOIN
PINVOICE ON PNV_INVOICENO = PND_INVOICENO AND PNV_Site = PND_Site LEFT JOIN
SALESTAX on STX_GROUPID = PND_TAX1 left join
ITEMS on ITE_INVNO = PND_INVNO
(assuming that something after the full join is converting it to a left join anyway).

MySQL with 2 LEFT JOINs on the same table

I'm trying to run this query:
SELECT
Destaque.destaque, Noticia.id, Noticia.antetitulo,
Noticia.titulo, Noticia.lead, Noticia.legenda,
Noticia.publicacao, Seccao.descricao, Album.pasta,
Foto.ficheiro, Foto.descricao, Cronista.nome,
Cronista.profissao, Cronista.ficheiro,
AudioFile.*, AudioCollection.*, VideoFile.*, VideoCollection.*
FROM
nt_highlights AS Destaque
LEFT JOIN nt_noticias AS Noticia ON Destaque.noticia_id = Noticia.id
LEFT JOIN mm_fotos AS Foto ON Noticia.foto_id = Foto.id
LEFT JOIN nt_temas AS Seccao ON Noticia.tema_id = Seccao.id
LEFT JOIN mm_albuns AS Album ON Foto.album_id = Album.id
LEFT JOIN nt_cronistas AS Cronista ON Cronista.id = Noticia.cronista_id
LEFT JOIN ntNoticias_mmFiles AS Rel ON Rel.noticia_id = Noticia.id
LEFT JOIN mm_files AS AudioFile ON AudioFile.id = Rel.file_id
LEFT JOIN mm_coleccoes AS AudioCollection ON AudioFile.coleccao_id = AudioCollection.id
LEFT JOIN mm_files AS VideoFile ON VideoFile.id = Rel.file_id
LEFT JOIN mm_coleccoes AS VideoCollection ON VideoFile.coleccao_id = VideoCollection.id
WHERE
Destaque.area_id = 1
AND Noticia.paraPublicacao = 1
AND Noticia.publicacao <= NOW()
AND (AudioFile.mimeType != '' OR AudioFile.id IS NULL)
AND (VideoFile.mimeType = '' OR VideoFile.id IS NULL)
ORDER BY
Destaque.destaque
This will get me a number of articles (from nt_noticias) and the idea is to get at the same time a Video and an Audio file from the mm_files table.
What happens is that when I have an article with a sound and a video, MySQL will return 4 rows:
with the sound (video is null)
with the video (sound is null)
with all nulls
with the sound AND the video
How can I "force" it to return just one row per article with any existing video AND audio associated? What am I doing wrong here?
I think you want something like this:
SELECT
Destaque.destaque, Noticia.id, Noticia.antetitulo,
Noticia.titulo, Noticia.lead, Noticia.legenda,
Noticia.publicacao, Seccao.descricao, Album.pasta,
Foto.ficheiro, Foto.descricao, Cronista.nome,
Cronista.profissao, Cronista.ficheiro,
AudioFile.*, AudioCollection.*, VideoFile.*, VideoCollection.*
FROM
nt_highlights AS Destaque
LEFT JOIN nt_noticias AS Noticia ON Destaque.noticia_id = Noticia.id
LEFT JOIN mm_fotos AS Foto ON Noticia.foto_id = Foto.id
LEFT JOIN nt_temas AS Seccao ON Noticia.tema_id = Seccao.id
LEFT JOIN mm_albuns AS Album ON Foto.album_id = Album.id
LEFT JOIN nt_cronistas AS Cronista ON Cronista.id = Noticia.cronista_id
LEFT JOIN ntNoticias_mmFiles AS AudioRel ON Rel.noticia_id = Noticia.id
AND AudioRel.file_id IN (
SELECT file_id
FROM ntNoticias_mmFiles
WHERE noticia_id = Noticia.id AND IsAudioFile = 1 /* whatever the check is */
LIMIT 1
)
LEFT JOIN mm_files AS AudioFile ON AudioFile.id = Rel.file_id
LEFT JOIN mm_coleccoes AS AudioCollection ON AudioFile.coleccao_id = AudioCollection.id
LEFT JOIN ntNoticias_mmFiles AS VideoRel ON VideoRel.noticia_id = Noticia.id
AND VideoRel.file_id IN (
SELECT file_id
FROM ntNoticias_mmFiles
WHERE noticia_id = Noticia.id AND IsVideoFile = 1 /* whatever the check is */
LIMIT 1
)
LEFT JOIN mm_files AS VideoFile ON VideoFile.id = Rel.file_id
AND VideoFile.IsVideoFile = 1
LEFT JOIN mm_coleccoes AS VideoCollection ON VideoFile.coleccao_id = VideoCollection.id
WHERE
Destaque.area_id = 1
AND Noticia.paraPublicacao = 1
AND Noticia.publicacao <= NOW()
ORDER BY
Destaque.destaque
My thought was this:
You want one audio file and one video file, at most. There are several files available per Noticia, so you need to make sure that a maximum of one file per type gets into the join. This also means you have to join in the ntNoticias_mmFiles table twice — once per type.
This is what the sub-queries in the join conditions are supposed to do: Select one row per file type. Going on from there you LEFT JOIN the rest of the data in, just like you already do.
The JOIN will return all the combinations, that's the problem.
If you only have one audio and/or videofile per article then you might want to look at subselects.
In SQL Server this would look something like (untested code):
SELECT title,
(select TOP 1 audio from audio where audio.aid = articles.id) as Audio,
(select TOP 1 video from video where video.aid = articles.id) as Video
FROM articles
Be careful that on large datasets this can perform poorly as the subselects in this example are executed individually for each row returned to the outer query. For example, if you return 10,000 articles then a total of 20,001 queries would actually be executed on the server.
There are other possible answers to overcome this but they get more involved (I suspect you could do something with a derived table but it eludes me at the moment).
You probably want to optimize that join query into a view. It's a large query, and with that many joins, it's going to be pretty inefficient. Plus, a view helps you debug the joins and will basically simplifies by allowing you to write your joins (in the view) and the WHERE clause (in your select from the view) separately, which can help with debugging the queries.