I have this query: (apologies for complexity, I'm not certain what I can remove without impacting the question)
SELECT COUNT(*) AS total,
SUM(o.total) AS total_loss,
SUM((SELECT SUM(cost_price) FROM `orders_items` WHERE orders_id = o.orders_id)) AS cost_total ,
SUM((SELECT COUNT(*) FROM refunds AS r1 WHERE r1.order_id = r.order_id AND NOT r.reason IS NULL)) AS refund_count ,
SUM((SELECT COUNT(*) FROM exchanges AS e1 WHERE e1.order_id = e.order_id AND e.type = :countResend AND NOT e.reason IS NULL)) AS resend_count ,
SUM((SELECT COUNT(*) FROM exchanges AS e2 WHERE e2.order_id = e.order_id AND e.type = :countExchange AND NOT e.reason IS NULL)) AS exchange_count
FROM orders AS o
JOIN sales_channel_config AS s ON o.sales_channel = s.sales_channel AND o.sub_sales_channel = s.sub_sales_channel
JOIN courier_service AS cs ON o.courier_service = cs.code
LEFT JOIN refunds AS r ON o.orders_id = r.order_id
JOIN orders_items AS oi ON o.orders_id = oi.orders_id
JOIN third_party_config AS tc ON SUBSTRING(oi.product_id_new, 3, 2) = tc.code
LEFT JOIN exchanges AS e ON o.orders_id = e.order_id
WHERE 1 = 1
AND o.tracking_num NOT IN (:cancelStatus)
AND (o.order_date >= :startDate AND o.order_date <= :endDate)
AND o.courier_service = :courier
AND SUBSTRING(oi.product_id_new, 3, 2) = :supplier
AND (NOT r.reason IS NULL OR NOT e.reason IS NULL)
The problem I'm having is that the various SUM((query)) clauses are counting duplicate orders, which is proving difficult to resolve. For example:
SUM((SELECT COUNT(DISTINCT r1.order_id) FROM refunds AS r1 WHERE r1.order_id = r.order_id AND NOT r.reason IS NULL)) AS refund_count ,
And
SUM((SELECT COUNT(*) FROM refunds AS r1 WHERE r1.order_id = r.order_id AND NOT r.reason IS NULL GROUP BY r1.order_id)) AS refund_count ,
Do not lower the resulting SUM at all. I have confirmed that the data returned will contain duplicates via another structurally identical query that returns rows from the parent query. When the other query is run without duplicate filtering, the counts match correctly so I'm confident that my problem query is accurate aside from including duplicated order ids.
So can anyone suggest another approach I might try?
For anyone who might benefit:
I removed most of the select logic and grouped on orders_id, which gives me an entirely accurate list of relevant orders:
SELECT o.orders_id AS order_id, r.id AS refund_id, e.id AS exchange_id, e.type AS exchange_type
FROM orders AS o
JOIN sales_channel_config AS s ON o.sales_channel = s.sales_channel AND o.sub_sales_channel = s.sub_sales_channel
JOIN courier_service AS cs ON o.courier_service = cs.code
LEFT JOIN refunds AS r ON o.orders_id = r.order_id
JOIN orders_items AS oi ON o.orders_id = oi.orders_id
JOIN third_party_config AS tc ON SUBSTRING(oi.product_id_new, 3, 2) = tc.code
LEFT JOIN exchanges AS e ON o.orders_id = e.order_id
WHERE 1 = 1
AND o.tracking_num NOT IN (:cancelStatus)
AND (o.order_date >= :startDate
AND o.order_date <= :endDate)
AND o.courier_service = :courier
AND SUBSTRING(oi.product_id_new, 3, 2) = :supplier
AND (NOT r.reason IS NULL OR NOT e.reason IS NULL)
GROUP BY (o.orders_id)
I've bitten the bullet here. I'm going to do some post processing to get the counts themselves, which is at least possible for me now.
Still don't understand why getting distinct values in the sub selects failed though.
Related
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.
Using the table below
It can also be easily replaced with an OUTER JOIN whenever a need arises.
The WHERE syntax is more relational model oriented.
A result of two tables JOIN'ed is a cartesian product of the tables to which a filter is applied which selects only those rows with joining columns matching.
It's easier to see this with the WHERE syntax.
SELECT Toy_name, Quantity
FROM Toy T,Hire Transaction H, Store S
WHERE S.Store_ID = T.store_id
AND t.toy_id = H.toy_id
AND t.hire_price = (SELECT max(hire_price) from Toy)
AND UPPER(S.store_suburb = ‘SCARSDALE’)
AND H.hire_date >= ’01/02/2013’
AND H.hire_date <= ’31/03/2015’;
I was able to write this. But how can I solve this using a subquery as asked?
You could avoid the sub select, by ordering your result by descending price, and then limiting the output to just one record:
SELECT Toy_name,
Sum(H.Quantity) total_quantity
FROM Toy T
INNER JOIN `Hire Transaction` H
ON H.Toy_id = t.Toy_id
INNER JOIN Store S
ON S.Store_ID = T.store_id
WHERE upper(S.store_suburb) = 'SCARSDALE'
AND H.hire_date BETWEEN '2013-02-01' AND '2015-03-31'
GROUP BY T.Toy_id
ORDER BY T.Hire_price DESC
LIMIT 1
You can try this
SELECT t.toy_name, sum(ht.quantity) quantity, max(t.hire_price) max_p
FROM toy t
INNER JOIN hire_transaction ht on t.toy_id = ht.toy_id
INNER JOIN store s on t.store_id = s.store_id
WHERE ht.hire_date between '2013-02-01' and '2015-03-31' and upper(s.store_suburb) = 'SCARSDALE'
group by t.toy_id
having max(max_p)
select h.toy_id, sum(h.Quantity) as hire_count from HireTransaction as h where h.hire_date >= ’01/02/2013’
AND h.hire_date <= ’31/03/2015’ group by h.toy_id having h.toy_id = (select toy_id from Toy where hire_price = (select max(hire_price) from Toy) and store_id = (select store_id from Store where store_suburb = 'SCARSDALE')
I am trying to generate a report that will display data from 3 tables as follows:
SELECT B.datepoll, B.clientID, B.badTerm, A.FullQuery,
B.badMessage, C.clientNAME, E.CURRENT_TERMS
FROM BadTerms AS B INNER JOIN
QueryTERMS AS A ON B.badTerm = A.QUERIES INNER JOIN
Clients AS C ON B.clientID = C.clientID INNER JOIN
(
SELECT CLIENTID,COUNT(QUERIES) AS CURRENT_TERMS FROM QueryTERMS WHERE
clientID = 'XXXXXX' GROUP BY CLIENTID) E ON
E.CLIENTID = A.CLIENTID
WHERE (CONVERT(VARCHAR(10), B.datepoll, 120) < CONVERT(VARCHAR(10), GETDATE(), 120))
AND (B.clientID = 'XXXXXX')
Basically what should be displayed is for a given datepoll, display all badTerms, fullQuery, badMessage, clientID, clientName and total terms. The clientID is common among all 3 tables and queries is common among BadTerms and QueryTerms.
I have everything fine except for the total count. It should only appear once and be the total queries in the QueryTerms table not the BadTerms table.
Any help appreciated.
Actually got it to work now:
SELECT B.datepoll, B.clientID, B.badTerm, A.FullQuery,
B.badMessage, C.clientNAME, E.CURRENT_TERMS
FROM BadTerms AS B INNER JOIN
QueryTERMS AS A ON B.badTerm = A.QUERIES INNER JOIN
Clients AS C ON B.clientID = C.clientID INNER JOIN
(
SELECT CLIENTID,COUNT(QUERIES) AS CURRENT_TERMS FROM QueryTERMS WHERE
clientID = 'XXXXXX' GROUP BY CLIENTID) E ON
E.CLIENTID = A.CLIENTID
WHERE (CONVERT(VARCHAR(10), B.datepoll, 120) < CONVERT(VARCHAR(10), GETDATE(), 120))
AND (B.clientID = 'XXXXXX')
Hi I'm having an issue with a query that was once working. My SQL skills aren't all that great, not sure what I'm missing. Or if this is the correct approach. Maybe use a temp table instead?
The basic gist is given a certain time frame, I need to calculate the highest aggregate of points over 5 classes.
trialScores - keeps scores/points,
trials, dog, people and member tables are just meta data
classId 5 requires a different date range
Here is my Query on MySQL
select
t.id,
d.id,
p.id,
p.firstname,
p.lastname,
d.id dogId,
d.dogName,
c.id,
c.class,
t.trialStartDate,
s.points,
if(c.id = 5, '2012-08-01', '2012-11-18') as startDate,
if(c.id = 5, '2013-07-31', '2013-12-31') as endDate,
SUM(ts.points) AS pointsAggregate
from trialScores ts
inner join trials t on t.id = ts.trialId
inner join dogs d on d.id = ts.dogId
inner join people p on p.id = ts.personId
inner join classes c on c.id = ts.classId
where t.deletedAt is null
and ts.deletedAt is null
and ts.memberAtTrial = 1
and d.omitFromTripleCrownDOY = 0
and t.associationId = 1
GROUP BY p.id, ts.dogId, ts.classId
having t.trialStartDate between startDate and endDate
order by ts.classId, pointsAggregate desc
Looks like this fixed it, too much in the select and not in Group by?:
select
d.dogName,
c.class,
p.firstName,
p.lastName,
SUM(ts.points) AS pointsAggregate
from trialScores ts
inner join trials t on t.id = ts.trialId
inner join dogs d on d.id = ts.dogId
inner join people p on p.id = ts.personId
inner join classes c on c.id = ts.classId
where t.deletedAt is null
and ts.deletedAt is null
and ts.memberAtTrial = 1
and d.omitFromTripleCrownDOY = 0
and t.associationId = 1
and t.trialStartDate between if(c.id = 5, '2012-08-01', '2012-11-18') and if(c.id = 5, '2013-07-31', '2013-12-31')
GROUP BY ts.dogId, ts.classId
order by ts.classId, pointsAggregate desc
Can you try below query and let if it's work or not ?
select
d.dogName,
c.class,
p.firstName,
p.lastName,
SUM(ts.points) AS pointsAggregate
from trialScores ts
inner join trials t on t.id = ts.trialId
inner join dogs d on d.id = ts.dogId
inner join people p on p.id = ts.personId
inner join classes c on c.id = ts.classId
where t.deletedAt is null
and ts.deletedAt is null
and ts.memberAtTrial = 1
and d.omitFromTripleCrownDOY = 0
and t.associationId = 1
and t.trialStartDate between if(c.id = 5, '2012-08-01', '2012-11-18') and if(c.id = 5, '2013-07-31', '2013-12-31')
GROUP BY ts.classId,p.firstName,p.lastName
order by ts.classId, pointsAggregate desc
I have to write a query where, I need to fetch records for last week, last month, and for all.
For this problem I wrote 3 diffrent queries (for last week, for last month and for all)
For Weekly Info :-
SELECT bu.brand_name AS 'Brand_Name',COUNT(s.unique) AS '# Item Sold',SUM(s.price) AS 'Total_Price'
FROM item_details s
LEFT JOIN sales_order o ON s.fk_sales_order = o.id_sales_order
LEFT JOIN customer_info AS c ON o.fk_customer_id = c.id_customer
LEFT JOIN simple_details cc ON s.unique = cc.unique
LEFT JOIN config_details cf ON cc.fk_config_id = cf.config_id
LEFT JOIN brand_details cb ON cf.fk_brand_id = cb.brand_id
LEFT JOIN category_details ctc ON cf.fk_category_id = ctc.category_id
LEFT JOIN gender_details g ON cf.fk_gender_id = g.gender_id
LEFT JOIN buyers AS bu ON bu.brand_name = cb.name AND bu.category_name = ctc.name AND bu.gender = g.name
WHERE bu.buyers = 'xyz' AND DATE_FORMAT(o.created_date,'%Y-%m-%d') >= #weekstartdate AND DATE_FORMAT(o.created_date,'%Y-%m-%d') <= #weekenddate
GROUP BY bu.brand_name
For Monthly Info :-
SELECT bu.brand_name AS 'Brand_Name',COUNT(s.unique) AS '# Item Sold',SUM(s.price) AS 'Total_Price'
FROM item_details s
LEFT JOIN sales_order o ON s.fk_sales_order = o.id_sales_order
LEFT JOIN customer_info AS c ON o.fk_customer_id = c.id_customer
LEFT JOIN simple_details cc ON s.unique = cc.unique
LEFT JOIN config_details cf ON cc.fk_config_id = cf.config_id
LEFT JOIN brand_details cb ON cf.fk_brand_id = cb.brand_id
LEFT JOIN category_details ctc ON cf.fk_category_id = ctc.category_id
LEFT JOIN gender_details g ON cf.fk_gender_id = g.gender_id
LEFT JOIN buyers AS bu ON bu.brand_name = cb.name AND bu.category_name = ctc.name AND bu.gender = g.name
WHERE bu.buyers = 'xyz' AND DATE_FORMAT(o.created_date,'%Y-%m-%d') >= #monthstartdate AND DATE_FORMAT(o.created_date,'%Y-%m-%d') <= #monthenddate
GROUP BY bu.brand_name
For All Records :-
SELECT bu.brand_name AS 'Brand_Name',COUNT(s.unique) AS '# Item Sold',SUM(s.price) AS 'Total_Price'
FROM item_details s
LEFT JOIN sales_order o ON s.fk_sales_order = o.id_sales_order
LEFT JOIN customer_info AS c ON o.fk_customer_id = c.id_customer
LEFT JOIN simple_details cc ON s.unique = cc.unique
LEFT JOIN config_details cf ON cc.fk_config_id = cf.config_id
LEFT JOIN brand_details cb ON cf.fk_brand_id = cb.brand_id
LEFT JOIN category_details ctc ON cf.fk_category_id = ctc.category_id
LEFT JOIN gender_details g ON cf.fk_gender_id = g.gender_id
LEFT JOIN buyers AS bu ON bu.brand_name = cb.name AND bu.category_name = ctc.name AND bu.gender = g.name
WHERE bu.buyers = 'xyz'
GROUP BY bu.brand_name
and these are working fine (giving currect output).
But problem is that, I have to merge these three queries in single one.
Where output should be as
Brand name, item_sold(week), total_price(week),item_sold(month), total_price(month),item_sold(all), total_price(all)
How can I write this query?
Without looking deep into your code, the obvious solution would be
SELECT
all.brand_name
pw.items_sold items_sold_week
pw.total_price total_price_week
pm.items_sold items_sold_month
pm.total_price total_price_month
all.items_sold items_sold_all
all.total_price total_price_all
FROM
(your all-time select) all
JOIN (your per-month select) pm ON all.brand_name = pm.brand_name
JOIN (your per-week select) pw ON all.brand_name = pw.brand_name
Though you probably should rethink your entire approach and make sure whether you really want that kind of logic in a DB layer or it is better to be in your application.
You could use case to limit aggregates to a subset of rows:
select bu.brand_name
, count(case when date_format(o.created_date,'%Y-%m-%d') >= #weekstartdate
and date_format(o.created_date,'%Y-%m-%d') <= #weekenddate
then 1 end) as '# Item Sold Week'
, sum(case when date_format(o.created_date,'%Y-%m-%d') >= #weekstartdate
and date_format(o.created_date,'%Y-%m-%d') <= #weekenddate
then s.price end) as 'Total_Price Week'
, count(case when date_format(o.created_date,'%Y-%m-%d') >= #monthstartdate
and date_format(o.created_date,'%Y-%m-%d') <= #monthstartdate
then 1 end) as '# Item Sold Month'
, ...
If all three selects uses the same fields in the results, you can UNION them:
SELECT *
FROM (SELECT 1) AS a
UNION (SELECT 2) AS b
UNION (SELECT 3) AS c
If you need to tell week/mon/all records from each other - just add constant field containing "week" or "mon"
You cam use the UNION.keyword between the queries to bundle them.together BUT tje column types and sequence must be the same in all queries. You could add an identifier to each set