mysql get last N records with MAX(date) - mysql

So I have following data in a product_rate_history table -
I want to select last N records ( eg 7 records ) informing rate change history of given product. If product rate is changed more than one time a day, then query should select most recent rate change for that day.
So from above table I want output like following for product id 16-
+-----------+-------------------------+------------------------+
| product_id | previous_rate | date |
+----------------+--------------------+------------------------|
| 16 | 2400 | 2016-04-30 23:05:35 |
| 16 | 4500 | 2016-04-29 11:02:42 |
+----------------+--------------------+------------------------+
I have tried following query but it returns only one row having last update rate only-
SELECT * FROM `product_rate_history` prh
INNER JOIN (SELECT max(created_on) as max FROM `product_rate_history` GROUP BY Date(created_on)) prh2
ON prh.created_on = prh2.max
WHERE prh.product_id = 16
GROUP BY DATE(prh.created_on)
ORDER BY prh.created_on DESC;

First, you do not need an aggregation in the outer query.
Second, you need to repeat the WHERE clause in the subquery (for the method you are using):
SELECT prh.*
FROM product_rate_history prh INNER JOIN
(SELECT max(created_on) as maxco
FROM product_rate_history
WHERE prh.product_id = 16
GROUP BY Date(created_on)
) prh2
ON prh.created_on = prh2.maxco
WHERE prh.product_id = 16
ORDER BY prh.created_on DESC;

Related

Group by with sum doesn't return correct result

Say a table has this schema :
grp | number
1 | 10
1 | 10
1 | 10
2 | 30
2 | 30
3 | 20
Note that each unique grp has a unique number even if there are more than 1 grp. I'm looking to sum all numbers for each unique grp.
So I want to group my table by grp to have this :
grp | number
1 | 10
2 | 30
3 | 20
And then get the sum which is now 60, but without grouping it gets me 110 as it calculates the sum of everything without grouping. All in one query, with no sub-queries if possible.
I've tried doing the following :
SELECT sum(number) as f
FROM ...
WHERE ...
GROUP BY grp
But this doesn't work, it returns multiple results and not the single result of the sum. What am I doing wrong?
You can use subquery to select unique records & do the sum:
select sum(number)
from (select distinct grp, number
from table t
) t;
If you group by the group, then you'll get one result for each group. And it won't take into account the fact that you only want to use the value from each group once.
To get your desired result, taking one row from each group, you first need to make a subquery selecting DISTINCT group/number combinations from the table, and then SUM that.
SELECT
sum(`number`) as f
FROM
(SELECT DISTINCT `grp`, `number` FROM table1) g
This will output 60.
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=8a3b346041731a4b4c85f4e151c10f70

mysql: get daily average price of product table with version

my products table :
ProductId(inc.key) | Price | VersionCreatedDate | MainProductId
1 | 15 | 1-11-2016 | 1
2 | 20 | 1-11-2016 | 2
3 | 30 | 1-11-2016 | 3
4 | 10 | 2-11-2016 | 1 -> mainProductId 1 changed price(-5$)
5 | 20 | 3-11-2016 | 3 -> mainProductId 3 changed price(-10$)
6 | 30 | 4-11-2016 | 3 -> mainProductId 3 changed price(+10$)
I want to display the output as like this
Date | AvgPrice
1-11-2016 | 21.67 ((15+20+30)/3)
2-11-2016 | 20 ((10+20+30)/3)
3-11-2016 | 16.67 ((10+20+20)/3)
4-11-2016 | 20 ((10+20+30)/3)
How do I get the output with sql code?
Assuming you have a calendar table with all dates you need. And you have a main_products table with MainProductId as primary/unique key. The following query should return average prices for every day in october 2016.
select sub.date, avg(sub.Price) as Price
from (
select
c.date,
m.MainProductId,
(
select p.Price
from products
where p.MainProductId = m.MainProductId
and p.VersionCreatedDate < c.date + interval 1 day
order by p.VersionCreatedDate desc
limit 1
) as Price
from callendar c
cross join main_products m
where c.date between '2016-10-01' and '2016-10-31'
) sub
group by sub.date
order by sub.date
The subquery (derived table aliased as sub) returns a combination of all dates in the range and all "main products" from the main_products table. The recent price each "main product" for a specific date is calculated in the subselect (correlated subquery in the SELECT clause) using ORDER BY and LIMIT 1. This allows us to group the subquery result by date and calculate the average price per date.
It is even possible to eliminate the derived table and hope that mysql can use an index to GROUP BY date instead of working on a temp table:
select c.date, avg((
select p.Price
from products
where p.MainProductId = m.MainProductId
and p.VersionCreatedDate < c.date + interval 1 day
order by p.VersionCreatedDate desc
limit 1
)) as Price
from callendar c
cross join main_products m
where c.date between '2016-10-01' and '2016-10-31'
group by c.date
order by c.date
I have no clue if that query can be executed effiently (especially if mysql can). You should however have at least the following indexes: callendar(date), products(MainProductId, VersionCreatedDate)

Mysql Agregate function to select maximum and then select minimum price within that group

I am trying to get the maximum value out of a aggregate function, and then also get the min value out of a Price column which comes back in results.
id | discount | price
1 | 60 | 656
2 | 60 | 454
3 | 60 | 222
4 | 30 | 335
5 | 30 | 333
6 | 10 | 232
So in above table, I would like to separate Minimum Price vs Highest Discount.
This is the result I should be seeing:
id | discount | price
3 | 60 | 222
5 | 30 | 333
6 | 10 | 232
As you can see, its taken discount=60 group and separated the lowest price - 222, and the same for all other discount groups.
Could someone give me the SQL for this please, something like this -
SELECT MAX(discount) AS Maxdisc
, MIN(price) as MinPrice
,
FROM mytable
GROUP
BY discount
However, this doesnt separate the minimum price for each group. I think i need to join this table to itself to achieve that. Also, the table contains milions of rows, so the sql needs to be fast. One flat table.
This question is asked and answered with tedious regularity in SO. If only the algorithm was better at spotting duplicates. Anyway...
SELECT x.*
FROM my_table x
JOIN
( SELECT discount,MIN(price) min_price FROM my_table GROUP BY discount) y
ON y.discount = x.discount
AND y.min_price = x.price;
In your query, you cannot group by discount and then maximize the discount value.
This should get you the result you are looking for..
SELECT Max(ID) AS ID, discount, MIN(price) as MinPrice, FROM mytable GROUP BY discount
If you do not need the id, yo would do:
select discount, min(price) as minprice
from table t
group by discount;
If you want other columns in the row, you can either join back to the original table or use the substring_index()/group_concat() trick:
select substring_index(group_concat(id order by price), ',', 1) as id,
discount, min(price)
from table t
group by discount;
This will not always work because the intermediate result for group_concat() can overflow if there are too many matches within a column. This is controlled by a system parameter, which could be made bigger if necessary.

Join tables and preform aggregation on each of them

I have the following tables:
table part_list:
part_number | description | type
100 blablabla blabla
table part_list_supplier:
part_id | artikel
100 100100
100 200100
and I have this query:
select part_list.part_number, part_list.description, part_list.type, group_concat(coalesce(part_list_supplier.artikel, "nothing")) as "artikel"
from part_list
left join part_list_supplier on (part_list.part_number = part_list_supplier.part_id)
group by part_list.part_number;
this is the result:
part_number | description | type | artikel
100 blablablabla blabla 100100,200100
but I want to show the total stock per partnumber behind it. table receipt:
Number | import
100 5
100 10
table sales:
Number | sold
100 5
this is my query for one table:
SELECT SUM(sold) AS sold
FROM sales WHERE number = '".$partnumber.”'
but I want to calculate the stock per number and that must be shown behind the other results.
the full result:
part_number | description | type | artikel | stock
100 blablablabla blabla 100100,200100 10
The stock should be 10 because the total number of imports is 15 (5 + 10) and the total number of sales is 5.
I broke this up into pieces to solve it. I started by writing two queries, one that counted total receipt and one that counted total sales:
SELECT r.number, SUM(r.import) AS totalIn
FROM receipt r
GROUP BY r.number;
SELECT s.number, SUM(s.sold) AS totalOut
FROM sales s
GROUP BY s.number;
Then, I used those as two subqueries of a join to get the stock:
SELECT r.number, totalIn - totalOut AS stock
FROM(
SELECT r.number, SUM(r.import) AS totalIn
FROM receipt r
GROUP BY r.number) r
JOIN(
SELECT s.number, SUM(s.sold) AS totalOut
FROM sales s
GROUP BY s.number) s ON s.number = r.number;
Once I verfied this gave the proper stock, I was able to include those subqueries into your original query to build this:
SELECT pl.part_number, pl.description, pl.type,
GROUP_CONCAT(COALESCE(pls.artikel, "Nothing.")) AS artikel,
r.totalIn - s.totalOut AS stock
FROM part_list pl
LEFT JOIN part_list_supplier pls ON pls.part_id = pl.part_number
JOIN(
SELECT number, SUM(import) AS totalIn
FROM receipt
GROUP BY number) r ON r.number = pl.part_number
JOIN(
SELECT number, SUM(sold) AS totalOut
FROM sales
GROUP BY number) s ON s.number = r.number
GROUP BY pl.part_number;
Here is an SQL Fiddle example.
I may not be understanding your question properly, but can't you just add sum(sales.sold) to your select statement and join the sales table? E.g.:
select part_list.part_number, part_list.description, part_list.type, group_concat(coalesce(part_list_supplier.artikel, "nothing")) as "artikel", sum(sales.sold)
from part_list
left join part_list_supplier on (part_list.part_number = part_list_supplier.part_id)
left join sales on (part_list.part_number = sales.number
group by part_list.part_number;

How to count all the results and count specific in the same query - MySql

I need to count all FirstExtracted for a specific date, and I need to count all LastExtracted for the same date. So, for today, I would need all the FirstExtracted and LastExtractedthat equal 2012-10-24.
Here is what I have so far but it doesnt bring up LastExtracted. It outputs LastExtracted as same count as FirstExtracted:
(SELECT LastExtracted,FirstExtracted,
COUNT(FirstExtracted) AS FirstCount,
COUNT(LastExtracted) AS LastCount,
DATE_FORMAT(`LastExtracted`,'%Y-%m-%d') AS Lastdate,
DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') AS Firstdate
FROM results
WHERE DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') = DATE_FORMAT(`LastExtracted`,'%Y-%m-%d'))
UNION ALL
(SELECT LastExtracted,FirstExtracted,
COUNT(FirstExtracted) AS FirstCount,
COUNT(LastExtracted) AS LastCount,
DATE_FORMAT(`LastExtracted`,'%Y-%m-%d') AS Lastdate,
DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') AS Firstdate
FROM results
WHERE DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') = DATE_FORMAT(`LastExtracted`,'%Y-%m-%d') GROUP BY Firstdate)
ORDER BY Firstdate DESC
LIMIT 20
Maybe I should use inner join?
UPDATE:
so using your query i made some changes to make it do something else for me now. if you look at this page i put the query up
semesterold.com/code2.html
I want to count all titles and GROUP BY artist. it would be an array. then i want the sub queries to count by searchtype that match by the artists. so if the db has akon, rihanna, chris brown. i want it to count how many titles each artist has, say akon has 100. then i want to display the number of titles and then count how many of those 100 titles are google, bing, site specific for akon and etc.
I would use the union to get the two results (FirstExtracted and LastExtracted) separately and then merge them using a subquery.
SELECT
sub.date, sum(sub.FirstCount) AS FirstCount,
sum(sub.LastCount) AS LastCount
FROM (
SELECT
DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') AS date,
COUNT(FirstExtracted) AS FirstCount, 0 AS LastCount
FROM results
GROUP BY date
UNION ALL
SELECT
DATE_FORMAT(`LastExtracted`,'%Y-%m-%d'),
0, COUNT(LastExtracted)
FROM results
GROUP BY date
) AS sub
GROUP BY sub.date
Say you have 220 entries extracted today for the first time and 292 that were most recently extracted today. Among entries for other dates, this would give you:
+------------+------------+-----------+
| date | FirstCount | LastCount |
+------------+------------+-----------+
| 2012-10-24 | 220 | 292 |
+------------+------------+-----------+
Update The UNION alone will give you the following results. Notice the zero placeholders.
+------------+------------+-----------+
| date | FirstCount | LastCount |
+------------+------------+-----------+
| 2012-10-24 | 220 | 0 |
+------------+------------+-----------+
| 2012-10-24 | 0 | 292 |
+------------+------------+-----------+
I suggest adding DISTINCT in your query:
(SELECT LastExtracted,FirstExtracted,
COUNT(DISTINCT FirstExtracted) AS FirstCount,
COUNT(DISTINCT LastExtracted) AS LastCount,
DATE_FORMAT(`LastExtracted`,'%Y-%m-%d') AS Lastdate,
DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') AS Firstdate
FROM results
WHERE DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') = DATE_FORMAT(`LastExtracted`,'%Y-%m-%d'))
UNION ALL
(SELECT LastExtracted,FirstExtracted,
COUNT(DISTINCT FirstExtracted) AS FirstCount,
COUNT(DISTINCT LastExtracted) AS LastCount,
DATE_FORMAT(`LastExtracted`,'%Y-%m-%d') AS Lastdate,
DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') AS Firstdate
FROM results
WHERE DATE_FORMAT(`FirstExtracted`,'%Y-%m-%d') = DATE_FORMAT(`LastExtracted`,'%Y-%m-%d') GROUP BY Firstdate)
ORDER BY Firstdate DESC
LIMIT 20