SQL prestashop stats order total by customers group - sql

I try to export my order total by customers group in prestashop
This is my REQUEST for the group N° 3 for example:
SELECT ROUND(SUM(IFNULL(o.`total_paid_real`, 0 ) / cu.conversion_rate), 2) as totalMoneySpent
FROM `ps_orders` o
LEFT JOIN `ps_currency` cu ON o.id_currency = cu.id_currency
LEFT JOIN `ps_customer` c ON c.id_default_group= 3
WHERE o.valid = 1;
OTHER VERSION (SAME RESULT, the total of all group)
SELECT ROUND( SUM( IFNULL(o.`total_paid_real`, 0 ) / cu.conversion_rate), 2 ) as totalMoneySpent
FROM `ps_orders` o
LEFT JOIN `ps_currency` cu ON o.id_currency = cu.id_currency
#LEFT JOIN `ps_customer` c ON c.id_default_group=3
WHERE o.valid = 1
AND o.id_customer IN( SELECT c.id_customer FROM `ps_customer` c WHERE c.id_default_group=3 )
;
My problem is that c.id_default_group never change the results,
Thanks for your help

SELECT c.group_id, ROUND(SUM(IFNULL(o.`total_paid_real`, 0 ) / cu.conversion_rate), 2) as totalMoneySpent
FROM `ps_orders` o
LEFT JOIN `ps_currency` cu ON o.id_currency = cu.id_currency

Once you apply the aggregate function SUM you have to GROUP BY in order to get the aggregate result for a number of rows (otherwise the aggregate is applied individually for each row, defeating its own purpose).
SELECT c.group_id, ROUND(SUM(IFNULL(o.`total_paid_real`, 0 ) / cu.conversion_rate), 2) as totalMoneySpent
FROM `ps_orders` o
LEFT JOIN `ps_currency` cu ON o.id_currency = cu.id_currency
LEFT JOIN `ps_customer` c ON c.id_default_group= 3
WHERE o.valid = 1
GROUP BY c.group
ORDER BY c.group
assuming group_id identifies the customer group you refer to in the question.

Related

How to find duplicate rows from three tables using two columns

—oracle sql-
Select *
From Client cl, Contact c, Location l
Where l.locID = c.locID
and cl.clientID = l.clientID
I want to find more than one occurrences(duplicates) using combined columns of locID and clientID
We can use aggregation here:
SELECT cl.clientID, l.locID
FROM Client cl
INNER JOIN Location l ON l.clientID = cl.clientID
INNER JOIN Contact c ON c.locID = l.locID
GROUP BY cl.clientID, l.locID
HAVING COUNT(*) > 1;
Note that I also refactored your query to use explicit joins.
To see all columns, use this version:
WITH cte AS (
SELECT cl.clientID, l.locID, -- add more columns here
COUNT(*) OVER (PARTITION BY cl.clientID, l.locID) cnt
FROM Client cl
INNER JOIN Location l ON l.clientID = cl.clientID
INNER JOIN Contact c ON c.locID = l.locID
)
SELECT *
FROM cte
WHERE cnt > 1;
You can get all the details of the rows using the analytic COUNT function:
SELECT *
FROM (
SELECT cl.clientID,
cl.otherClientColumn,
c.locID,
c.otherContactColumn,
l.otherLocationColumn,
COUNT(*) OVER (PARTITION BY cl.clientID, c.locID) AS num_duplicates
From Client cl
INNER JOIN Contact c
ON (cl.clientID = l.clientID)
INNER JOIN Location l
ON (l.locID = c.locID)
)
WHERE num_duplicates > 1

Getting Latest 3 orders by Supplier ID

I have the following SQL Server code to get information from a combination of 4 tables.
I would like to modify it to only retrieve the latest 3 orders (pmpOrderDate) by supplier (pmpSupplierOrganizationID).
SELECT
PO.pmpPurchaseOrderID, PO.pmpOrderDate, PO.pmpSupplierOrganizationID, O.cmoName
FROM
PurchaseOrders PO
INNER JOIN
PurchaseOrderLines POL ON PO.pmpPurchaseOrderID = POL.pmlPurchaseOrderID
INNER JOIN
Organizations O ON PO.pmpSupplierOrganizationID = O.cmoOrganizationID
INNER JOIN
Parts P ON POL.pmlPartID = P.impPartID
WHERE
P.impPartClassID LIKE 'PUMP%'
Can you please help?
EDIT:
I wasn't fully clear on my actual requirements. To clarify further, what I need in the end is to display the latest 3 unique Purchase Orders by Supplier ID based on at least one of the PartClassID for the PartID in the PurchaseOrderLines to have criteria of beginning with string 'PUMP'
Use a ROW_NUMBER to partition by pmpSupplierOrganizationID and order by pmpOrderDate.
with cteTopOrders AS (
SELECT PO.pmpPurchaseOrderID, PO.pmpOrderDate, PO.pmpSupplierOrganizationID, O.cmoName,
ROW_NUMBER() OVER(PARTITION BY pmpSupplierOrganizationID ORDER BY pmpOrderDate DESC) AS RowNum
FROM PurchaseOrders PO
Inner Join PurchaseOrderLines POL ON PO.pmpPurchaseOrderID = POL.pmlPurchaseOrderID
Inner Join Organizations O On PO.pmpSupplierOrganizationID = O.cmoOrganizationID
Inner Join Parts P ON POL.pmlPartID = P.impPartID
WHERE P.impPartClassID Like 'PUMP%'
)
SELECT pmpPurchaseOrderID, pmpOrderDate, pmpSupplierOrganizationID, cmoName
FROM cteTopOrders
WHERE RowNum <= 3;
I'm a fan of lateral joins for this . . . cross apply:
select p.*, O.cmoName
from Organizations O cross apply
(select top (3) PO.pmpPurchaseOrderID, PO.pmpOrderDate, PO.pmpSupplierOrganizationID
from PurchaseOrders PO join
PurchaseOrderLines POL
on PO.pmpPurchaseOrderID = POL.pmlPurchaseOrderID join
Parts P
on POL.pmlPartID = P.impPartID
where PO.pmpSupplierOrganizationID = O.cmoOrganizationID and
P.impPartClassID Like 'PUMP%'
order by PO.pmpOrderDate desc
) p
You need a nested row_number to get the three rows per supplier and another OLAP-function on top of it:
with OrderRowNum as
(
SELECT PO.pmpPurchaseOrderID, PO.pmpOrderDate, PO.pmpSupplierOrganizationID, O.cmoName, P.impPartClassID,
row_number()
over (partition by PO.pmpSupplierOrganizationID
order by pmpOrderDate desc) as rn
FROM PurchaseOrders PO
Inner Join PurchaseOrderLines POL ON PO.pmpPurchaseOrderID = POL.pmlPurchaseOrderID
Inner Join Organizations O On PO.pmpSupplierOrganizationID = O.cmoOrganizationID
Inner Join Parts P ON POL.pmlPartID = P.impPartID
)
, CheckPUMP as
(
select *,
-- check if at least one of the three rows contains PUMP
max(case when impPartClassID Like 'PUMP%' then 1 else 0 end)
over (partition by PO.pmpSupplierOrganizationID) as PUMPflag
from OrderRowNum
where rn <= 3 -- get the last three rows per supplier
)
select *
from CheckPUMP
where flag = 1

SQL correct query or not

given these relationships, how could you query the following:
The tourists (name and email) that booked at least a pension whose rating is greater than 9, but didn't book any 3 star hotel with a rating less than 9.
Is the following correct?
SELECT Tourists.name, Tourists.email
FROM Tourists
WHERE EXISTS (
SELECT id FROM Bookings
INNER JOIN Tourists ON Bookings.touristId=Tourists.id
INNER JOIN AccomodationEstablishments ON Bookings.accEstId=AccomodationEstablishments.id
INNER JOIN AccomodationTypes ON AccomodationEstablishments.accType=AccomodationTypes.id
WHERE AccomodationTypes.name = 'Pension' AND
AccomodationEstablishments.rating > 9
) AND NOT EXISTS (
SELECT id FROM Bookings
INNER JOIN Tourists ON Bookings.touristId=Tourists.id
INNER JOIN AccomodationEstablishments ON Bookings.accEstId=AccomodationEstablishments.id
INNER JOIN AccomodationTypes ON AccomodationEstablishments.accType=AccomodationTypes.id
WHERE AccomodationTypes.name = 'Hotel' AND
AccomodationEstablishments.noOfStars = 3 AND
AccomodationEstablishments.rating < 9
)
I would do this using aggregation and having:
SELECT t.name, t.email
FROM Bookings b INNER JOIN
Tourists t
ON b.touristId = t.id INNER JOIN
AccomodationEstablishments ae
ON b.accEstId = ae.id INNER JOIN
AccomodationTypes a
ON ae.accType = a.id
GROUP BY t.name, t.email
HAVING SUM(CASE WHEN a.name = 'Pension' AND ae.rating > 9 THEN 1 ELSE 0 END) > 0 AND
SUM(a.name = 'Hotel' AND ae.noOfStars = 3 AND ae.rating < 9 THEN 1 ELSE 0 END)= 0;
Your method also works, but you probably need t.id in the subqueries.

Sum of resulting set of rows in SQL

I've got the following query:
SELECT DISTINCT CU.permit_id, CU.month, /*CU.year,*/ M.material_id, M.material_name, /*MC.chemical_id, C.chemical_name,
C.precursor_organic_compound, C.non_precursor_organic_compound,*/
/*MC.chemical_percentage,*/
POC_emissions =
CASE
WHEN (C.precursor_organic_compound = 'true')
THEN (CU.chemical_usage_lbs / CU.material_density) * M.VOC
ELSE 0
END,
NON_POC_emissions =
CASE
WHEN (C.non_precursor_organic_compound = 'true')
THEN CU.chemical_usage_lbs * (MC.chemical_percentage / 100)
ELSE 0
END
FROM material M
LEFT OUTER JOIN material_chemical MC ON MC.material_id = M.material_id
LEFT OUTER JOIN chemical_usage CU ON CU.material_id = MC.material_id
LEFT OUTER JOIN chemical C ON C.chemical_id = MC.chemical_id
WHERE (CU.month >=1 AND CU.month <= 2)
AND CU.year = 2013
AND M.material_id = 52
--AND CU.permit_id = 2118
--GROUP BY CU.permit_id, M.material_id, M.material_name, CU.month, MC.chemical_id, MC.chemical_id, C.chemical_name, C.precursor_organic_compound, C.non_precursor_organic_compound
--ORDER BY C.chemical_name ASC
Which returns:
But what I need is to return one row per month per material adding up the values of POC per month and NON_POC per month.
So, I should end up with something like:
Month material_id material_name POC NON_POC
1 52 Krylon... 0.107581 0.074108687
2 52 Krylon... 0.143437 0.0988125
I tried using SUM but it sums up the same result multiple times:
SELECT /*DISTINCT*/ CU.permit_id, CU.month, /*CU.year,*/ M.material_id, M.material_name, /*MC.chemical_id, C.chemical_name,
C.precursor_organic_compound, C.non_precursor_organic_compound,*/
--MC.chemical_percentage,
POC_emissions = SUM(
CASE
WHEN (C.precursor_organic_compound = 'true')
THEN (CU.chemical_usage_lbs / CU.material_density) * M.VOC
ELSE 0
END),
NON_POC_emissions = SUM(
CASE
WHEN (C.non_precursor_organic_compound = 'true')
THEN CU.chemical_usage_lbs * (MC.chemical_percentage / 100)
ELSE 0
END)
FROM material M
LEFT OUTER JOIN material_chemical MC ON MC.material_id = M.material_id
LEFT OUTER JOIN chemical_usage CU ON CU.material_id = MC.material_id
LEFT OUTER JOIN chemical C ON C.chemical_id = MC.chemical_id
WHERE M.material_id = 52
--AND CU.permit_id = 187
AND (CU.month >=1 AND CU.month <= 2)
AND CU.year = 2013
GROUP BY CU.permit_id, M.material_id, M.material_name, CU.month/*, CU.year, MC.chemical_id, C.chemical_name, C.precursor_organic_compound, C.non_precursor_organic_compound*/
--ORDER BY C.chemical_name ASC
The first query has a DISTINCT clause. What is the output without the DISTINCT clause. I suspect you have more rows than shows in your screenshot.
Regardless, you could try something like this to get the desired result.
select permit_id, month, material_id, material_name,
sum(poc_emissions), sum(non_poc_emissions)
from (
SELECT DISTINCT CU.permit_id, CU.month, M.material_id, M.material_name,
POC_emissions =
CASE
WHEN (C.precursor_organic_compound = 'true')
THEN (CU.chemical_usage_lbs / CU.material_density) * M.VOC
ELSE 0
END,
NON_POC_emissions =
CASE
WHEN (C.non_precursor_organic_compound = 'true')
THEN CU.chemical_usage_lbs * (MC.chemical_percentage / 100)
ELSE 0
END
FROM material M
LEFT OUTER JOIN material_chemical MC ON MC.material_id = M.material_id
LEFT OUTER JOIN chemical_usage CU ON CU.material_id = MC.material_id
LEFT OUTER JOIN chemical C ON C.chemical_id = MC.chemical_id
WHERE (CU.month >=1 AND CU.month <= 2)
AND CU.year = 2013
AND M.material_id = 52
) main
group by permit_id, month, material_id, material_name
Explanation
Since the results you retrieved by doing a DISTINCT was consider source-of-truth, I created an in-memory table by making it a sub-query. However, this subquery must have a name of some kind...whatever name. I gave it a name main. Subqueries look like this:
select ... from (sub-query) <give-it-a-table-name>
Simple Example:
select * from (select userid, username from user) user_temp
Advanced Example:
select * from (select userid, username from user) user_temp
inner join (select userid, sum(debits) as totaldebits from debittable) debit
on debit.userid = user_temp.userid
Notice how user_temp alias for the subquery can be used as if the sub-query was a real table.
Use above query in subquery and group by (month) and select sum(POC_emissions) and sum(NON_POC_emissions )

(ORDER BY CASE WHEN) ordering by subquery

I need to order my results by int column ascending, but I want to get only rows with numbers (0...10000) but default ordering gives me rows with null values for this column before numbers. I googled solution which set rows with null into the end of ordering (after all numbers) it looks like
SELECT ProductName
FROM Products
ORDER BY
CASE WHEN Position is null THEN 1 ELSE 0 END,
Position
So I my query looks like:
SELECT c.CompanyId, c.CompanyName, c.CompanyCategoryId, cc.CompanyCategoryName, c.HQCountryISO, c.CrunchBaseUrl,c.AngelListUrl,
(SELECT MAX(mf.NumLikes) FROM MeasurementFacebook mf
JOIN FacebookAccount f ON f.CompanyId = c.CompanyId
WHERE f.FacebookAccountId in (mf.FacebookAccountId)) as Likes,
(SELECT MAX(mt.NumFollowers) FROM MeasurementTwitter mt
JOIN TwitterAccount t ON t.CompanyId = c.CompanyId
WHERE t.TwitterAccountId in (mt.TwitterAccountId)) as Followers,
(SELECT MAX(ma.AlexaRanking) FROM MeasurementAlexa ma
JOIN Website w ON w.CompanyId = c.CompanyId
WHERE w.WebsiteId in (ma.WebsiteId)) as AlexaRank
FROM Company c
JOIN CompanyCategory cc ON c.CompanyCategoryId = cc.CompanyCategoryId
WHERE c.HQCountryISO = 'FRA'
ORDER BY CASE WHEN AlexaRank IS NULL THEN 1 ELSE 0 END, AlexaRank
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
As you can see, AlexaRank is the result of third subquery, and I want to order result by this column. But I have an error which says:
Msg 207, Level 16, State 1, Line 14
Invalid column name 'AlexaRank'.
What I'm doing wrong? Thanks
While you can use an alias in the ORDER BY clause, you can't use an alias in an expression, easiest solution is to plop it in a cte/subquery:
;WITH cte AS (SELECT c.CompanyId
, c.CompanyName
, c.CompanyCategoryId
, cc.CompanyCategoryName
, c.HQCountryISO
, c.CrunchBaseUrl
,c.AngelListUrl
,(SELECT MAX(mf.NumLikes)
FROM MeasurementFacebook mf
JOIN FacebookAccount f ON f.CompanyId = c.CompanyId
WHERE f.FacebookAccountId in (mf.FacebookAccountId)) as Likes
,(SELECT MAX(mt.NumFollowers)
FROM MeasurementTwitter mt
JOIN TwitterAccount t ON t.CompanyId = c.CompanyId
WHERE t.TwitterAccountId in (mt.TwitterAccountId)) as Followers
,(SELECT MAX(ma.AlexaRanking)
FROM MeasurementAlexa ma
JOIN Website w ON w.CompanyId = c.CompanyId
WHERE w.WebsiteId in (ma.WebsiteId)) as AlexaRank
FROM Company c
JOIN CompanyCategory cc ON c.CompanyCategoryId = cc.CompanyCategoryId
WHERE c.HQCountryISO = 'FRA')
SELECT *
FROM cte
ORDER BY CASE WHEN AlexaRank IS NULL THEN 1 ELSE 0 END, AlexaRank
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
Very inefficient code but you could do something like the following. Basically wrap your initial query in a common table expression so you don't need to rewrite your 3rd sub-select in your order by.
SELECT * FROM (
SELECT c.companyid,
c.companyname,
c.companycategoryid,
cc.companycategoryname,
c.hqcountryiso,
c.crunchbaseurl,
c.angellisturl,
(SELECT Max(mf.numlikes)
FROM measurementfacebook mf
JOIN facebookaccount f
ON f.companyid = c.companyid
WHERE f.facebookaccountid IN ( mf.facebookaccountid )) AS Likes,
(SELECT Max(mt.numfollowers)
FROM measurementtwitter mt
JOIN twitteraccount t
ON t.companyid = c.companyid
WHERE t.twitteraccountid IN ( mt.twitteraccountid )) AS Followers,
(SELECT Max(ma.alexaranking)
FROM measurementalexa ma
JOIN website w
ON w.companyid = c.companyid
WHERE w.websiteid IN ( ma.websiteid )) AS AlexaRank
FROM company c
JOIN companycategory cc
ON c.companycategoryid = cc.companycategoryid
WHERE c.hqcountryiso = 'FRA' ) Q
ORDER BY CASE
WHEN Q.AlexaRank IS NULL THEN 1
ELSE 0
END,
Q.AlexaRank