How to optimize this nested SQL query - mysql

Here is the database schema:
[redacted]
I'll describe what I'm doing with the query below:
Innermost query: Select all the saleIds satisfying the WHERE conditions
Middle query: Select all the productIds that were a part of the saleId
Outermost query: SUM the products.cost and select the vendors.name.
And here is the SQL query I came up with:
SELECT vendors.name AS Company
, SUM(products.cost) AS Revenue
FROM
products
INNER JOIN sold_products
ON (products.productId = sold_products.productId)
INNER JOIN vendors
ON (products.vendorId = vendors.vendorId)
WHERE sold_products.productId IN (
SELECT sold_products.productId
FROM
sold_products
WHERE sold_products.saleId IN (
SELECT sales.saleId
FROM
markets
INNER JOIN vendors
ON (markets.vendorId = vendors.vendorId)
INNER JOIN sales_campaign
ON (sales_campaign.marketId = markets.marketId)
INNER JOIN packet_headers
ON (sales_campaign.packetHeaderId = packet_headers.packetHeaderId)
INNER JOIN packet_details
ON (packet_details.packetHeaderId = packet_headers.packetHeaderId)
INNER JOIN sales
ON (sales.packetDetailsId = packet_details.packetDetailsId)
WHERE vendors.customerId=60
)
)
GROUP BY Company
ORDER BY Revenue DESC;
Any help in optimizing this?

Since you are just using inner joins you normally simplify the query to smth like this:
SELECT ve.name AS Company
, SUM(pr.cost) AS Revenue
FROM products pr
, sold_products sp
, vendors ve
, markets ma
, sales_campaign sc
, packet_headers ph
, packet_details pd
, sales sa
Where pr.productId = sp.productId
And pr.vendorId = ve.vendorId
And ve.vendorId = ma.vendorId
And sc.marketId = ma.marketId
And sc.packetHeaderId = ph.packetHeaderId
And pd.packetHeaderId = ph.packetHeaderId)
And sa.packetDetailsId = pd.packetDetailsId
And ve.customerId = 60
GROUP BY ve.Company
ORDER BY pr.Revenue DESC;
Please try if this works and if it is faster and let me know.

Related

Is there a method of counting an attribute that is in a GROUP BY clause?

I need have created a select statement to list out all the customers that have been to multiple merchants below.
I want to create another statement to display how many of those customers have been to each merchant.
What is the optimal method of approaching this problem?
Lists out all customers that have been to multiple merchants.
WITH valentinesDayMerchant AS (
SELECT m.MerchantId, m.MerchantGroupId, m.WebsiteName
FROM Merchant m
INNER JOIN OpeningHours oh ON m.MerchantId = oh.MerchantId AND oh.DayOfWeek = 'TUE'
LEFT JOIN devices.DeviceConnectionState AS dcs ON dcs.MerchantId = oh.MerchantId
WHERE MerchantStatus = '-' AND (m.PrinterType IN ('V','O') OR dcs.State = 1 OR dcs.StateTransitionDateTime > '2023-01-23')
)
SELECT DISTINCT ul.UserLoginId, ul.FullName, ul.EmailAddress, ul.Mobile
FROM dbo.UserLogin AS ul
INNER JOIN dbo.Patron AS p ON p.UserLoginId = ul.UserLoginId
INNER JOIN valentinesDayMerchant AS m ON (m.MerchantId = ul.ReferringMerchantId OR m.MerchantId IN (SELECT pml.MerchantId FROM dbo.PatronMerchantLink AS pml WHERE pml.PatronId = p.PatronId AND ISNULL(pml.IsBanned, 0) = 0))
LEFT JOIN (
SELECT mg.MerchantGroupId, mg.MerchantGroupName, groupHost.HostName [GroupHostName]
FROM dbo.MerchantGroup AS mg
INNER JOIN dbo.Merchant AS parent ON parent.MerchantId = mg.ParentMerchantId
INNER JOIN dbo.HttpHostName AS groupHost ON groupHost.MerchantID = parent.MerchantId AND groupHost.Priority = 0
) mGroup ON mGroup.MerchantGroupId = m.MerchantGroupId
LEFT JOIN (
SELECT po.PatronId, MAX(po.OrderDateTime) [LastOrder]
FROM dbo.PatronsOrder AS po
GROUP BY po.PatronId
) orders ON orders.PatronId = p.PatronId
INNER JOIN dbo.HttpHostName AS hhn ON hhn.MerchantID = m.MerchantId AND hhn.Priority = 1
WHERE ul.UserLoginId NOT IN (1,2,100,372) AND ul.UserStatus <> 'D' AND (
ISNULL(orders.LastOrder, '2000-01-01') > '2020-01-01' OR ul.RegistrationDate > '2022-01-01'
)
GROUP BY ul.UserLoginId, ul.FullName, ul.EmailAddress, ul.Mobile
HAVING COUNT(m.MerchantId) > 1
Methods I have tried include adding the merchant name to a group by and displaying the count of the customers, however this does not work as I cannot have anything related to the Merchant in the GROUP BY, or I wouldn't be able to use HAVING clause to identify the customers that have been to multiple merchants. I have also tried selecting all the merchants and counting the distinct customers which doesn't work as it takes into account all the customers, not specifically the customers that have been to multiple merchants only.

How to run two where clauses inside a Join?

Please check the below code.
SELECT
`order`.idorder
, order_status_code.idorder_status_code
, order_status_code.order_status_code
, user.iduser
, `order`.required_delivery_date
, `order`.cancel
, `order`.date_created
, `order`.last_updated
, COUNT(order_item.idorder_item)
from
`order`
INNER JOIN order_status_code
ON `order`.idorder_status_code = order_status_code.idorder_status_code
INNER JOIN user
ON `order`.iduser = user.iduser
INNER JOIN order_item
ON order_item.idorder = `order`.`idorder`
WHERE
`order`.iduser = 1
In here, I want the COUNT(order_item.idorder_item) to return the number of items under the idorder. In other words, if I run that SQL Part along, that would be like below
SELECT
COUNT(`idorder_item`)
from
order_item
where
idorder = 1
How can I get this done in my main query?
SELECT `order`.idorder,
order_status_code.idorder_status_code,
order_status_code.order_status_code,
user.iduser,
`order`.required_delivery_date,
`order`.cancel,
`order`.date_created,
`order`.last_updated,
COUNT(order_item.idorder_item),
(SELECT COUNT(`idorder_item`)
from order_item
where idorder=1) as count_idorder_item
from `order`
INNER JOIN order_status_code ON `order`.idorder_status_code = order_status_code.idorder_status_code
INNER JOIN user ON `order`.iduser = user.iduser
INNER JOIN order_item ON order_item.idorder = `order`.`idorder`
WHERE `order`.iduser= 1

SQL Query - Count inside GROUP BY no result

I want to count the patient diagnosis per municipality and consultation per municipality:
so it should be:
diagnosis per municipality + consultation per municipality
SELECT COUNT(consultations.id) +
(SELECT COUNT(patientdiagnosis.id)
FROM consultations
LEFT JOIN patientdiagnosis
ON patientdiagnosis.consultation_id = consultations.id
LEFT JOIN patients
ON consultations.patient_id = patients.id
LEFT JOIN rcitymun
ON patients.municipality = rcitymun.citycode
/*GROUP BY PER MUNICIPALITY SHOULD BE HERE*/
) as encounters, rcitymun.cityname
FROM consultations
LEFT JOIN patients
ON consultations.patient_id = patients.id
LEFT JOIN rcitymun
ON patients.municipality = rcitymun.citycode
GROUP BY patients.municipality;
current output:
encounters municipality
10323 BATAC
10423 NUEVA ERA
the encounter data is huge because it's counting all of the diagnosis instead of per municipality
what i want is to count the diagnosis per municipality.
desired output is something like this:
encounters municipality
105 BATAC
70 NUEVA ERA
It may be possible to reduce this by one subquery, but often it is best to start with independently grouped subqueries.
SELECT
rcitymun.cityname
, SUM(c.consult_count) consult_count
, SUM(d.diag_count) diag_count
FROM patients
INNER JOIN rcitymun ON patients.municipality = rcitymun.citycode
LEFT JOIN (
SELECT
consultations.patient_id
, COUNT(*) consult_count
FROM consultations
GROUP BY
consultations.patient_id
) c ON patients.id = c.patient_id
LEFT JOIN (
SELECT
consultations.patient_id
, COUNT(*) diag_count
FROM consultations
INNER JOIN patientdiagnosis ON patientdiagnosis.consultation_id = consultations.id
GROUP BY
consultations.patient_id
) d ON patients.id = d.patient_id
GROUP BY
rcitymun.cityname

How to deduct in group by query in sum in access

I have query like this ::
SELECT account.AccountNumber, account.NAME, Sum(agro.price * agro.qty) AS Expr1
FROM ((account
INNER JOIN data ON account.AccountNumber = data.acno)
INNER JOIN agro ON agro.BillNo = data.BillNo)
WHERE data.db='true'
GROUP BY account.AccountNumber, account.NAME;
I want to deduct another groupby query output in to Sum(agro.price * agro.qty) this
the another group by query is SELECT Sum(rs),acno
FROM jma group by acno;
i want to deduct Sum(agro.price * agro.qty)-Sum(rs) how its work please help me solve this
If I am understanding you correctly the following query may work for you:
SELECT subQ.AccountNumber, subQ.NAME, (subQ.subSum - jmaSum.jSum) AS FinalSum
FROM
(
SELECT a.AccountNumber, a.NAME, Sum(ag.price * ag.qty) AS subSum
FROM (account AS a
INNER JOIN data AS d ON a.AccountNumber = d.acno)
INNER JOIN agro AS ag ON ag.BillNo = d.BillNo
WHERE d.db = 'true'
GROUP BY a.AccountNumber, a.NAME
) AS subQ
LEFT JOIN
(
SELECT Sum(j.rs) AS jSum, j.acno
FROM jma AS j
GROUP BY j.acno
) AS jmaSum ON subQ.AccountNumber = jmaSum.acno

Getting the latest date from a id

I run the above sql statement and i got this.[IMG]http://i1093.photobucket.com/albums/i422/walkgirl_1993/asd-1_zps5506632e.jpg[/IMG] i'm trying display the latest date which you can see the 3 and 4. For caseid 3, it should display the latest row which is the 2012-12-20 16:12:36.000. I tried using group by, order by. Google some website said to use rank but i'm not sure about the rank as i dont really get rank. Some suggestions?
select [Case].CaseID, Agent.AgentName, Assignment.Description, A.AgentName as EditedBy, A.DateEdited from Agent inner join [Case-Agent] on [Case-Agent].AgentID = Agent.AgentID inner join [Assignment] on Assignment.AssignmentID = [Case-Agent].AssignmentID inner join [Case] on [Case].CaseID = [Case-Agent].CaseID inner join (select EditedCase.CaseID, [EditedCase].DateEdited, [Agent].AgentName from EditedCase inner join [Agent] on [Agent].AgentID = [EditedCase].AgentID) A on A.CaseID = [Case].CaseID where [Assignment].AssignmentID = 0
To do it using RANK you just need to add the RANK to the subquery and get to rank the DateEdited for each CaseID and Agent and then in the main query put a WHERE clause to only select rows where the rank is 1. I think I have got the partition clause right - its a bit hard without seeing your data.
Like this:
SELECT
[Case].CaseID
,Agent.AgentName
,Assignment.Description
,A.AgentName AS EditedBy
,A.DateEdited
FROM Agent
INNER JOIN [Case-Agent] ON [Case-Agent].AgentID = Agent.AgentID
INNER JOIN [Assignment] ON Assignment.AssignmentID = [Case-Agent].AssignmentID
INNER JOIN [Case] ON [Case].CaseID = [Case-Agent].CaseID
INNER JOIN (SELECT
EditedCase.CaseID
,[EditedCase].DateEdited
,[Agent].AgentName
,RANK ( ) OVER (PARTITION BY EditedCase.CaseID, [Agent].AgentName
ORDER BY [EditedCase].DateEdited DESC ) AS pos
FROM EditedCase
INNER JOIN [Agent] on [Agent].AgentID = [EditedCase].AgentID) A on A.CaseID = [Case].CaseID
WHERE [Assignment].AssignmentID = 0
AND pos = 1
You could also change the sub query into an aggregate query that brings back the MAX date like this:
SELECT
[Case].CaseID
,Agent.AgentName
,Assignment.Description
,A.AgentName AS EditedBy
,A.DateEdited
FROM Agent
INNER JOIN [Case-Agent] ON [Case-Agent].AgentID = Agent.AgentID
INNER JOIN [Assignment] ON Assignment.AssignmentID = [Case-Agent].AssignmentID
INNER JOIN [Case] ON [Case].CaseID = [Case-Agent].CaseID
INNER JOIN (SELECT
EditedCase.CaseID
,MAX([EditedCase].DateEdited) AS DateEdited
,[Agent].AgentName
FROM EditedCase
INNER JOIN [Agent] on [Agent].AgentID = [EditedCase].AgentID
GROUP BY
EditedCase.CaseID
,[Agent].AgentName) A on A.CaseID = [Case].CaseID
WHERE [Assignment].AssignmentID = 0
AND pos = 1
You were on the right track; you need to use a ranking function here, for example row_number():
with LatestCase as
(
select [Case].CaseID
, Agent.AgentName
, Assignment.Description
, A.AgentName as EditedBy
, A.DateEdited
, caseRank = row_number() over (partition by [Case].CaseID order by A.DateEdited desc)
from Agent
inner join [Case-Agent] on [Case-Agent].AgentID = Agent.AgentID
inner join [Assignment] on Assignment.AssignmentID = [Case-Agent].AssignmentID
inner join [Case] on [Case].CaseID = [Case-Agent].CaseID
inner join
(
select EditedCase.CaseID
, [EditedCase].DateEdited
, [Agent].AgentName
from EditedCase
inner join [Agent] on [Agent].AgentID = [EditedCase].AgentID
) A on A.CaseID = [Case].CaseID where [Assignment].AssignmentID = 0
)
select *
from LatestCase
where caseRank = 1