compare aggregates with row data - mysql

I have a table in MySql related to purchase of Inventory.
The table format is as below :
itemname (varchar)
vendor (varchar)
pdate (datetime)
qty (decimal)
rate (decimal)
total (decimal)
id (mediumint) (autoincrement)
A report is to be generated that must have the following columns
Item HighestRate HighestRateDate LowestRate LowestRateDate % difference
--
Item is the itemname
HighestRate and HighestRateDate are rates at maximum
LowestRate and Date are rates at minimum
%difference is a basic difference percentage between highestrate and lowestrate of a row
I have prepared the following query
SELECT itemname,rate,pdate from purchases
group by itemname
having rate = max(rate)
order by itemname
which does generate one half of the report.
However since it requires both the lowest and highest rate. This report is incomplete and printing two reports makes comparison difficult.
Any help would be appreciated.
Thankyou

Here goes. It's not pretty but it works.
select min.itemname,
min.rate,
min.pdate,
max.rate,
max.pdate,
(max.rate - min.rate)/max.rate as diff
from (SELECT p.itemname,
p.rate,
pdate
from purchases p,
(select itemname,
max(rate) as rate
from purchases
group by itemname) max_values
where p.itemname = max_values.itemname
and p.rate = max_values.rate
) max,
(SELECT p.itemname,
p.rate,
pdate
from purchases p,
(select itemname,
min(rate) as rate
from purchases
group by itemname) min_values
where p.itemname = min_values.itemname
and p.rate = min_values.rate
) min
where max.itemname = min.itemname;

You'll need an index on (itemname, rate) for this query not to be awfully slow.
If there are two or more dates with same maximum (or minimum) rate, then a (more or less) random date will be selected. If you want to control that, change the ORDER BY rate ASC (to ORDER BY rate ASC, pdate DESC for example):
SELECT
di.itemname AS Item
, maxr.rate AS HighestRate
, maxr.pdate AS HighestRateDate
, minr.rate AS LowestRate
, minr.pdate AS LowestRateDate
, (maxr.rate - minr.rate)/maxr.rate --- calculations
AS PrecentDifference
FROM
( SELECT DISTINCT itemname
FROM purchases
) AS di
JOIN
purchases AS minr
ON minr.id = --- I guess this is the PK
( SELECT id
FROM purchases p
WHERE p.itemname = di.itemname
ORDER BY rate ASC
LIMIT 1
)
JOIN
purchases AS maxr
ON maxr.id =
( SELECT id
FROM purchases p
WHERE p.itemname = di.itemname
ORDER BY rate DESC
LIMIT 1
)

Related

I Need help in writing a multi-level ms access subquery

I am having hard time to wrap my mind around a multi-level query/sub-query. In short, I a table called REGISTER with 3 fields in it: TransactionId (PK), TransDate (DateTime), Amount (Currency). I need a single query to retrieve the TransactionId where TransDate is the Max and the Amount is the Min qualifying records. For example, if the max of qualifying record returns 5 records with today's date (any future dates is excluded), I would like to know the TransactionId of the lowest Amount within the 5 records returned.
I amble to accomplish this task with two separate query but I am sure it cab be done with a single one.
Query 1 (qryFlag):
SELECT REGISTER.TransDate, REGISTER.*
FROM REGISTER
WHERE (((REGISTER.TransDate)=(
SELECT Max(t2.Transdate) from REGISTER t2
where Transdate <= Date())));
Query 2:
SELECT REGISTER.TransactionId
FROM qryFlag INNER JOIN REGISTER ON qryFlag.TransactionId = REGISTER.TransactionId
WHERE (((qryFlag.Amount)=(SELECT Min(t2.Amount) from qryFlag t2)));
Try:
SELECT TOP 1 VTID.TransactionID
FROM (
SELECT TransactionID, Amount
FROM Register
WHERE TransDate = (SELECT Max(R.TransDate) FROM Register as R WHERE R.TransDate <= Date())
) as VTID
ORDER BY VTID.Amount
HTH
Dale
You need to GROUP BY TransDate to get the min amount and then join to the table:
SELECT r.*
FROM REGISTER AS r INNER JOIN (
SELECT TransDate, MIN(Amount) AS MinAmount
FROM REGISTER
WHERE TransDate = (SELECT Max(Transdate) FROM REGISTER WHERE Transdate <= Date())
GROUP BY TransDate
) AS g on g.TransDate = r.TransDate AND g.MinAmount = r.Amount

How to combine two sql expressions into one?

I have the following issue:
It is necessary to write a query that will output 'item_id', 'price_in_byr'.
'price_in_byr' is calculated as the 'price' of the items table multiplied by the currency rate at the maximum date of the rate from the table rates.
See Schema
I apologize for my English, I'll try to explain by example:
Goods with item_id = 5 costs 20 euros, in the rates table the maximum date for the euro is January 12, at that date the exchange rate was 25. Total our 'price_in_byr' is 25 * 20 = 500
My solution with temp table:
CREATE TABLE tempRate SELECT currency, MAX(rate) AS maxRate FROM rates GROUP
BY currency;
SELECT items.item_id,(ifnull(tempRate.maxRate,1) * items.price) AS price_in_byr
FROM items
LEFT JOIN tempRate ON items.currency = tempRate.currency;
Tell me please, how can I do it in one query?
You can just use a subquery:
SELECT
items.item_id,(ifnull(tempRate.maxRate,1) * items.price) AS price_in_byr
FROM
items
LEFT JOIN
(
SELECT
currency, MAX(rate) AS maxRate
FROM
rates
GROUP BY
currency
) AS tempRate
ON items.currency = tempRate.currency;
In practice, you substitute "tempRate" by (definition of tempRate) AS tempRate.
You can see an example at dbfiddle here
If you actually want the * most_recent_rate*, you'd do something completely different; insert a subquery to compute it. This subquery looks for all rates of the given currency, sorts them by their exchange_ts (meaning timestamp) in descending order, and just picks to top 1:
SELECT
items.item_id,(ifnull(
(
SELECT
rate AS most_recent_rate
FROM
rates
WHERE
rates.currency = items.currency
ORDER BY
exchange_ts DESC
LIMIT 1
)
, 1) * items.price) AS price_in_byr
FROM
items ;
dbfiddle here
You can make your tempRate query into a subquery:
SELECT
items.item_id,
(ifnull(tempRate.maxRate,1) * items.price) AS price_in_byr
FROM
items
LEFT JOIN (
SELECT currency, MAX(rate) AS maxRate
FROM rates
GROUP BY currency
) as tempRate ON items.currency = tempRate.currency;
I am not sure if mysql supports this level of subquery but see if it works:
select I.item_id, I.price * CR.rt
from
items as I,
(
select r.currency cy, r.rate rt
from
rates as r, (select currency, max(date) max_date from rates group by currency) as R_MAXDATES
where
r.currency = R_MAXDATES.currency
and r.date = R_MAXDATES.max_date;
) As CR
where
I.currency = CR.cy

MySQL Query issue - Adding additional and totaling

I have written the following query which is design the multiply the total quantity of products, against quantity price and put this into a new column. What I am wondering and a bit stuck with. Is there then a way to also total this new column. So I as well as getting a total for each row ordered, I can also get a total for the order itself.
SELECT Product_Quantity, ProductPrice, (Product_Quantity*ProductPrice) as Total FROM Order_Info WHERE Order_Number = '1'
This add a record with a total.
Simulates GROUP BY with ROLLUP
SELECT
Product_Quantity
, ProductPrice
, (Product_Quantity*ProductPrice) AS Total
FROM
Order_Info
WHERE
Order_Number = '1'
UNION ALL
SELECT
NULL
, NULL
, SUM((Product_Quantity*ProductPrice)) AS Total
FROM
Order_Info
WHERE
Order_Number = '1'
To get a summary row, you can use sql's union and the sum aggregating function. Try this:
SELECT Product_Quantity, ProductPrice, (Product_Quantity*ProductPrice) as Total FROM Order_Info WHERE Order_Number = '1'
union all
SELECT sum(Product_Quantity), sum(ProductPrice), sum(Product_Quantity*ProductPrice) as Total FROM Order_Info WHERE Order_Number = '1'

SQL - How to select the most occurring entry from one column and list it with another column?

I am trying to select the most sold item from the column Product_ID (FK) which should be listed together with a BRANCH_ID (FK) e.g The most sold item for branch 1, then the most sold item for branch 2 etc. These are all in my table called SALES.
Is there a query that allows me to do this?
Any help is appreciated
If one row is one order item then something like this would help:
SELECT
s.branch_id
, s.product_id
, s.qnty
FROM (
SELECT
branch_id
, s.product_id
, COUNT(*) AS qnty
, RANK() OVER(PARTITION BY s.branch_id ORDER BY COUNT(*) DESC) AS [rank]
FROM dbo.sales AS s
GROUP BY s.branch_id, s.product_id
) s
WHERE s.rank = 1
ORDER BY branch_id
If two items sold the same amount they will both appear.
Without table definition I can't help you further.
For MySQL:
select distinct s1.BRANCH_ID, s1.Product_ID
from SALES s1
where s1.Product_ID = (
select s2.Product_ID
from SALES s2
where s2.BRANCH_ID = s1.BRANCH_ID
group by s2.Product_ID
order by count(1) desc
limit 1
);
Or better:
select b.BRANCH_ID, (
select s2.Product_ID
from SALES s2
where s2.BRANCH_ID = b.BRANCH_ID
group by s2.Product_ID
order by count(1) desc
limit 1
) as Product_ID
from BRANCHES b;
http://sqlfiddle.com/#!9/5aaf65/4

SQL Average of Sum of Calculated Attribute

I am trying to take the average of the values for a category, where the rows are grouped by sub-category with a calculated sum. The Primary key of the Parent Table is the grouped attribute of the Child Table. The grouped attribute of the Parent Table is neither the primary key or in the Child Table.
Simple representation:
select Category, avg(CalculatedSum)
from ParentTable pt
inner join (
select Subcategory, sum(Quantity * Price) as 'CalculatedSum'
from ChildTable
group by Subcategory
) ct
on pt.ID = ct.Subcategory
group by Category
The actual SQL is as follows:
select c.CU_AGE_RANGE, count(*) as '# of Customers', avg(SumSales) as 'Avg of SumSales', max([Max of SumSales]) as 'Max of SumSales', min([Min of SumSales]) as 'Min of SumSales'
from Customers c
inner join (
select CUSTOMER_ID, sum(QTY_SOLD * SALES) as SumSales, max(QTY_SOLD*SALES) as 'Max of SumSales', min(QTY_SOLD*SALES) as 'Min of SumSales'
from Sales
where (SALES > 0) and (QTY_SOLD > 0) and (COST > 0)
Group by CUSTOMER_ID
) s
on c.CUSTOMER_ID = s.CUSTOMER_ID
group by c.CU_AGE_RANGE
I have tried changing the group by clause to various orders of the Category (CU_AGE_RANGE) and Subcategory (CUSTOMER_ID) but am always having the same error.
The error is that the table will always show me the SUM of the SUMS (I believe). I am assuming this is the error because the typical average in the Child Table is 250 to 1000, and the Avg(Sum()) is returning values that are roughly the number of rows per Category times the expected Sum().
I cannot post a photo due to low reputation, so please see the following Comma Delimited Results Table:
CU_AGE_RANGE,#_of_Customers,Avg_of_SumSales,Max_of_SumSales,Min_of_SumSales
NULL,125,4261665.306,433460737.7,0.0017
20-29 ,1192,1154040.907,1374037708,0.00025
30-39 ,1902,25429.52329,29426212.64,0.00015
40-49 ,2118,2418.829874,2066725,0.0001
50-59 ,2204,114625.4111,248240261.3,0.00015
60+ ,2135,160156.4341,334617675,0.0005
patrickbig,1,65.5737,12,0.06
Under 19 ,484,1431.262112,92160,0.0001
I am trying to figure out why the AVG(SUM()) is returning what seems to be the SUM(SUM()). My current hunch is that since the SUM() is of a calculated entry, the calculated value is recalculated based on the grouping in the Parent Table. So this would be:
DESIRED:
x * y for each row in Child Table
sum(x*y) for each Subcategory
Avg(sum(x/y)) for each Category of Subcategory
QTY_SOLD * SALE for each row in Sales
sum(QTY_SOLD*SALE) for each CUSTOMER_ID
avg(sum(QTY_SOLD*SALE) for each CU_AGE_RANGE group of CUSTOMER_IDs
ACTUAL:
x * y for each row in Child Table
sum(x * y) for each Subcategory
avg(sum(x * y) for each Category
avg(sum(QTY_SOLD*SALE) for each CU_AGE_RANGE
which is equal to:
sum(QTY_SOLD*SALE) for each CU_AGE_RANGE
How do I get from the current (sum of Category) to desired (avg by Category of sum of Subcategory)?
Your count of customers is wrong. You are counting the number of sales made, not the number of customers. Change to count( DISTINCT c.CUSTOMER_ID ) should solve it.
select c.CU_AGE_RANGE, count( DISTINCT c.CUSTOMER_ID ) as '# of Customers', avg(SumSales) as 'Avg of SumSales', max([Max of SumSales]) as 'Max of SumSales', min([Min of SumSales]) as 'Min of SumSales'
from Customers c
inner join (
select CUSTOMER_ID, sum(QTY_SOLD * SALES) as SumSales, max(QTY_SOLD*SALES) as 'Max of SumSales', min(QTY_SOLD*SALES) as 'Min of SumSales'
from Sales
where (SALES > 0) and (QTY_SOLD > 0) and (COST > 0)
Group by CUSTOMER_ID
) s
on c.CUSTOMER_ID = s.CUSTOMER_ID
group by c.CU_AGE_RANGE
Let's think about the sub-query first:
select Subcategory, sum(Quantity * Price) as 'CalculatedSum'
from ChildTable
group by Subcategory
Each and every record of the resulting relation is representing an aggregation of a Subcategory. Now, avg(CalculatedSum) should yield the average of the CalculatedSum values. Try to calculate sum(CalculatedSum) instead and see if there is a difference.