Optimizing Access Query with Multiple sub-queries - sql

I have a table of data that contains baseline project information, called ADMIN_WORK. I have a separate table that contains various attributes related to each project listed in ADMIN_WORK called WR_ATTRIBUTE. The structure of WR_ATTRIBUTE is such that each project has about 300 attributes (300 rows in the table) each with a unique name associated with the project number. I need to pull approximately 15 of these attributes into a final table.
I built a query with a number of sub-queries, but it takes multiple hours to run. The code below represents pulling a single attribute (called Circuit) from WR_ATTRIBUTE for all projects.
SELECT ADMIN_WORK.WR_NO AS [WR Number],
ADMIN_WORK.PREMISE_ID AS [Premise ID],
AttributeCircuit.Circuit
FROM ADMIN_WORK INNER JOIN
(SELECT ADMIN_WORK.WR_NO AS [WR Number], WR_ATTRIBUTE.ATTRIBUTE_VALUE AS Circuit
FROM ADMIN_WORK INNER JOIN
WR_ATTRIBUTE ON ADMIN_WORK.WR_NO = WR_ATTRIBUTE.WR_NO
WHERE (((WR_ATTRIBUTE.WR_ATTRIBUTE_CODE)="Circuit") AND ((ADMIN_WORK.WR_TYPE_CODE)="ACNEM"))
) AS AttributeCircuit
ON ADMIN_WORK.WR_NO = [AttributeCircuit].[WR Number]);
My final query has about 14 more of these subqueries similarly implemented, each with a different WR_ATTRIBUTE_CODE.
My final table is generated accurately, but it is extremely slow. I am wondering if there is a better way of structuring my query.

You probably just want conditional aggregation:
SELECT w.WR_NO AS [WR Number],
w.PREMISE_ID AS [Premise ID],
a.Circuit
FROM ADMIN_WORK as INNER JOIN
(SELECT a.WR_NO AS [WR Number],
MAX(IIF(a.WR_ATTRIBUTE_CODE)="Circuit" a.ATTRIBUTE_VALUE, NULL) AS Circuit ,
. . .
FROM WR_ATTRIBUTE as a
GROUP BY WR_NO
) as a
ON w.WR_NO = a.WR_NO ;

Related

SQL - Display columns from two tables with where clause, preform count and GROUP BY COLUMNS in one table

I have two tables. One table is the main registered Boat table that holds most of the information. it has many columns one of them is [MFR CODE].
The other table is a reference table that contains all the records associated with only Business Boats and it has only 3 columns : [MFR CODE], [MFR NAME] & [MODEL]
I'm trying to find how many times (count) Business Boats appear in the Registered boat tables.
SELECT
Count(*) As 'TotalNumberBoats'
, [BoatsReg].[dbo].[MASTER].[MFR MDL CODE]
,[BoatsReg].[dbo].[BusinessBoats].[MFR]
,[BoatsReg].[dbo].[BusinessBoats].[MODEL]
FROM [BoatsReg].[dbo].[MASTER],[BoatsReg].[dbo].[BusinessBoats]
Where [BoatsReg].[dbo].[MASTER].[MFR MDL CODE] = [BoatsReg].[dbo].[BusinessBoats].[CODE]
group by [BoatsReg].[dbo].[BusinessBoats].[CODE]
order by TotalNumberBoats asc
How do i get rid of all the square brackets, it's annoying.
why do i get an error ?
Well when you aggregate a result, you have to specify all other fixed columns in the GROUP BY clause
(Answer edited to add total row with a UNION ALL and add a sort field to put the total row as last one)
In your case:
SELECT 1 as sorted,
,Count(*) As TotalNumberBoats
,MASTER.[MFR MDL CODE]
,BusinessBoats.MFR
,BusinessBoats.MODEL
FROM BoatsReg, BusinessBoats
Where MASTER.[MFR MDL CODE] = BusinessBoats.CODE
Group by
,MASTER.[MFR MDL CODE]
,BusinessBoats.MFR
,BusinessBoats.MODEL
UNION ALL
SELECT 2 as sorted,
,Count(*) As TotalNumberBoats
,'','',''
FROM BoatsReg
Order by sorted, TotalNumberBoats

How to show elements from 2 tables ( existing in one , another or both)

I have 2 tables than i need to fuse together for data analysis.
Table One ( shows year consumption of items with values, from a contract)
Table One fields : product code, quantity, total value, contract number
Table Two (shows contract defined included products)
Table Two fields : included product code, included quantity, total included value, contract number
I need to join both of them so that shows per contract, all the related products, both consumed or included, so that shows either i only have consumed but not included, included but not consumed and included and consumed...
Something like this :
Contract|Product Code|Consumed qty|Included Qty|Consumed Total|Included Total
CTC001|X0001|55|45|550|450
CTC001|X0002|20|NULL|200|NULL
CTC001|X0003|NULL|10|NULL|100
CTC002|X0001|10|10|100|100
Using inner join only shows the ones on both tables
Using left or right joins shows all from one table and similar and null's from other table...
My goal was to show from both tables, has the example
Any help or tip ?
(this is my current query, field names not all equal as example, but you get the idea :
SELECT dbo.USR_View_ArtIncludContr.strCodArtigo, dbo.USR_View_TotaisConsumos.strCodArtigo AS Expr2, dbo.USR_View_TotaisConsumos.QTDTOTAL,
dbo.USR_View_ArtIncludContr.fltQuantLimiteInc, dbo.USR_View_TotaisConsumos.VALORTOTAL, dbo.USR_View_ArtIncludContr.Total, dbo.USR_View_TotaisConsumos.strCodSecContrato,
dbo.USR_View_TotaisConsumos.strCodTpContrato, dbo.USR_View_TotaisConsumos.strCodExercContrato, dbo.USR_View_TotaisConsumos.intNumeroContrato, dbo.USR_View_ArtIncludContr.strCodSeccao,
dbo.USR_View_ArtIncludContr.strCodTpContrato AS Expr1, dbo.USR_View_ArtIncludContr.strCodExercicio, dbo.USR_View_ArtIncludContr.intNumero
FROM dbo.USR_View_ArtIncludContr INNER JOIN
dbo.USR_View_TotaisConsumos ON dbo.USR_View_ArtIncludContr.strCodSeccao = dbo.USR_View_TotaisConsumos.strCodSecContrato AND
dbo.USR_View_ArtIncludContr.strCodTpContrato = dbo.USR_View_TotaisConsumos.strCodTpContrato AND
dbo.USR_View_ArtIncludContr.strCodExercicio = dbo.USR_View_TotaisConsumos.strCodExercContrato AND dbo.USR_View_ArtIncludContr.intNumero = dbo.USR_View_TotaisConsumos.intNumeroContrato AND
dbo.USR_View_ArtIncludContr.strCodArtigo = dbo.USR_View_TotaisConsumos.strCodArtigo
Sounds like you want a full join:
SELECT aic.strCodArtigo, tc.strCodArtigo AS Expr2, tc.QTDTOTAL,
aic.fltQuantLimiteInc, tc.VALORTOTAL, aic.Total, tc.strCodSecContrato,
tc.strCodTpContrato, tc.strCodExercContrato, tc.intNumeroContrato, aic.strCodSeccao,
aic.strCodTpContrato AS Expr1, aic.strCodExercicio, aic.intNumero
FROM dbo.USR_View_ArtIncludContr aic FULL JOIN
dbo.USR_View_TotaisConsumos tc
ON aic.strCodSeccao = tc.strCodSecContrato AND
aic.strCodTpContrato = tc.strCodTpContrato AND
aic.strCodExercicio = tc.strCodExercContrato AND
aic.intNumero = tc.intNumeroContrato AND
aic.strCodArtigo = tc.strCodArtigo
Notice that column aliases make the query much easier to write and to read.
Just figure out an workaround...
i could use CASE WHEN for the contract and for product code...
Something like this in select :
"CASE WHEN AIC.strCodArtigo IS NULL THEN TC.strCodArtigo WHEN TC.strCodArtigo IS NULL THEN AIC.strCodArtigo ELSE AIC.strCodArtigo END AS ARTIGO "
And use FULL OUTER JOIN
In case anyone has a better way, i appreciate any opinion

COUNT Clicks/Opens for Engagement Scoring

I am a bit rusty on SQL so any assistance is appreciated. I am also referencing my SQL textbook but I thought I would try this out.
I am developing a lead scoring model starting with engagement scoring. I created a data extension to house the results and used the following query to populate:
SELECT a.[opportunityid],
a.[first name],
a.[last name],
a.[anticipatedentryterm],
a.[funnelstage],
a.[programofinterest],
a.[opportunitystage],
a.[opportunitystatus],
a.[createdon],
a.[ownerfirstname],
a.[ownerlastname],
a.[f or j visa student],
a.[donotbulkemail],
a.[statecode],
Count(DISTINCT c.[subscriberkey]) AS 'Clicks',
Count(DISTINCT b.[subscriberkey]) AS 'Opens',
Count(DISTINCT b.[subscriberkey]) * 1.5 +
Count(DISTINCT c.[subscriberkey]) * 3 AS 'Probability'
FROM [ug_all_time_joined] a
INNER JOIN [open] b
ON a.[opportunityid] = b.[subscriberkey]
INNER JOIN [click] c
ON a.[opportunityid] = c.[subscriberkey]
GROUP BY a.[opportunityid],
a.[first name],
a.[last name],
a.[anticipatedentryterm],
a.[funnelstage],
a.[programofinterest],
a.[opportunitystage],
a.[opportunitystatus],
a.[createdon],
a.[ownerfirstname],
a.[ownerlastname],
a.[f or j visa student],
a.[donotbulkemail],
a.[statecode]
Something is wrong with my COUNT functions, the query populates the same value in both Clicks and Opens and I don't think it's accurate. The result I am aiming for is how many times a subscriber id appears (which would correspond with the individual clicks/opens, each row is a 1 action).
Thank you!
Why is that surprising?
You have two joins that if you take to their logical conclusion imply that
b.[SubscriberKey] = c.[SubscriberKey]
Hence, counting distinct values will be the same.
You have not provide sample data or desired results. I can speculate, though, that you intend LEFT JOINs so you get some values in one table that are not matched in the other.
When you do an inner join, between a and b, your data is filtered when you join a and c, which will give you incorrect results. having no view of your data and no background of your tables, this is the best guess i have

Include missing years in Group By query

I am fairly new in Access and SQL programming. I am trying to do the following:
Sum(SO_SalesOrderPaymentHistoryLineT.Amount) AS [Sum Of PaymentPerYear]
and group by year even when there is no amount in some of the years. I would like to have these years listed as well for a report with charts. I'm not certain if this is possible, but every bit of help is appreciated.
My code so far is as follows:
SELECT
Base_CustomerT.SalesRep,
SO_SalesOrderT.CustomerId,
Base_CustomerT.Customer,
SO_SalesOrderPaymentHistoryLineT.DatePaid,
Sum(SO_SalesOrderPaymentHistoryLineT.Amount) AS [Sum Of PaymentPerYear]
FROM
Base_CustomerT
INNER JOIN (
SO_SalesOrderPaymentHistoryLineT
INNER JOIN SO_SalesOrderT
ON SO_SalesOrderPaymentHistoryLineT.SalesOrderId = SO_SalesOrderT.SalesOrderId
) ON Base_CustomerT.CustomerId = SO_SalesOrderT.CustomerId
GROUP BY
Base_CustomerT.SalesRep,
SO_SalesOrderT.CustomerId,
Base_CustomerT.Customer,
SO_SalesOrderPaymentHistoryLineT.DatePaid,
SO_SalesOrderPaymentHistoryLineT.PaymentType,
Base_CustomerT.IsActive
HAVING
(((SO_SalesOrderPaymentHistoryLineT.PaymentType)=1)
AND ((Base_CustomerT.IsActive)=Yes))
ORDER BY
Base_CustomerT.SalesRep,
Base_CustomerT.Customer;
You need another table with all years listed -- you can create this on the fly or have one in the db... join from that. So if you had a table called alltheyears with a column called y that just listed the years then you could use code like this:
WITH minmax as
(
select min(year(SO_SalesOrderPaymentHistoryLineT.DatePaid) as minyear,
max(year(SO_SalesOrderPaymentHistoryLineT.DatePaid) as maxyear)
from SalesOrderPaymentHistoryLineT
), yearsused as
(
select y
from alltheyears, minmax
where alltheyears.y >= minyear and alltheyears.y <= maxyear
)
select *
from yearsused
join ( -- your query above goes here! -- ) T
ON year(T.SO_SalesOrderPaymentHistoryLineT.DatePaid) = yearsused.y
You need a data source that will provide the year numbers. You cannot manufacture them out of thin air. Supposing you had a table Interesting_year with a single column year, populated, say, with every distinct integer between 2000 and 2050, you could do something like this:
SELECT
base.SalesRep,
base.CustomerId,
base.Customer,
base.year,
Sum(NZ(data.Amount)) AS [Sum Of PaymentPerYear]
FROM
(SELECT * FROM Base_CustomerT INNER JOIN Year) AS base
LEFT JOIN
(SELECT * FROM
SO_SalesOrderT
INNER JOIN SO_SalesOrderPaymentHistoryLineT
ON (SO_SalesOrderPaymentHistoryLineT.SalesOrderId = SO_SalesOrderT.SalesOrderId)
) AS data
ON ((base.CustomerId = data.CustomerId)
AND (base.year = Year(data.DatePaid))),
WHERE
(data.PaymentType = 1)
AND (base.IsActive = Yes)
AND (base.year BETWEEN
(SELECT Min(year(DatePaid) FROM SO_SalesOrderPaymentHistoryLineT)
AND (SELECT Max(year(DatePaid) FROM SO_SalesOrderPaymentHistoryLineT))
GROUP BY
base.SalesRep,
base.CustomerId,
base.Customer,
base.year,
ORDER BY
base.SalesRep,
base.Customer;
Note the following:
The revised query first forms the Cartesian product of BaseCustomerT with Interesting_year in order to have base customer data associated with each year (this is sometimes called a CROSS JOIN, but it's the same thing as an INNER JOIN with no join predicate, which is what Access requires)
In order to have result rows for years with no payments, you must perform an outer join (in this case a LEFT JOIN). Where a (base customer, year) combination has no associated orders, the rest of the columns of the join result will be NULL.
I'm selecting the CustomerId from Base_CustomerT because you would sometimes get a NULL if you selected from SO_SalesOrderT as in the starting query
I'm using the Access Nz() function to convert NULL payment amounts to 0 (from rows corresponding to years with no payments)
I converted your HAVING clause to a WHERE clause. That's semantically equivalent in this particular case, and it will be more efficient because the WHERE filter is applied before groups are formed, and because it allows some columns to be omitted from the GROUP BY clause.
Following Hogan's example, I filter out data for years outside the overall range covered by your data. Alternatively, you could achieve the same effect without that filter condition and its subqueries by ensuring that table Intersting_year contains only the year numbers for which you want results.
Update: modified the query to a different, but logically equivalent "something like this" that I hope Access will like better. Aside from adding a bunch of parentheses, the main difference is making both the left and the right operand of the LEFT JOIN into a subquery. That's consistent with the consensus recommendation for resolving Access "ambiguous outer join" errors.
Thank you John for your help. I found a solution which works for me. It looks quiet different but I learned a lot out of it. If you are interested here is how it looks now.
SELECT DISTINCTROW
Base_Customer_RevenueYearQ.SalesRep,
Base_Customer_RevenueYearQ.CustomerId,
Base_Customer_RevenueYearQ.Customer,
Base_Customer_RevenueYearQ.RevenueYear,
CustomerPaymentPerYearQ.[Sum Of PaymentPerYear]
FROM
Base_Customer_RevenueYearQ
LEFT JOIN CustomerPaymentPerYearQ
ON (Base_Customer_RevenueYearQ.RevenueYear = CustomerPaymentPerYearQ.[RevenueYear])
AND (Base_Customer_RevenueYearQ.CustomerId = CustomerPaymentPerYearQ.CustomerId)
GROUP BY
Base_Customer_RevenueYearQ.SalesRep,
Base_Customer_RevenueYearQ.CustomerId,
Base_Customer_RevenueYearQ.Customer,
Base_Customer_RevenueYearQ.RevenueYear,
CustomerPaymentPerYearQ.[Sum Of PaymentPerYear]
;

Left Outer Joins from Two Tables using MS Access

I'm working on an existing Access database trying to implement some changes which will allow the estimation of cutting times for my employer (We're an industrial company who fabricate Flight Cases). This should function on only certain types of components; We've got a Stock table, which holds information on each Stock item including it's category. A Specification table which is used in order to build an estimate/quotation, and a table named [Spec Components] which holds the list of Stock Items which are attached to a Specification.
The tables can be joined as follows : Stock INNER JOIN [Spec Components] ON Stock.ID = [Spec Components].[Stock ID]
Specification INNER JOIN [Spec Components] ON Specification.SpecID = [Spec Components].[Spec ID]
My problem is that I only want to apply cutting times to an item in [Spec Components] if the item is listed as "Panels", "Extrusions", "Hybrids" etc (which is information that can be queried via Stock.Category) and different variables are used depending on the type of item we're quoting for, for example a Fabricated Lid Case's Panel may require 18 cuts but a different case may require 26. The case type is something which can be retrieved via Specification.CaseType, and determines which type of case we're quoting for.
Initially I tried to tackle this problem using the fast and dirty solution of nested conditional statements within the SQL Query however eventually got the error "Query too complex", as there is a limit on the number of nested ifs.
What I'm attempting now is to use a separate table which contains the list of the different cuts/setups etc
Category | CaseType | Setups | Cuts | PCID
--------------------------------------------
Panels | Lidmaker | 2 | 32 | 1
Panels | Fab Lid | 4 | 16 | 1
Extrusion | Lidmaker | 1 | 24 | 1
I then need to be able to access the contents of this table where applicable, but still be able to retrieve the values from my other tables for which the contents of the table are not applicable (Which, to me, identifies the need for a Left Outer Join on this table).
I can do this using design view in MS Access:
However when I run the query I get this message, but I don't really understand what it's telling me to do, or how on earth I should separate the queries, perhaps I'm being silly?
The query itself goes something like this:
SELECT [Spec Components].Qty, Specification.Height, Specification.Width, Specification.Depth, IIf(Cutting.Cuts>0 And Cutting.Setup>0,(Cutting.Cuts*Stock.CutTime)+(Cutting.Setup*Stock.SetupTime),0)
FROM ((Stock INNER JOIN [Spec Components] ON Stock.ID = [Spec Components].[Stock ID]) INNER JOIN Specification ON [Spec Components].[Spec ID] = Specification.SpecID) LEFT JOIN Cutting ON (Stock.Category = Cutting.Category) AND (Specification.[Case Type] = Cutting.CaseType)
ORDER BY [Spec Components].[Stock ID];
EDIT : possible duplicate of Ambiguous left joins in MS Access
About the error message, you can find more information following these links:
http://rogersaccessblog.blogspot.fr/2008/09/ambiguous-outer-joins.html
http://support.microsoft.com/kb/124937/en-us
Excerpt from 'Fixing Access Annoyances'
To solve the issue, "You must specify which method should be used by changing one of the joins or by separating the query into two queries." (quoted from the second link).
Below is a way of handling the issue using a subquery.
SELECT
Specified_Stock.Qty,
Specified_Stock.Height,
Specified_Stock.Width,
Specified_Stock.Depth,
IIf(
Cutting.Cuts > 0 And Cutting.Setup > 0,
(Cutting.Cuts * Specified_Stock.CutTime) + (Cutting.Setup * Specified_Stock.SetupTime),
0
)
FROM (
SELECT
[Spec Components].[Stock ID],
[Spec Components].Qty,
Specification.Height,
Specification.Width,
Specification.Depth,
Stock.CutTime,
Stock.SetupTime,
Stock.Category,
Specification.[Case Type]
FROM Stock
INNER JOIN [Spec Components] ON Stock.ID = [Spec Components].[Stock ID]
INNER JOIN Specification ON [Spec Components].[Spec ID] = Specification.SpecID
) as Specified_Stock
LEFT JOIN Cutting ON (Specified_Stock.Category = Cutting.Category) AND (Specified_Stock.[Case Type] = Cutting.CaseType)
ORDER BY Specified_Stock.[Stock ID];