getting duplicate records in Access query - sql

I have three tables: The [inventory snapshot] table shows the name of the product and how many we have on site; the [inbound loads] table shows how many are coming in; the [outbound routes] table lists how many are going out.
I was getting unique values when I only had the first and second table (showing on-hand and 'arriving') but when I added in the third, I began getting multiple records instead of summed records.
Purpose of query
I work in a warehouse and I'm trying to isolate pick slots that are 1) low on inventory; 2)have no more product coming in; 3) I'd like to know if there are open orders to ship out any of the remaining product.
SELECT DISTINCT [inventory snapshot].locn_brcd,
[inventory snapshot].description,
[inventory snapshot].item_name,
Sum([inventory snapshot].on_hand_qty) AS SumOfON_HAND_QTY,
Sum([outbound routes].quantity) AS SumOfQuantity
FROM [outbound routes]
RIGHT JOIN ([inbound loads]
RIGHT JOIN [inventory snapshot]
ON [inbound loads].[wrin number] =
[inventory snapshot].item_name)
ON [outbound routes].[wrin number] =
[inventory snapshot].item_name
GROUP BY [inventory snapshot].locn_brcd,
[inventory snapshot].description,
[inventory snapshot].item_name,
[inbound loads].[quantity to receive]
HAVING ( ( ( Sum([inventory snapshot].on_hand_qty) ) < 10 )
AND ( ( [inbound loads].[quantity to receive] ) IS NULL ) );

Since you resolved your issue, consider further adjustment of grouping columns. Right now, the GROUP BY clause contains one more field than SELECT clause, namely: [inbound loads].[quantity to receive].
Typically, in aggregate SQL queries like this one, numeric values like quantity are not grouped but run as aggregated columns. You may have added it to GROUP BY in order to use it in HAVING. However, non-aggregates can be handled in WHERE to filter before aggregation. (Oddly, MS Access tends to move such level filtering in HAVING using Query Design.)
Consider below refactored SQL which uses table alias to avoid repetition of long table names. Of course, remove your needless location grouping.
SELECT s.locn_brcd
, s.description
, s.item_name
, SUM(s.on_hand_qty) AS SumOfON_HAND_QTY
, SUM(r.quantity) AS SumOfQuantity
FROM [outbound routes] r
RIGHT JOIN ([inbound loads] l
RIGHT JOIN [inventory snapshot] s
ON l.[wrin number] = s.item_name)
ON r.[wrin number] = s.item_name
WHERE l.[quantity to receive] IS NULL
GROUP BY s.locn_brcd
, s.description
, s.item_name
HAVING SUM(s.on_hand_qty) < 10 ;
By the way, the SQL industry tends to use LEFT JOIN rather than RIGHT JOIN for readability which you may not have control as this query may be output from Query Design. Try equivalent version without nesting JOINs where parentheses is required in Access SQL:
FROM ([inventory snapshot] s
LEFT JOIN ([inbound loads] l
ON l.[wrin number] = s.item_name)
LEFT JOIN [outbound routes] r
ON r.[wrin number] = s.item_name

Related

Selecting the most frequent value in a column based on the value of another column in the same row?

So basically what I'm trying to do is generate a report for our stores. We have an incident report website where the employees can report an incident that takes place at any of our stores. So in the general report I'm trying to generate, I want to show the details for each store we have (Five stores). This would include the name of the store, number of incidents, oldest incident date, newest incident date, and then the most recurring type of incident at each store.
SELECT Store.Name AS [Store Name], COUNT(*) AS [No. Of Incidents], Min(CAST(DateNotified AS date)) AS [Oldest Incident], Max(CAST(DateNotified AS date)) AS [Latest Incident],
( SELECT TOP 1 IncidentType.Details
FROM IncidentDetails
INNER JOIN Store ON IncidentDetails.StoreID = Store.StoreID
INNER JOIN IncidentType On IncidentDetails.IncidentTypeID = IncidentType.IncidentTypeID
Group By IncidentType.Details, IncidentDetails.StoreID
Order By COUNT(IncidentType.Details) DESC) AS [Most Freqeuent Incident]
FROM IncidentDetails
INNER JOIN Store ON IncidentDetails.StoreID = Store.StoreID
INNER JOIN IncidentType On IncidentDetails.IncidentTypeID = IncidentType.IncidentTypeID
GROUP BY Store.Name
Just to make it clear, the IncidentDetails table stores all the details about the incident including which store it occured at, what the type of incident was, time/date, etc.
What this does though is it gives me 5 rows for each store, but the [Most Frequent Incident] value is the same for every row. Basically, it gets the most frequent incident value for the whole table, regardless of which store it came from, and then displays that for each store, even though different stores have different values for the column.
I've been trying to solve this for a while now but haven't been able to :-(
You have too many joins and no correlation clause.
There are several ways to approach this problem. You have already started with an aggregation in the outer query and then a nested subquery. So, this continues that approach. I think this does what you want:
SELECT s.Name AS [Store Name], COUNT(*) AS [No. Of Incidents],
Min(CAST(DateNotified AS date)) AS [Oldest Incident],
Max(CAST(DateNotified AS date)) AS [Latest Incident],
(SELECT TOP 1 it.Details
FROM IncidentDetails id2 INNER JOIN
IncidentType it2
On id2.IncidentTypeID = it2.IncidentTypeID
WHERE id2.StoreId = s.StoreId
Group By it.Details
Order By COUNT(*) DESC
) AS [Most Freqeuent Incident]
FROM IncidentDetails id INNER JOIN
Store s
ON id.StoreID = s.StoreID
GROUP BY s.Name, s.StoreId;
Notes:
Removed the IncidentType table from the outer joins. This doesn't seem needed (although it could be used for filtering).
Added s.StoredId to the group by clause. This is needed for the correlation in the subquery.
Added a where clause so the subquery is only processed once for each store in the outer query.
Removed the join to Store in the subquery. It seems unnecessary, if the queries can be correlated on StoreId.
Changed the group by in the subquery to use Details. That is the value being selected.
Added table aliases, which make queries easier to write and to read.

Combining multiple queries in Access so that the data is displayed in different fields within a single table

I have two queries which I want to join together. Unionoption will not work as I want the values to be displayed in different fields. The 1st query is
SELECT Outage.DISTRICT, Count(Outage.DISTRICT) AS CountOfDISTRICT
FROM Outage LEFT JOIN [Site ID gets generated] ON Outage.[Site ID]=[Site ID gets generated].[Site ID]
GROUP BY Outage.DISTRICT;
The 2nd query is
SELECT Outage.DISTRICT, Count(Outage.[Site ID]) AS [CountOfSite ID]
FROM Outage
GROUP BY Outage.DISTRICT;
I tried the below code but it is giving syntax error (missing operator) in the 1st query
SELECT Outage.DISTRICT,
(SELECT Count(Outage.[Site ID]) AS [CountOfSite ID] FROM Outage),
(SELECT Count(Outage.DISTRICT) AS CountOfDISTRICT FROM Outage LEFT JOIN [Site ID gets generated] ON Outage.[Site ID]=[Site ID gets generated].[Site ID])
GROUP BY Outage.DISTRICT;
It would be very helpful if answer is also given with an explanation, as I am quite new to access and trying to learn it from scratch.
You can join the two queries together to get their columns side-by-side:
select
sq1.DISTRICT
, sq1.CountOfDISTRICT
, sq2.[CountOfSite ID]
from (
... your first query here ...
) as sq1
inner join (
... your second query here ...
) as sq2
on sq1.DISTRICT = sq2.DISTRICT
The thing to remember is that unions are for joining tables/queries vertically, while joins are for joining them horizontally.

Issue when I join three tables

I have three tables TeamTerritoryMapping, UpdQuotaTbl and tblInvoiceFile
When I join the first two tables I am getting the correct result
select
TTM.Team, Sum(Upd.Quota) as Quota
from
TeamTerritoryMapping TTM
inner join
UpdQuotaTbl upd on upd.ITM = TTm.Territory
where
upd.Month = 'july'
group by
TTM.Team
but when I join the third table for another column revenue from tblinvoicefile, some of the rows are getting duplicated and the end result is becoming higher. Below is the query which I am using to join 3 tables
select
TTM.Team,
Sum(upd.Quota) as quota,
sum(inv.[End MS Sales Revenue]) as Revenue
from
UpdQuotaTbl Upd
inner join
TeamTerritoryMapping TTM on TTM.Territory = upd.ITM
inner join
tblInvoiceFile inv on inv.[Inv Territory] = TTM.Territory
where
upd.Month = 'july'
and inv.[End Fiscal Month] = 'July, 2013'
So how can I eradicate the duplicate values in third table, I am getting the correct value when I join two tables ie TeamTerritoryMapping,UpdQuotaTbl and also tables TeamTerritoryMapping.
It looks like tblInvoiceFile has multiple entires for [Invenio Territory] which is causing this issue. If your intention is to bring in and sum all the [End MS Sales Revenue] for that table you can try something like this
SELECT TTM.Team,Sum(upd.Quota) as quota,sum(inv.[End MS Sales Revenue]) as Revenue
FROM UpdQuotaTbl Upd
INNER JOIN TeamTerritoryMapping TTM ON TTM.Territory = upd.ITM
INNER JOIN
(SELECT [Invenio Territory], SUM([End MS Sales Revenue]) AS [End MS Sales Revenue]
FROM tblInvoiceFile
WHERE [End Fiscal Month] = 'July, 2013'
GROUP BY [Invenio Territory]) AS inv
ON inv.[Invenio Territory] = TTM.Territory
WHERE upd.Month = 'july'
My initial response would be : The reason you're getting some results multiple times is because the ON used to join tblInvoiceFile is not 'unique' enough. If you look at the primary key (or a unique?) of said table you'll probably notice that it involves more than just the Inv Territory field.
To solve it you can do two different things.
Expand the ON clause to include more fields that make the connection unique
Keep the query as it is now but aggregate the results so the invoice information gets SUMmed
The problem is, it seems like you're already SUM()-ing the information (although as noted by Win, you're missing the GROUP BY), so I'm not quite sure why this would not work.
UPDATE: Only now realise that your problem is not doubled records, but doubled values in the first SUM(). Best way to solve this is by PRE-aggregating the values as shown by Abhi.

One Query, Three Tables, Show all rows in left table?

I have three tables I need to pull data for, the left table needs to always display the rows that are called out in the "where". My problem is that since there are three tables I can't get this to work. I have tried joins like crazy, it works when only doing two tables, but as soon as I get the third in there it doesn't work, it then will only display rows that are not null across all three tables. I need to be able to display the specified IDs in the first table regardless if the other two tables are completely null for the specified IDs
I have a database where I only have read access, so changing the structure on this won't work. Below is my SQL code that works as it is to get me the data I want with the exception that there is a ID that doesn't display since it has no data for the day specified.
select
[User].Id,
[User].Name,
convert(float,round(sum(SalesOrderJournalDetail.Price*SalesOrderJournalDetail.Shipped),2)) as 'Sales Yesterday',
convert(float,round(sum(SalesOrderJournalDetail.ActualCost*SalesOrderJournalDetail.Shipped),2)) as 'Cost Yesterday',
count(distinct(SalesOrderJournalDetail.SalesOrderId)) as 'Orders Yesterday',
count(SalesOrderJournalDetail.SalesOrderId) as 'Lines Yesterday',
convert(float,UserTotal.SalesMTD,2) as 'Sales MTD',
round(convert(float,UserTotal.CostMTD,2),2) as 'Cost MTD'
from
[User], UserTotal, SalesOrderJournalDetail
where
[User].Id in (' 725',' 150',' 239',' 225',' 209',' 227',' 222',' 232',' 241',' 215',' 214',' 722',' 134',' 201',' 238',' 721','M104',' 244',' 245',' 104')
and convert(varchar(10),SalesOrderJournalDetail.InvoiceDate,111) = '2012/04/19'
and [User].Id=SalesOrderJournalDetail.SalesPersonUserId
and SalesOrderJournalDetail.SalesPersonUserId=UserTotal.UserId
group by [User].Id, [User].Name, SalesOrderJournalDetail.SalesPersonUserId, UserTotal.UserId, UserTotal.SalesMTD, UserTotal.CostMTD
order by [User].Name
To help explain the structure, the [User] table displays the user's ID number and Name, the SalesOrderJournalDetail table displays the current day's sales, and the UserTotal table displays the monthly sales data (via some calculations).
I need to display the day's sales and monthly sales from the latter two tables, and need to pull the user's name and ID from the first.
All three table's unique data are [User].Id, SalesJournalDetail.SalesPersonUserId, and UserTotal.UserId
Any ideas how I can get this to show all the ID's shown in the where cluase regardless if there are no day or monthly sales for that user?
Oh and this is using Microsoft SQL
Created tables with your structure. Very minor column name changes are there. Please change accordingly. ActualCost is ActualPrice in my table.
Following will be the query
SELECT
ISNULL(SalesJournalUserId,TotalSalesUserId) AS UserId,
ISNULL(SalesJournalUserName,TotalSalesUserName) AS UserName,
[Orders Yesterday],
[Lines Yesterday],
[Sales Yesterday],
[Cost Yesterday],
[Sales MTD],
[Cost MTD] FROM
(
(
SELECT
SalesUser.Id as SalesJournalUserId,
SalesUser.Name as SalesJournalUserName,
[Orders Yesterday],
[Lines Yesterday],
[Sales Yesterday],
[Cost Yesterday]
FROM
[SalesUser]
LEFT JOIN
(
select
[User].Id,
[User].Name,
Count(distinct(SalesOrderJournalDetail.ID)) as 'Orders Yesterday',
Count((SalesOrderJournalDetail.ID)) as 'Lines Yesterday',
convert(float,round(sum(SalesOrderJournalDetail.Price*SalesOrderJournalDetail.Shipped),2)) as 'Sales Yesterday',
convert(float,round(sum(SalesOrderJournalDetail.ActualPrice*SalesOrderJournalDetail.Shipped),2)) as 'Cost Yesterday'
from
[SalesUser] as [User]
LEFT JOIN SalesOrderJournalDetail
on [User].Id = SalesOrderJournalDetail.SalesPersonUserId
where
--[User].Id =1 and
convert(varchar(10),SalesOrderJournalDetail.InvoiceDate,111) = '2012/04/19'
group by [User].Id,[user].Name
) SOJD
ON SalesUser.Id = SOJD.ID
) SOJDALLUSERS
INNER JOIN
(
SELECT
SalesUser.Id as TotalSalesUserId,
SalesUser.Name as TotalSalesUserName,
[Sales MTD],
[Cost MTD]
FROM
[SalesUser]
LEFT JOIN
(
select
[User].Id,
[User].Name,
convert(float,sum(UserTotal.SalesMTD),2) as 'Sales MTD',
round(convert(float,sum(UserTotal.CostMTD),2),2) as 'Cost MTD'
from
[SalesUser] as [User]
LEFT JOIN UserTotal
on [User].Id = UserTotal.UserId
--where
--[User].Id =1
group by [User].Id,[User].name
) AS SOUT
ON SalesUser.Id = SOUT.ID
) SOUTALLUSERS
ON SOJDALLUsers.SalesJournalUserId = SOUTALLUSERS.TotalSalesUserId
)
How it works
Subquery SOJDALLUSERS: Joins SalesUser table with your SalesOrderJournalTable. Fetches data for every user
Subquery SOUT : Joins SalesUser table with UserTotal table. fetches data for every user. Your SalesMTD and CostMTD should be a "SUM" for every user if i am not wrong. You have missed it.
The above two results are inner joined to get the details for every user. Since the above subquery brings data for every user in sales table, an inner join for the above two subqueries will guarantee data for all users with appropriate null values whenever data is missing.
I have created the tables and checked following scenarios
1. data in both journal and salestotal table for a user
2. data in journal table but not in salestotal table for a user
3. data in salestotal table but not in journal table for a user.
IT WORKS.. ENJOY
Table schema for your reference
Mark as answer if it gives you some direction into your problem. It was a nice challenge doing this query..

Using Access for Sales Variance Reporting - Query issues

So I want to create a variance report. Simply sales - planned sales = variance etc.
I have two table, one called Actual one called Plan. Both have the same fields,
-account code
-month
-year
-sales
The idea being I populate the Plan table with the years plan data then update Actuals with actuals as they come in.
The problem I have, is how do I build a query that shows both (1) unplanned sales and (2) planned sales without an actual? I can't find a join type that does it.
Or am I completely missing the point and a more obvious way of doing this?!
I am not sure if I understand the problem. If sometimes records are missing in Actual and sometimes in Plan then combine a LEFT JOIN query with a RIGHT JOIN query in a UNION query.
SELECT
P.*,
A.sales - P.sales AS variance
FROM
Plan P
LEFT JOIN Actual A
ON P.[account code] = A.[account code] AND
P.month = A.month AND
P.year = A.year
UNION ALL
SELECT
A.*,
NULL AS variance
FROM
Plan P
RIGHT JOIN Actual A
ON P.[account code] = A.[account code] AND
P.month = A.month AND
P.year = A.year
WHERE
P.[account code] IS NULL
The WHERE clause avoids duplicates and selects only records that were missing in the first SELECT query.