If logic in SQL or Access queries - sql

Greetings, fellow SO people.
I am working on a project that has me working with an Access database. Here's the setup:
I have three tables:
Tab1 with employee names, ID#s, Manager names and Manager ID#s.
Tab2 with chat info, employee ID#s and employee names.
Tab3 with Manager ID#s, Manager names and team names.
I currently have a query that selects the following:
tab2.[employee name], tab2.[employee id], tab3.[chat info], tab1.[manager id], tab1.[manager id], tab3.[team name]
where
tab2.[employee id] = tab1.[employee id] and tab2.[manager id] = tab3.[manager id];
What I am trying to accomplish is this: I would like to have a way to put "Unknown" in the "Team" field if the IDs don't match up somewhere along the line. Any ideas?
Thanks in advance!

Perhaps something like:
select tab2.[employee name],
tab2.[employee id],
tab3.[chat info],
tab1.[manager id],
Nz(tab3.[team name], 'Unknown') as [team name]
from (tab2
left join tab1
on tab2.[employee id] = tab1.[employee id])
left join tab3
on tab2.[manager id] = tab3.[manager id]

SELECT
tab2.[employee name], tab2.[employee id],
tab3.[chat info], tab1.[manager id],
tab1.[manager id],
Nz(tab3.[team name],"Unknown")
FROM (tab2
LEFT JOIN tab1
ON tab2.[employee id] = tab1.[employee id])
LEFT JOIN tab3
ON tab2.[manager id] = tab3.[manager id];

It seems that Access doesn't support SQL case statements, so my previous answer is incorrect. I'm leaving it there for anyone looking for the same problem with a SQL compliant database.
Apparently you can use a switch statement to achieve the same result; this example shows how a switch can be used. Hopefully that's more helpful.
Otherwise, if possible, switch to a real DB :)

Okay, I was slow off the mark with the fact that you can't use CASE, and Joe's use of Nz is probably your best bet, but there is another Access-specific alternative, presented here in case it fits your situation better:
select tab2.[employee name],
tab2.[employee id],
tab3.[chat info],
tab1.[manager id],
Iif(IsNull(tab3.[team name]), 'Unknown', tab3.[team name]) as [team name]
from tab2
left join tab1
on tab2.[employee id] = tab1.[employee id]
left join tab3
on tab2.[manager id] = tab3.[manager id]
In this situation, Iif( condition, trueAnswer, falseAnswer ) does the same thing as the Nz, but if your condition is something besides an IsNull it can be more flexible.

I'm not sure about Access, but in SQL normally you would left join your tables and you can use a CASE statement to add the conditional behaviour your looking for; access may not support this.
The example shown here is reasonably standard.
Wow, do access queries really look like that? Hmmm... Then perhaps something like this.
tab2.[employee name], tab2.[employee id], tab3.[chat info], tab1.[manager id], tab1.[manager id], case when tab3.[team name] is null then 'Unknown' else tab3.[team name] end as [team name]
where
tab2.[employee id] = tab1.[employee id] and tab2.[manager id] = tab3.[manager id];

Related

Can't find error with MS Access SQL FROM Clause Syntax

Select distinct [Doc Type], [Customer Number], count([Customer Number]) , [T] From (
Select distinct A.[Customer Number] & A.[Membership Number], A.[Customer Number] , B.[Doc Type ], B.[SumOpenAmount] From(
SELECT distinct [Doc Type] , [Customer Number], Sum([Open Amount]) as T FROM Data Where [Doc Type] = 'RU')B, [Data] A
Where B.[Customer Number] = A.[Customer Number] Group by [Doc Type])
group by [Doc Type], [Customer Number]
having count([Customer Number]) = 1
Throwing an Error that Doc Type could refer to more than 1 table listed in the from clause of your SQL Statement
Currently, your query has a number of syntax and suboptimal issues:
GROUP BY: In aggregate queries that contain non-aggregated columns in SELECT clause, GROUP BY must be used. Some dialects allow GROUP BY columns to be omitted but not Access SQL. Also, DISTINCT is not necessary for GROUP BY.
ALIASES: Whenever subqueries and joins are utilized, always use table aliases to avoid name collision for both derived tables and column aliases for all expressions. Additionally, avoid A, B, C ... for more informative aliases including T. See Bad Habits to Kick : Using table aliases like (a, b, c) or (t1, t2, t3).
EXPLICIT JOIN: Use the current ANSI SQL standard of explicit joins and not the outdated implicit joins that use WHERE. See Explicit vs implicit SQL joins.
Therefore, consider following adjustments that employ the above guidelines.
SELECT [doc type]
, [customer number]
, COUNT([customer number]) As CountCustomerNumber -- ALIAS ADDED
, SUM([SumOpenAmount]) As TotalOpenAmount -- AGGREGATED COLUMN
FROM
(SELECT d.[customer number] & d.[membership number] AS CustMemb -- ALIAS ADDED
, d.[customer number]
, agg.[doc type]
, SUM(agg.[TotalSubOpenAmount]) AS SumOpenAmount -- AGGREGATED COLUMN
FROM (SELECT [doc type]
, [customer number]
, SUM([open amount]) AS TotalSubOpenAmount -- INFORMATIVE ALIAS
FROM data
WHERE [doc type] = 'RU'
GROUP BY [doc type]
, [customer number]
) agg -- INFORMATIVE ALIAS
INNER JOIN [data] d -- INNER JOIN USED
ON d.[customer number] = agg.[customer number]
GROUP BY d.[customer number] & d.[membership number] -- GROUP BY COLUMNS ADDED
, d.[customer number]
, agg.[doc type]
) AS sub -- ALIAS ADDED
GROUP BY [doc type]
, [customer number]
HAVING COUNT([customer number]) = 1
Note: Since Access does not support comments in queries. Remove all -- messages before running.
It appears that the B.[DOC TYPE ] in the sub-query has an extra space in the field name.
Also, the sub-query does not reference the inner sub-query's [T] field and as such it will not be available to the main query unless it is in the Data table.
Finally, the outer sub-query's group by does not specify which data source the [Doc Type] is coming from for the grouping.
Try this
Select distinct
[Doc Type],
[Customer Number],
count([Customer Number]),
[T]
From
(
Select
distinct A.[Customer Number] & A.[Membership Number],
A.[Customer Number] ,
B.[Doc Type],
B.[T]
From
(
SELECT distinct
[Doc Type] ,
[Customer Number],
Sum([Open Amount]) as T
FROM
Data
Where [Doc Type] = 'RU'
)B,
[Data] A
Where B.[Customer Number] = A.[Customer Number]
Group by B.[Doc Type]
)
group by [Doc Type], [Customer Number]
having count([Customer Number]) = 1
So, this is a good reason to do aliasing. I think what's happening is your innermost (data) subquery is returning doctype (becomes b as part of the outer subquery), and a also has a doc type. You can also remove the inner Group By clause, because it's done on the outermost query; the results should be the same.
I also noticed that you do this: A.[Customer Number] & A.[Membership Number] and then don't do anything with the column. If you want to do something with that, you should name the Column. I named it CMN below, you can pick whatever you want.
Am I correct that you're also doing an implicit JOIN with the line ) as B, [Data] A? If so, you should consider making that explicit, or you may end up with undesired matches.
If that's what you want, do this:
-- as B, [Data] A
++ as B LEFT JOIN [Data] as A on a.[Customer Number] = b.[Customer Number]
This way, you can get rid of your Where B.[Customer Number] = A.[Customer Number] line (after testing, of course), and you'll end up with a more explicitly defined JOIN. See bottom for what that looks like.
The first Group by [Doc Type] is what's tripping you up.
When referring to fields, it's my personal preference to always add an alias unless I'm only working with a simple oneliner, with one table/view, even if there aren't any fields with similar names, because I usually end up with duplicate names in the future. Even then, I try to add aliases, because then later if I decide I want to add more fields/tables it doesn't make me re-factor the whole thing.
Try this (if you're not doing implicit JOIN):
Select distinct c.[Doc Type], c.[Customer Number], c.CMN, count(c.[Customer Number]) , c.[T]
From (
Select distinct (A.[Customer Number] & A.[Membership Number]) as CMN, A.[Customer Number] , B.[Doc Type], B.[SumOpenAmount]
From(
SELECT distinct d.[Doc Type] , d.[Customer Number], Sum(d.[Open Amount]) as T
FROM Data as d
Where d.[Doc Type] = 'RU'
) as B, [Data] A
Where B.[Customer Number] = A.[Customer Number]
) as C
group by C.[Doc Type], C.[Customer Number], C.CMN
having count(C.[Customer Number]) = 1
Do this if you want to have an explicit JOIN (recommended):
Select distinct c.[Doc Type], c.[Customer Number], c.CMN, count(c.[Customer Number]) , c.[T]
From (
Select distinct (A.[Customer Number] & A.[Membership Number]) as CMN, A.[Customer Number] , B.[Doc Type], B.[SumOpenAmount]
From(
SELECT distinct d.[Doc Type] , d.[Customer Number], Sum(d.[Open Amount]) as T
FROM Data as d
Where d.[Doc Type] = 'RU'
) as B
LEFT JOIN [Data] as A on a.[Customer Number] = b.[Customer Number]
) as C
group by C.[Doc Type], C.[Customer Number], C.CMN
having count(C.[Customer Number]) = 1
(Removed extra spaces)

The specified field [Report].[Field] could refer to more than one table listed in the FROM clause of your SQL statement

I am creating a report based on a query in Microsoft Access, and I keep receiving an error message when switching from Design View to Report view.
The specified field '[Customer Report].[Need Date]' could refer to more than
one table listed in the FROM clause of your SQL statement.
I have searched through questions that have already been asked, and it seems as though the answer is simple: I just need to specify what table the [Need Date] is coming from in the SQL coding. However, I've already done that, and the error message continues to happen. Here is the coding for the query that my report is based on:
SELECT
[UID Only].[Our PN],
[UID Only].[Customer PN],
[UID Only].Description,
[UID Only].[Order #],
[UID Only].[Customer PO],
[UID Only].[Need Date],
[Customer Report].[Need Date],
[UID Only].[Unique ID]
FROM ([UID Only]
LEFT JOIN [UID, Date] ON [UID Only].[Unique ID] = [UID, Date].[Unique ID])
LEFT JOIN [Customer Report] ON [UID Only].[Unique ID] = [Customer Report].[Unique ID]
WHERE [UID, Date].[Unique ID] Is Null;
The error comes from the report, not from the query itself.
The records resulting from the query shown above contains two fields named [Need Date] and the report cannot differentiate them. Give them different aliases
SELECT
[UID Only].[Our PN],
[UID Only].[Customer PN],
[UID Only].Description,
[UID Only].[Order #],
[UID Only].[Customer PO],
[UID Only].[Need Date] AS UO_NeedDate,
[Customer Report].[Need Date] AS CR_NeedDate,
[UID Only].[Unique ID]
FROM ([UID Only]
LEFT JOIN [UID, Date] ON [UID Only].[Unique ID] = [UID, Date].[Unique ID])
LEFT JOIN [Customer Report] ON [UID Only].[Unique ID] = [Customer Report].[Unique ID]
WHERE [UID, Date].[Unique ID] Is Null;
Now, in the report you can refer to them as UO_NeedDate and CR_NeedDate.

Ms Access Delete Query with Join and Where Clause

I have 2 tables (tblTrainingElements and tblCourses).
tblTrainingElements has the following fields:
([Training Element ID], [Course ID], [Title],[Duration (min)],[Patient],[Status],[Description], [Comments],[Site],[ElementSeq])
tblCourses has the following relevant fields:
[Course ID], [App ID]
I need to delete all records in tblTrainingElements WHERE the [App ID] from tblCourses = "CAD". I need to join the tables on the [Course ID] field. Below is the SQL statement that I tried to use. I keep getting the error message "Please specify which table you would like to delete from"
DELETE tblCourses.[Course Name], tblCourses.[App ID], tblTrainingElements.[Training Element ID], tblTrainingElements.[Course ID], tblTrainingElements.Title, tblTrainingElements.[Duration (min)], tblTrainingElements.Patient, tblTrainingElements.Status, tblTrainingElements.Description, tblTrainingElements.Comments, tblTrainingElements.Site, tblTrainingElements.ElementSeq
FROM tblCourses INNER JOIN tblTrainingElements ON tblCourses.[Course ID] = tblTrainingElements.[Course ID]
WHERE (((tblCourses.[App ID])="CAD"))
Delete records only in tblTrainingElements table:
DELETE *
FROM tblTrainingElements
WHERE tblTrainingElements.[Course ID] IN (SELECT tblCourses.[Course ID] FROM tblCourses WHERE tblCourses.[App ID]="CAD" );
DELETE tc
FROM tblCourses tc
INNER JOIN tblTrainingElements tte ON tc.[Course ID] = tte.[Course ID]
WHERE (((tc.[App ID])="CAD"))

Using an aggregate function as part of an expression

Simple question, I assume it is impossible to use an aggregate function as part of an expression but here is basically what I want to do:
SELECT ((Sum(RESOURCES.[Hours Spent]*EMPLOYEES.[Employee Rate]))
+ GRANTS.Overhead) AS [Estimated Development Cost]
FROM GRANTS
INNER JOIN
(EMPLOYEES
INNER JOIN RESOURCES ON EMPLOYEES.[Employee Number] = RESOURCES.[Employee Number])
ON GRANTS.[Grant Program Number] = RESOURCES.[Grant Program Number];
It works perfectly before I try to add that last value to it (Grants.Overhead). How could I go about doing this?
Thanks.
You have to put the Grants.Overhead in a separate query, as it counts only once per group. So here you go:
SELECT T1.EDC + GRANTS.Overhead AS [Estimated Development Cost]
FROM (SELECT RESOURCES.[Grant Program Number] AS GPN,
(Sum(RESOURCES.[Hours Spent] * EMPLOYEES.[Employee Rate])) AS EDC
FROM EMPLOYEES
INNER JOIN
RESOURCES
ON EMPLOYEES.[Employee Number] = RESOURCES.[Employee Number]
GROUP BY RESOURCES.[Grant Program Number]) AS T1
INNER JOIN
GRANTS
ON GRANTS.[Grant Program Number] = T1.GPN;
You need to sum() the overhead too, otherwise you will get the old "it is not contained in either an aggregate function or the GROUP BY clause" error.
This works:
SELECT ((Sum(RESOURCES.[Hours Spent]*EMPLOYEES.[Employee Rate])) + sum(GRANTS.Overhead)) AS [Estimated Development Cost]
FROM GRANTS
INNER JOIN (EMPLOYEES
INNER JOIN RESOURCES ON EMPLOYEES.[Employee Number] = RESOURCES.[Employee Number])
ON GRANTS.[Grant Program Number] = RESOURCES.[Grant Program Number];

SQL joins and group by with 3 separate tables

I have two separate queries here that I need to make into one query, I'll post the queries, then try to explain what I'm trying to do.
SELECT Distinct I.ITMCDE, V.VNDRCDE, V.VNAME
FROM (SELECT RIGHT(Items.[Item Number], 3) as ITMCDE FROM Items) I,
(SELECT LEFT(Vendors.[Vendor ID], 3) as VNDRCDE,
Vendors.[Vendor Name] as VNAME
FROM Vendors) V
WHERE I.ITMCDE = V.VNDRCDE
In this first one, I simply match up the vendor code with the item code, to get the vendor name that produces the item.
SELECT DISTINCT (Items.[Item Description]), ItemQuantities.[QTY Available],
Items.[Selling U Of M], Items.[Item Number]
FROM ItemQuantities
INNER JOIN Items ON ItemQuantities.[Item Number] = Items.[Item Number]
WHERE Items.[Item Number] LIKE 'WH%'
AND Items.[Item Number] NOT LIKE '%RMW'
In this second one I'm selecting the item description, quantity available from two separate tables (quantity available is in a different table, match them up using the item number)
As you can see, the only correlation between the three tables is the item number, and not even that in the vendors table. The last three characters of the item number correlate with the first three characters of the vendor id... I did not design this setup. I'm just trying to work with it now.
How do I join these two statements into one single statement that will give me the vendor name, item description, Unit of Measure (Selling U of M), and item quantity where the item description is unique?
I think this should work:
SELECT DISTINCT Items.[Item Description],
ItemQuantities.[QTY Available],
Items.[Selling U Of M],
Items.[Item Number],
V.VNAME
FROM ItemQuantities
INNER JOIN Items ON ItemQuantities.[Item Number] = Items.[Item Number]
INNER JOIN Vendors ON
RIGHT(Items.[Item Number], 3) = LEFT(Vendors.[Vendor ID], 3)
WHERE Items.[Item Number] LIKE 'WH%'
AND Items.[Item Number] NOT LIKE '%RMW'
You can join it in:
SELECT DISTINCT (i.[Item Description]), iq.[QTY Available],
i.[Selling U Of M], i.[Item Number],
V.VNDRCDE, V.VNAME
FROM ItemQuantities iq INNER JOIN
Items i
ON iq.[Item Number] = i.[Item Number] left outer join
Vendors v
on LEFT(v.[Vendor ID], 3) = RIGHT(i.[Item Number], 3)
WHERE i.[Item Number] LIKE 'WH%' and
i.[Item Number] NOT LIKE '%RMW'
I am not sure if you intend anything special with the parentheses around i.[Item Description]. The distinct keyword applies to the entire row.
If you want distinct only on the description, then you need to use group by. Something like:
SELECT i.[Item Description],
max(iq.[QTY Available]),
max(i.[Selling U Of M]), max(i.[Item Number]),
max(V.VNDRCDE), max(V.VNAME)
FROM ItemQuantities iq INNER JOIN
Items i
ON iq.[Item Number] = i.[Item Number] left outer join
Vendors v
on LEFT(v.[Vendor ID], 3) = RIGHT(i.[Item Number], 3)
WHERE i.[Item Number] LIKE 'WH%' and
i.[Item Number] NOT LIKE '%RMW'
group by i.[Item Description])
The max() will return the maximum value. If all are the same, then this is a good way to get an "arbitrary" value.
#sgeddes Thank you.
This is what I came up with
SELECT DISTINCT
Item.ITEMNMBR AS [Item Number],
Item.ITEMDESC AS [Item Description],
Item.ITMGEDSC AS [Item Category],
Item.SELNGUOM AS [Unit of Measure],
(SELECT VENDNAME FROM PM00200 WHERE (VENDORID = IV00103.VENDORID)) AS [Vendor Name],
(CASE WHEN Quan.QTYONHND > 0 THEN 'In Stock' ELSE 'Out of Stock' END) AS [Stock Status]
FROM IV00101 AS Item INNER JOIN
IV00102 AS Quan ON Item.ITEMNMBR = Quan.ITEMNMBR INNER JOIN
IV00103 ON Item.ITEMNMBR = IV00103.ITEMNMBR AND Quan.ITEMNMBR = IV00103.ITEMNMBR
WHERE (Item.ITEMNMBR LIKE 'WH%') AND (IV00103.VENDORID NOT LIKE '%MIL')