Base: name, rate, dt.
Every day cost is inserted, I need to know the maximum cost without the first day. The first day of all products is different.
With all dates
SELECT `name`, MAX(rate) AS max
FROM `base`
GROUP BY `name`
This is similar to what I want, but not working.
SELECT `name`, MAX(rate) AS max, MIN(dt) AS min_dt
FROM `base`
WHERE `dt` > `min_dt`
GROUP BY `name`
Eample base
skirt, 6, 2018-10-10 00:00:00
skirt, 7, 2018-10-11 00:00:00
cap, 7, 2018-10-11 00:00:00
skirt, 8, 2018-10-12 00:00:00
cap, 6, 2018-10-12 00:00:00
Need
skirt, 8
cap, 6
One approach is to use an inline view to get the min_dt for each name. Then we can join and exclude the minimum date rows
Something like this:
SELECT b.name
, MAX(b.rate) AS `max`
FROM ( SELECT d.name
, MIN(d.dt) AS min_dt
FROM `base` d
GROUP
BY d.name
) m
JOIN `base` b
ON b.dt > m.min_dt
AND b.name = m.name
GROUP
BY b.name
There are other query patterns that will achieve an equivalent result. My preference would to avoid a correlated subquery, but something like this would also return the specified result:
SELECT b.name
, MAX(b.rate) AS `max`
FROM `base` b
WHERE b.dt > ( SELECT MIN(d.dt)
FROM `base` d
WHERE d.name = b.name
)
GROUP
BY b.name
(With both of these query forms, if there is only row in base for a given name, the query will not return a row for that name.)
You should try to exclude the min dt using subqueries:
SELECT name, MAX(rate) AS max
FROM (SELECT name, rate, dt
FROM base B
WHERE dt NOT IN (SELECT MIN(dt) FROM base WHERE name=B.name )) as A
GROUP BY name
Fiddle here
Related
I have a table with cars. Each car has a unique id, and other car stat-related columns. I want to display top 10 fastest cars. If a car has the same Trim + ModelID + MakeID, I want to show only the newest car.
Example:
Year Make Model Trim Time060
--------------------------------
2017 Bugatti Chiron null 2.3
2018 Bugatti Chiron null 2.3
My problem is that if Trim is null, neither 2017 nor 2018 shows up. If the trims are not null and are the same, I correctly get the 2018 car.
Here is the query that I have:
SELECT stats.*
FROM stats
NATURAL
JOIN
( SELECT MakeID
, ModelID
, Trim
, MAX(Year) AS Year
FROM stats
GROUP
BY Trim
, ModelID
, MakeID
) t
ORDER
BY Time060
LIMIT 10
I've tried using ...(SELECT MakeID, ModelID, IFNULL(Trim, 'temp_trim') AS Trim, MAX(Year) AS Year FROM stats GROUP BY Trim, ModelID, MakeID)..., but that doesn't work as well.
Use NOT EXISTS and the null safe equal to operator <=> to check for the equality of NULLs:
select s.* from stats s
where not exists (
select 1 from stats
where makeid = s.makeid and modelid = s.modelid and trim <=> s.trim and year > s.year
)
See the demo.
Or:
select s.* from stats s
where s.year = (
select max(year)
from stats
where makeid = s.makeid and modelid = s.modelid and trim <=> s.trim
)
See the demo.
Or with a join:
select s.*
from stats s inner join (
select makeid, modelid, trim, max(year) year
from stats
group by makeid, modelid, trim
) t on t.makeid = s.makeid and t.modelid = s.modelid and t.trim <=> s.trim and t.year = s.year
See the demo.
Since you are interested in performance check another option if your version of MySql/MariaDB supports window functions:
select t.year, t.makeid, t.modelid, t.trim, t.time060
from (
select s.*,
row_number() over (partition by makeid, modelid, trim order by year) rn
from stats s
) t
where t.rn = 1
See the demo.
Afternoon all
I am trying to create a view with 3 tables.
Create view breakdown as
SELECT x.month_
, x.returns_
, y.sales_
, z.profit_
FROM
(SELECT SUM(Return_Value_Eur) AS returns_
, monthname(Return_Date) AS month_
FROM returns
GROUP BY month_) x
INNER JOIN
(SELECT SUM(Total_Price_Eur) AS sales_
, monthname(Order_Date) AS month_
FROM orders
GROUP BY month_) y ON x.month_ = y.month_
INNER JOIN
(SELECT (SUM(Total_Price_Eur)-SUM(Return_Value_Eur)) AS profit_
, monthname(Order_Date) AS month_
FROM returns, orders
GROUP BY month_) z ON y.month_ = z.month_
I have the following code however I'm struggling with the profit table. This table should be a difference between the y.sales and x.returns. However the figures are coming up as follows, the profit column should be showing 394.
Month_ Returns_ Sales_ Profit_
January 108 502 -251
Any help would be greatly appreciated.
Don't mix the old-school comma syntax for the join operation with the JOIN keyword; actually, just ditch the old-school comma syntax altogether for new queries.
Looks like the problem is the cartesian product of the join between returns and orders, the inline view returning "profit".
It's not clear what we are trying to achieve (broken SQL is like that, in that it fails to accurately communicate the actual specification)
It looks to me (and this is just a guess here) is that the intent is to subtract total returns from total sales, by month.
I'd do it like this:
SELECT v.month_
, IFNULL( SUM(v.returns_) ,0) AS returns_
, IFNULL( SUM(v.sales_ ) ,0) AS sales
, IFNULL( SUM(v.sales_ ) ,0)
- IFNULL( SUM(v.returns_) ,0) AS net_
FROM (
SELECT 0.0 AS sales_
, SUM(r.return_value_eur) AS returns_
, MONTHNAME(r.return_date) AS month_
, MONTH(r.return_date) AS month_num
FROM returns r
GROUP
BY MONTHNAME(r.return_date)
, MONTH(r.return_date)
UNION ALL
SELECT SUM(o.total_price_eur) AS sales_
, 0.0 AS returns_
, MONTHNAME(o.order_date) AS month_
, MONTH(o.order_date) AS month_num
FROM orders o
GROUP
BY MONTHNAME(o.order_date)
, MONTH(o.order_date)
) v
GROUP
BY v.month_
, v.month_num
ORDER
BY v.month_num
Note that this query ignores the year, which is a bit odd, mashing together November 2019, with November 2018, 2017, et al.
The example query orders rows by calendar order, January, February, March, ... that's the reason for the additional month_num column.
If there are no rows in returns or orders for a given month, then that month will not appear in the resultset.
To guarantee rows returned for all 12 calendar months, I would use row source that was guaranteed to return me one row for each month. Then do outer joins to aggregated returns by month and aggregated sales by month. (I'd also have the queries work with the integer value for the month, and then translate the integer to a string "month name" as a final output step.)
Something like this:
SELECT MONTHNAME(STR_TO_DATE(c.month_num,'%m')) AS month_
, IFNULL( tr.returns_ ) ,0) AS returns_
, IFNULL( ts.sales_ ) ,0) AS sales_
, IFNULL( ts.sales_ ) ,0)
- IFNULL( tr.returns_ ) ,0) AS net_
FROM ( -- row source for calendar month 1..12
SELECT 1 AS month_num UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8
UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
) c
LEFT
JOIN ( -- total returns by month
SELECT SUM(r.return_value_eur) AS returns_
, MONTH(r.return_date) AS month_num
FROM returns r
GROUP
BY MONTH(r.return_date)
) tr
ON tr.month_num = c.month_num
LEFT
JOIN ( -- total orders by month
SELECT SUM(o.total_price_eur) AS sales_
, MONTH(o.order_date) AS month_num
FROM orders o
GROUP
BY MONTH(o.order_date)
) ts
ON ts.month_num = c.month_num
ORDER
BY c.month_num
I trying to get the last 6 months of the min and max of prices in my table and display them as a group by months. My query is not returning the corresponding rows values, such as the date time for when the max price was or min..
I want to select the min & max prices and the date time they both occurred and the rest of the data for that row...
(the reason why i have concat for report_term, as i need to print this with the dataset when displaying results. e.g. February 2018 -> ...., January 2018 -> ...)
SELECT metal_price_id, CONCAT(MONTHNAME(metal_price_datetime), ' ', YEAR(metal_price_datetime)) AS report_term, max(metal_price) as highest_gold_price, metal_price_datetime FROM metal_prices_v2
WHERE metal_id = 1
AND DATEDIFF(NOW(), metal_price_datetime) BETWEEN 0 AND 180
GROUP BY report_term
ORDER BY metal_price_datetime DESC
I have made an example, extract from my DB:
http://sqlfiddle.com/#!9/617bcb2/4/0
My desired result would be to see the min and max prices grouped by month, date of min, date of max.. and all in the last 6 months.
thanks
UPDATE.
The below code works, but it returns back rows from beyond the 180 days specified. I have just checked, and it is because it joining by the price which may be duplicated a number of times during the years.... see: http://sqlfiddle.com/#!9/5f501b/1
You could use twice inner join on the subselect for min and max
select a.metal_price_datetime
, t1.highest_gold_price
, t1.report_term
, t2.lowest_gold_price
,t2.metal_price_datetime
from metal_prices_v2 a
inner join (
SELECT CONCAT(MONTHNAME(metal_price_datetime), ' ', YEAR(metal_price_datetime)) AS report_term
, max(metal_price) as highest_gold_price
from metal_prices_v2
WHERE metal_id = 1
AND DATEDIFF(NOW(), metal_price_datetime) BETWEEN 0 AND 180
GROUP BY report_term
) t1 on t1.highest_gold_price = a.metal_price
inner join (
select a.metal_price_datetime
, t.lowest_gold_price
, t.report_term
from metal_prices_v2 a
inner join (
SELECT CONCAT(MONTHNAME(metal_price_datetime), ' ', YEAR(metal_price_datetime)) AS report_term
, min(metal_price) as lowest_gold_price
from metal_prices_v2
WHERE metal_id = 1
AND DATEDIFF(NOW(), metal_price_datetime) BETWEEN 0 AND 180
GROUP BY report_term
) t on t.lowest_gold_price = a.metal_price
) t2 on t2.report_term = t1.report_term
simplified version of what you should do so you can learn the working process.
You need calculate the min() max() of the periods you need. That is your first brick on this building.
you have tableA, you calculate min() lets call it R1
SELECT group_field, min() as min_value
FROM TableA
GROUP BY group_field
same for max() call it R2
SELECT group_field, max() as max_value
FROM TableA
GROUP BY group_field
Now you need to bring all the data from original fields so you join each result with your original table
We call those T1 and T2:
SELECT tableA.group_field, tableA.value, tableA.date
FROM tableA
JOIN ( ... .. ) as R1
ON tableA.group_field = R1.group_field
AND tableA.value = R1.min_value
SELECT tableA.group_field, tableA.value, tableA.date
FROM tableA
JOIN ( ... .. ) as R2
ON tableA.group_field = R2.group_field
AND tableA.value = R2.max_value
Now we join T1 and T2.
SELECT *
FROM ( .... ) as T1
JOIN ( .... ) as T2
ON t1.group_field = t2.group_field
So the idea is if you can do a brick, you do the next one. Then you also can add filters like last 6 months or something else you need.
In this case the group_field is the CONCAT() value
Here's the report:
This is how I got the percentages for column the '%Change of most recent year".
=((Last(Fields!Quantity.Value,"Child") - First(Fields!Quantity.Value)) / First(Fields!Quantity.Value))`
= ((54675 - 55968)/55968 ) = -2.31%'
= ((54675 - 57849)/57849) = -5.49%'
It will always take the first year '2012' in this case and get the percentages against each other year. If I enter the years 2005,2004,2003,2002,2001 it will always take the first year and do a percentages against each additional year. 2005 to 2004, 2005 to 2003, 2005 to 2002 and so on. I can have as many as 2 column (year) to many columns.
I need to do it for the Total and Subtotal but it won't work because it's in a different scope.
data is = row Child group
Sub Total: = row Parent group
Total: = row Total group
Year = Column Period group
Query use to get result.
SELECT MEMBERSHIP_CODE
, PERIOD, COUNT(DISTINCT ID) AS Distinct_ID
, SUM(QUANTITY) AS Quantity
, '01-Personal' AS Child
, '01-Overall' AS Parent
, 'Total' as Total
FROM vf_Sshot AS vfs
INNER JOIN vProd AS vP ON vfs.PRODUCT_CODE = vP.PRODUCT_CODE
INNER JOIN vMem_Type vMT on vMT.Member_Type = vfs.Member_Type
WHERE (PERIOD IN ( (SELECT Val from dbo.fn_String_To_Table(#Periods,',',1))))
AND (vMT.MEMBER_TYPE NOT IN ('a','b','c'))
AND (vfs.STATUS IN ( 'A', 'D', 'C'))
AND (MEMBERSHIP_CODE NOT IN ('ABC', 'DEF' ))
and vP.PROD_TYPE in ('DUE','MC','SC')
and vMT.Member_Record = '1'
GROUP BY MEMBERSHIP_CODE, PERIOD
Any ideas?
How would I produce this output?
TOTAL: 57,573 58,941 57,573 61,188 57,573 61,175 57,175
This is the easiest way of solving your problem. In your query, identify the sum for the latest period on a separate column (you can transform your query into a CTE, so that you don't have to change your base query a lot):
WITH query AS (
SELECT MEMBERSHIP_CODE
, PERIOD, COUNT(DISTINCT ID) AS Distinct_ID
, SUM(QUANTITY) AS Quantity
, '01-Personal' AS Child
, '01-Overall' AS Parent
, 'Total' as Total
...
UNION
SELECT
...
)
SELECT
A.MEMBERSHIP_CODE,
A.PERIOD,
A.Distinct_ID,
A.Child,
A.Parent,
A.Total,
A.Quantity,
B.Quantity AS LastPeriodQuantity
FROM
query A INNER JOIN
(SELECT *, ROW_NUMBER() OVER(PARTITION BY MEMBERSHIP_CODE, Distinct_ID, Child, Parent ORDER BY PERIOD DESC) as periodOrder FROM query) B ON
A.MEMBERSHIP_CODE = B.MEMBERSHIP_CODE AND
A.DISTINCT_ID = B.DISTINCT_ID AND
A.Parent = B.Parent AND
A.Child = B.Child AND
A.Total = B.Total AND
B.PeriodOrder = 1
And then on all your totals/subtotals/columns you will be accessing a column that is being grouped/filtered by the same rules than your denominator. Your expression can remain, for all cells, something like this:
=(Fields!LastPeriodQuantity.Value - Fields!Quantity.Value) / Fields!Quantity.Value
I have a table called receiving with 4 columns:
id, date, volume, volume_units
The volume units are always stored as a value of either "Lbs" or "Gals".
I am trying to write an SQL query to get the sum of the volumes in Lbs and Gals for a specific date range. Something along the lines of: (which doesn't work)
SELECT sum(p1.volume) as lbs,
p1.volume_units,
sum(p2.volume) as gals,
p2.volume_units
FROM receiving as p1, receiving as p2
where p1.volume_units = 'Lbs'
and p2.volume_units = 'Gals'
and p1.date between "2012-01-01" and "2012-03-07"
and p2.date between "2012-01-01" and "2012-03-07"
When I run these queries separately the results are way off. I know the join is wrong here, but I don't know what I am doing wrong to fix it.
SELECT SUM(volume) AS total_sum,
volume_units
FROM receiving
WHERE `date` BETWEEN '2012-01-01'
AND '2012-03-07'
GROUP BY volume_units
You can achieve this in one query by using IF(condition,then,else) within the SUM:
SELECT SUM(IF(volume_units="Lbs",volume,0)) as lbs,
SUM(IF(volume_units="Gals",volume,0)) as gals,
FROM receiving
WHERE `date` between "2012-01-01" and "2012-03-07"
This only adds volume if it is of the right unit.
This query will display the totals for each ID.
SELECT s.`id`,
CONCAT(s.TotalLbsVolume, ' ', 'lbs') as TotalLBS,
CONCAT(s.TotalGalVolume, ' ', 'gals') as TotalGAL
FROM
(
SELECT `id`, SUM(`volume`) as TotalLbsVolume
FROM Receiving a INNER JOIN
(
SELECT `id`, SUM(`volume`) as TotalGalVolume
FROM Receiving
WHERE (volume_units = 'Gals') AND
(`date` between '2012-01-01' and '2012-03-07')
GROUP BY `id`
) b ON a.`id` = b.`id`
WHERE (volume_units = 'Lbs') AND
(`date` between '2012-01-01' and '2012-03-07')
GROUP BY `id`
) s
this is a cross join with no visible condition on the join, i don't think you meant that
if you want to sum quantities you don't need to join at all, just group as zerkms did
You can simply group by date and volume_units without self-join.
SELECT date, volume_units, sum(volume) sum_vol
FROM receving
WHERE date between "2012-01-01" and "2012-03-07"
GROUP BY date, volume_units
Sample test:
select d, vol_units, sum(vol) sum_vol
from
(
select 1 id, '2012-03-07' d, 1 vol, 'lbs' vol_units
union
select 2 id, '2012-03-07' d, 2 vol, 'Gals' vol_units
union
select 3 id, '2012-03-08' d, 1 vol, 'lbs' vol_units
union
select 4 id, '2012-03-08' d, 2 vol, 'Gals' vol_units
union
select 5 id, '2012-03-07' d, 10 vol, 'lbs' vol_units
) t
group by d, vol_units