SQL-MS Access - Expression not included on multiple left join query - sql

I recently started the process of learning SQL and have hit my first wall. I have three tables in our database- Chart of Accounts (ChartAccts), Modified Transaction Detail (ModTD), and Beginning Trial Balance (TB_Beg). I am trying to create a query that shows all accounts and their names in the Chart of Accounts, the Beginning Balance for each account from the Trial Balance, and the amount column from the Modified Transaction Detail. They are all linked via the account numbers within each of the tables.
I am currently getting this "Your query does not include the specified expression 'Account_Num' as part of an aggregate function." when attempting to run this code:
SELECT A.Account_Num, A.Account_Name, NZ(Sum(B.[Amount ]),0) AS [Sum Of Amount], C.Amount
FROM ((ChartAccts AS A)
LEFT JOIN ModTD AS B ON A.[Account_Num] = B.[Account (Line): Number ])
LEFT JOIN TB_Beg AS C ON A.[Account_Num] = C.[Account #];
I feel like my problem must have something to do with the ON statements but I have been starting at this for so long that I don't think I am going to identify the issue despite how simple it must be. Any/all advice is appreciated!

You are missing the "GROUP BY" clause. When you use an SQL aggregate function (e.g., Max(), Min(), Sum(), ...) you must include all the fields that are not inside the expression of the argument of an SQL aggregate function in the GROUP BY clause. Aditionally, you cannot use the same field inside and outside the aggregate function: you cannot aggregate and not aggregate at the same time.
What I think you want is:
SELECT A.Account_Num, A.Account_Name, NZ(Sum(B.[Amount ]),0) AS [Sum Of Amount]
FROM
( ChartAccts AS A
LEFT JOIN
ModTD AS B
ON A.[Account_Num] = B.[Account (Line): Number ])
LEFT JOIN
TB_Beg AS C
ON A.[Account_Num] = C.[Account #]
GROUP BY A.Account_Num, A.Account_Name ;
You may want to check the Query "F_Select_w_group_by_aggreg" and related "F_Select_*" queries from the database of examples that you can download from LightningGuide.net.
Finally, be careful with the Nz() function, because when invoked from SQL it always returns a string. This can create problems if you use this fragment of code as part of a larger Query. You may want to enclose the Nz() function in a type-conversion function, or use an Iif() function instead.

Related

SQL sum, multiple Group By's and Date criteria

I'm looking to perform a sum calculation on a SQL table to find the quantity of a particular stock item held against a particular salesperson up to and including a specific date.
I'm able to perform the sum function to find the quantities on a Salesperson/item basis whenever I do not factor in the date range criteria, but as soon as i add that aspect, it all goes a bit pear shaped! Here is my code so far:
SELECT Salesperson, Item No, Sum(Quantity) AS 'Quantity'
FROM dbo
WHERE (Location Code='VAN')
GROUP BY Salesperson, Item No,
HAVING (Registering Date<={ts '2017-05-03 00:00:00'})
The location code = VAN filter is required to ensure it ignores Warehouse quantities.My SQL knowledge is limited to the few instances I run into it at work and my interaction is largely based through Microsoft Query in Excel. When looking at the above code, i figured that the 'Registering date' criteria should be in the 'WHERE' section, however when i add the criteria using the options available in Microsoft Query, it creates the 'HAVING' line.
If anyone could provide any pointers, it would be much appreciated!
Cheers
Peter
I would imagine a query like this:
SELECT Salesperson, [Item No], Sum(Quantity) AS Quantity
--------------------^ escape the non-standard column name
FROM dbo.??
---------^ table name goes here
WHERE Location Code = 'VAN' AND
[Registering Date] <= '2017-05-03'
------^ put the filtering condition in the correct clause
GROUP BY Salesperson, Item No
-----------------------------^ remove the comma
Your code, as written, has multiple errors. I am guessing that most are transcription errors rather than in the original query (queries don't run if no table is given in the FROM for instance). The "major" error would then be filtering in the HAVING clause rather than the WHERE clause.

Oracle SQL - Comparing AVG functions in WHERE

I'm trying to write a few Oracle SQL scripts for an assignment. I've managed to get all of it to work, except for one part. To summarize, I have to display data from 2 tables if the average of 1 column in table A is greater than the average of another column in table B. I realize you cannot include AVG functions in a WHERE clause or HAVING clause since it seems unable to properly access the data (from what I've read). When I exclude this clause, the script executes properly, so I'm confident there are no other errors.
I've tried writing it as follows but the error I get is ORA-00936: missing expression and it is just before the > sign. I thought this may be due to improper bracket placing but none of my attempts resolved this. Here is my attempt:
SELECT l.l_category, SUM(r.r_sold), AVG(l.l_cost)
FROM promos l
INNER JOIN sales r
ON r.promo_id = l.promo_id
GROUP BY l.l_category
HAVING (SELECT AVG(l.l_cost) OVER (PARTITION BY l.l_cost)) >
(SELECT AVG(r.r_sold) OVER (PARTITION BY r.r_sold));
I tried doing this without the OVER (PARTITION BY ...) as well as putting it into a WHERE clause but it didn't resolve the error. I'm pretty sure I need to put it into a SELECT statement somehow but I'm at a loss.
You do not need to use the OVER clause when applying the aggregate functions in the HAVING clause. Just use the aggregate functions on their own.
SELECT l.l_category, SUM(r.r_sold), AVG(l.l_cost)
FROM promos l
INNER JOIN sales r
ON r.promo_id = l.promo_id
GROUP BY l.l_category
HAVING HAVING AVG(l.l_cost) > AVG(r.r_sold)

Calculated field with Sum IFF in MS Access 2010: Query and Expression Builder

In MS Access 2010, I successfully wrote a query that gives me the following fields from two seperate tables: [Customer ID], [Product], [Price], [Total Price] and [Payment Method]
A customer could have order different product or use different payment method. Now, I am trying to have a calculated field that will give the total/sum of only the products that were paid online by each customer.
The [Payment Method] code for online is a "D". I used the code builder expression with the following expression:
1) Sum(IIf([Customer ID] = [Customer ID] AND [Payment Method] = "D", [Price], NULL))
However, it keeps on giving me this error message: You tried to execute a query that does not inclide the specified expression "Customer ID" as part of aggregate function.
If I want to do it in SQL (or expression builder) how would I do it? Everything I've tried so far leads me to the same error message.
Edit
My full query is:
SELECT CUSTOMER_INFO.ID AS [Customer ID],
CUSTOMER_INFO.PROD_KEY AS [Product],
CUSTOMER_INFO.PROD_PRICE AS [Price],
CUSTOMER_INFO.SUM_PRICE AS [Total Price],
PAYMENT_TRANZAK.PAY_METHD,
Sum(IIf([Customer ID]=[Customer ID] And [PAY_MTHD]="D",[Price],[IsNull])) AS [Online Total]
FROM CUSTOMER_INFO INNER JOIN PAYMENT_TRANZAK ON (CUSTOMER_INFO.PROD_KEY= PAYMENT_TRANZAK.SSBSECT_CRN) AND (CUSTOMER_INFO.TERM_CODE_KEY = PAYMENT_TRANZAK.DATE_CODE)
WHERE (
((CUSTOMER_INFO.SUM_PRICE)>0) AND ((PAYMENT_TRANZAK.PAY_METHD) Is Not Null) AND ((CUSTOMER_INFO.CUST_CODE)="RE" Or (CUSTOMER_INFO.CUST_CODE)="RW") AND ((CUSTOMER_INFO.DATE_CODE)=[Please enter a transaction date: ]) AND ((CUSTOMER_INFO.ESTS_CODE)="EL") AND ((CUSTOMER_INFO.STST_CODE)="AS")
)
ORDER BY CUSTOMER_INFO.ID;
You're trying to perform aggregation on non-aggregated data. In order to do a sum the function needs something over which to sum; a "group" of data. Hence you will need a Group By clause in there. Adding the clause GROUP BY CUSTOMER_INFO.ID will create a sum of the totals for each customer ID. You can add your payment type clause to the where statement, too, to get the proper payment type logic.
SELECT CUSTOMER_INFO.ID AS [Customer ID]
, Sum([Price]) AS [Online Total]
FROM CUSTOMER_INFO
INNER JOIN PAYMENT_TRANZAK
ON (CUSTOMER_INFO.PROD_KEY= PAYMENT_TRANZAK.SSBSECT_CRN)
AND (CUSTOMER_INFO.TERM_CODE_KEY = PAYMENT_TRANZAK.DATE_CODE)
WHERE (((CUSTOMER_INFO.SUM_PRICE)>0)
AND ((PAYMENT_TRANZAK.PAY_METHD) Is Not Null)
AND ((CUSTOMER_INFO.CUST_CODE)="RE" Or (CUSTOMER_INFO.CUST_CODE)="RW")
AND ((CUSTOMER_INFO.DATE_CODE)=[Please enter a transaction date: ])
AND ((CUSTOMER_INFO.ESTS_CODE)="EL")
AND ((CUSTOMER_INFO.STST_CODE)="AS"))
AND PAYMENT_TRANZAK.PAY_METHD="D"
GROUP BY CUSTOMER_INFO.ID
ORDER BY CUSTOMER_INFO.ID;
Because you are not aggregating all the fields of not all the fields are being grouped by it is not possible to express them in this kind of query. These
CUSTOMER_INFO.PROD_KEY AS [Product]
CUSTOMER_INFO.PROD_PRICE AS [Price]
CUSTOMER_INFO.SUM_PRICE AS [Total Price]
PAYMENT_TRANZAK.PAY_METHD
thus aren't a good match.
But you know your data better than me, maybe there's a way to fit them in logically. that's up to you.
Edit:
You could try a query like this where you don't do any filtering but you jsut do your grouping. This will present everything then you do the filtering on your report or form.
SELECT CUSTOMER_INFO.ID AS [Customer ID]
, CUSTOMER_INFO.CUST_CODE
, Sum(CUSTOMER_INFO.PROD_PRICE) AS [Online Total]
, Sum(CUSTOMER_INFO.SUM_PRICE) as [SumOfSumPrice]
, CUSTOMER_INFO.CUST_CODE
, PAYMENT_TRANZAK.PAY_METHD
, CUSTOMER_INFO.DATE_CODE
, CUSTOMER_INFO.ESTS_CODE
, CUSTOMER_INFO.STST_CODE
FROM CUSTOMER_INFO
INNER JOIN PAYMENT_TRANZAK
ON (CUSTOMER_INFO.PROD_KEY= PAYMENT_TRANZAK.SSBSECT_CRN)
AND (CUSTOMER_INFO.TERM_CODE_KEY = PAYMENT_TRANZAK.DATE_CODE)
GROUP BY CUSTOMER_INFO.ID
, CUSTOMER_INFO.CUST_CODE
, PAYMENT_TRANZAK.PAY_METHD
, CUSTOMER_INFO.DATE_CODE
, CUSTOMER_INFO.ESTS_CODE
, CUSTOMER_INFO.STST_CODE
ORDER BY CUSTOMER_INFO.ID;
In MS SQL SERVER, ORACLE, MS ACCESS you need to add all other fields in select clause into aggregate clause.
It may help to start with what Access Help has to say on the subject :
Jet SQL Help:All fields in the SELECT field list must either be included in the GROUP BY clause or be included as arguments to an SQL aggregate function.This quote from the Help system implies that all references to fields, even within compound references, must either be aggregated (IE. included in one of the aggregate functions listed above) or included in the GROUP BY clause. Any expression which is of either type is considered aggregated. The aggregate functions can only take field reference expressions which resolve to non-aggregated fields (IE. It is equally invalid to aggregate data more than once).
reference
Since aggregate function is anyway aggregating your query, you may try it without group by clause and see.
Or you could include each of those fields. Issue might be mainly in your case, you have two different fields within the IIF yet having nulls not handled. For an aggregate I would use a zero or make sure to have Isnull to sum IIF.
PS: I sent answer from mobile and it seems the full answer has not been published the first time.

MS Access SQL Query using Sum() and Count() gives incorrect results

I am having an issue with a query which returns results that are very far from reality (not only does it not make sense at all but I can also calculate the correct answer using filters).
I am building a KPI db for work and this query returns KPIs by employee by period. I have a very similar query from which this one is derived which returns KPIs by sector by period which gives the exact results I have calculated using a spreadsheet. I really have no idea what happens here. Basically, I want to sum a few measures that are in the maintenances table like temps_requete_min, temps_analyse_min, temps_maj_min and temps_rap_min and then create a subtotal AND present these measures as hours (measures are presented in minutes, thus the divide by 60).
SELECT
[anal].[prenom] & " " & [anal].[nom] AS Analyste,
maint.periode, maint.annee,
Round(Sum(maint.temps_requete_min)/60,2) AS REQ,
Round(Sum(maint.temps_analyse_min)/60,2) AS ANA,
Round(Sum(maint.temps_maj_min)/60,2) AS MAJ,
Round(Sum(maint.temps_rap_min)/60,2) AS RAP,
Round((Sum(maint.temps_requete_min)+Sum(maint.temps_analyse_min)+Sum(maint.temps_maj_min)+Sum(maint.temps_rap_min))/60,2) AS STOTAL,
Count(maint.periode) AS Nombre,
a.description
FROM
rapports AS rap,
analyste AS anal,
maintenances AS maint,
per_annuelle,
annees AS a
WHERE
(((rap.id_anal_maint)=anal.id_analyste) And
((maint.id_fichier)=rap.id_rapport) And
((maint.maint_effectuee)=True) And
((maint.annee)=per_annuelle.annee) And
((per_annuelle.annee)=a.annees))
GROUP BY
[anal].[prenom] & " " & [anal].[nom],
maint.periode,
maint.annee,
a.description,
anal.id_analyste
ORDER BY
maint.annee, maint.periode;
All measures are many orders of magnitude higher than what they should be. I suspect that my Count() is wrong, but I can't see what would be wrong with the sums :|
Edit: Finally I have come up with this query which shows the same measures I have calculated using Excel from the advice given in the comments and the answer provided. Many thanks to everyone. What I would like to know however, is why it makes a difference to use explicit joins rather than implicit joins (WHERE clause on PKs).
SELECT
maintenances.periode,
[analyste].[prenom] & " " & analyste.nom,
Round(Sum(maintenances.temps_requete_min)/60,2) AS REQ,
Round(Sum(maintenances.temps_analyse_min)/60,2) AS ANA,
Round(Sum(maintenances.temps_maj_min)/60,2) AS MAJ,
Round(Sum(maintenances.temps_rap_min)/60,2) AS RAP,
Round((Sum(maintenances.temps_requete_min)+Sum(maintenances.temps_analyse_min)+Sum(maintenances.temps_maj_min)+Sum(maintenances.temps_rap_min))/60,2) AS STOTAL,
Count(maintenances.periode) AS Nombre
FROM
(maintenances INNER JOIN rapports ON maintenances.id_fichier = rapports.id_rapport)
INNER JOIN analyste ON rapports.id_anal_maint = analyste.id_analyste
GROUP BY analyste.prenom, maintenances.periode
In this case, the problem is typically that your joins are bringing together multiple dimensions. You end up doing a cross product across two or more categories.
The fix is to do the summaries independently along each dimension. That means that the "from" clause contains subqueries with group bys, and these are then joined together. The group by would disappear from the outer query.
This would suggest having a subquery such as:
from (select maint.periode, maint.annee,
Round(Sum(maint.temps_requete_min)/60,2) AS REQ,
Round(Sum(maint.temps_analyse_min)/60,2) AS ANA,
Round(Sum(maint.temps_maj_min)/60,2) AS MAJ,
Round(Sum(maint.temps_rap_min)/60,2) AS RAP,
Round((Sum(maint.temps_requete_min)+Sum(maint.temps_analyse_min) +Sum(maint.temps_maj_min)+Sum(maint.temps_rap_min))/60,2) AS STOTAL,
Count(maint.periode) AS Nombre,
from maintenances maint
group by maint.periode, maint.annee
) m
I say "such as" because without a layout of the tables, it is difficult to see exactly where the problem is and what the exact solution is.

Use of the HAVING clause when using muliple sums

I was having a problem getting mulitple sums from multiple tables. Short story, my answer was solved in the "sql sum data from multiple tables" thread on this site. But where it came up short, is that now I'd like to only show sums that are greater than a certain amount. So while I have sub-selects in my select, I think I need to use a HAVING clause to filter the summed amounts that are too low.
Example, using the code specified in the link above (more specifically the answer that the owner has chosen as correct), I would only like to see a query result if SUM(AP2.Value) > 1500. Any thoughts?
If you need to filter on the results of ANY aggregate function, you MUST use a HAVING clause. WHERE is applied at the row level as the DB scans the tables for matching things. HAVING is applied basically immediately before the result set is sent out to the client. At the time WHERE operates, the aggregate function results are not (and cannot) be available, so you have to use a HAVING clause, which is applied after the main query is complete and all aggregate results are available.
So... long story short, yes, you'll need to do
SELECT ...
FROM ...
WHERE ...
HAVING (SUM_AP > 1500)
Note that you can use column aliases in the having clause. In technical terms, having on a query as above works basically exactly the same as wrapping the initial query in another query and applying another WHERE clause on the wrapper:
SELECT *
FROM (
SELECT ...
) AS child
WHERE (SUM_AP > 1500)
You could wrap that query as a subselect and then specify your criteria in the WHERE clause:
SELECT
PROJECT,
SUM_AP,
SUM_INV
FROM (
SELECT
AP1.[PROJECT],
(SELECT SUM(AP2.Value) FROM AP AS AP2 WHERE AP2.PROJECT = AP1.PROJECT) AS SUM_AP,
(SELECT SUM(INV2.Value) FROM INV AS INV2 WHERE INV2.PROJECT = AP1.PROJECT) AS SUM_INV
FROM AP AS AP1
INNER JOIN INV AS INV1 ON
AP1.[PROJECT] = INV1.[PROJECT]
WHERE
AP1.[PROJECT] = 'XXXXX'
GROUP BY
AP1.[PROJECT]
) SQ
WHERE
SQ.SUM_AP > 1500