Select last 3 rows of each item - mysql

i have two tables raw_materials and raw_materials_moves.
raw_materials has fields
id, name
raw_materials_moves has this fields
id, raw_material_id, price, created
so, i need to select last 3 moves of each raw material
i have this query but doesn´t work
SELECT a.*, b.price, b.created
FROM raw_materials a
LEFT JOIN
(SELECT price, raw_material_id, created FROM raw_material_moves ORDER BY created DESC LIMIT 3) b
ON b.raw_material_id=a.id
ORDER BY a.id

I would agree mostly with #chetan on the usage of variables to get this job done.
Here is one of the best articles that I have come across on the subject and different ways to handle it.. ranging from easy to hard.

i will suggest you to user cursor with DESC (or) by fetching in reverse order

SELECT r1.id,name,raw_material_id
FROM raw_materials r1, raw_materials_move r2
WHERE r1.id=r2.id ORDER BY DESC LIMIT 3;

last 3 moves by created (I assume date) of each raw material
select * from
(select a.id,a.price,a.created,
if(#mat=a.raw_material_id,#num:=#num+1,#num:=1) rank,
#mat:=a.raw_material_id
from raw_materials_moves a, (select #num:=0) b
order by a.raw_material_id,a.created desc) temp
where rank <=3;
see demo here

Reverse-sort it (i.e. ... SORT BY somefield DESC) and then get the first 3 (by appending LIMIT 3)

Related

SQL query:Having number=max(number) doesn't work

I have two tables,Writer and Books. A writer can pruduce many books. I want to get the all writers who produce maximal number of books.
Firstly, my sql query is like:
SELECT Name FROM(
SELECT Writer.Name,COUNT(Book.ID) AS NUMBER FROM Writer,Book
WHERE
Writer.ID=Book.ID
GROUP BY Writer.Name
)
WHERE NUMBER=(SELECT MAX(NUMBER) FROM
(SELECT Writer.Name,COUNT(Book.ID) AS NUMBER FROM Writer,Book
WHERE Writer.ID=Book.ID
GROUP BY Writer.Name
)
It works. However I think this query is too long and there exists some duplications. I want to make this query shorter. So I try another query like this:
SELECT Name FROM(
SELECT Writer.Name,COUNT(Book.ID) AS NUMBER FROM Writer,Book
WHERE
Writer.ID=Book.ID
GROUP BY Writer.Name
HAVING NUMBER = MAX(NUMBER)
)
However, this HAVING clause doesn't work and my sqlite says its an error.
I don't know why. Can anyone explain to me ? Thank you!
The HAVING clause provides filtering on the final set (typically after a group by) and does not provide additional grouping functionality. Think of it just like a WHERE clause, but can be applied after a GROUP BY.
Your query with the HAVING NUMBER = MAX(NUMBER) implies grouping of the set of NUMBER values across all records and doesn't make sense in this example (even though we all get what you want it to do).
Each query provides you with one level of aggregation, so you cannot use Max on COUNT in the same query. You need a sub-query like you did in your first query.
However, your first query can be simplified on MySQL to:
SELECT Writer.Name
FROM Writer, Book
WHERE Writer.ID = Book.ID
GROUP BY Writer.Name
HAVING COUNT(Book.ID) = (SELECT COUNT(Book.ID) AS n
FROM Writer, Book
WHERE Writer.ID = Book.ID
GROUP BY Writer.Name
ORDER BY n DESC
LIMIT 1)
In MySQL (but not SQLite), you can use variables to reduce the amount of work and make a simpler query. However, there are nuances there, because variables with group by require an extra level of subqueries:
SELECT name
FROM (SELECT t.*, (#m := if(#m = 0, NUMBER, #m)) as maxn
FROM (SELECT w.Name, COUNT(b.ID) AS NUMBER
FROM Writer w JOIN
Book b
ON w.ID = b.ID
GROUP BY w.Name
) t CROSS JOIN
(SELECT #m := 0) params
ORDER BY NUMBER desc
) t
WHERE maxn = number;
It looks like you are nesting aggregate functions, which is not allowed.
HAVING NUMBER = MAX(NUMBER) is like HAVING COUNT(Book.ID) = MAX(COUNT(Book.ID))
Nesting COUNT inside MAX seems to be the issue here

Is it possible to find out a value that is the most different with pure MySQL?

Lets say I have a list of url's and I want to find out the url that is the most unique. I mean which is appearing the fewest. Here is an example of the database:
3598 ('www.emp.de/blog/tag/fear-factory/',)
3599 ('www.emp.de/blog/tag/white-russian/',)
3600 ('www.emp.de/blog/musik/die-emp-plattenkiste-zum-07-august-2015/',)
3601 ('www.emp.de/Warenkorb/car_/',)
3602 ('www.emp.de/ter_dataprotection/',)
3603 ('hilfe.monster.de/my20/faq.aspx#help_1_211589',)
3604 ('jobs.monster.de/l-nordrhein-westfalen.aspx',)
3605 ('karriere-beratung.monster.de',)
3606 ('karriere-beratung.monster.de',)
In this case it should return jobs.monster.de or hilfe.monster.de. I only want one return value. Is that possible with pure mysql?
It should be some kind of counting of the main url before the ".de"
At this moment I do it this way:
con.execute("select url, date from urls_to_visit ORDER BY RANDOM() LIMIT 1")
You could join the table on itself where ID's are not identical and count those, Then order by descending order and limit to 1 result.
not checked.
SELECT COUNT(*) as hitcount,
SUBSTRING_INDEX(t1.`url`,'.',2) as url
FROM table t1
INNER JOIN table t2 ON
SUBSTRING_INDEX(t1.`url`,'.',2) = SUBSTRING_INDEX(t2.`url`,'.',2)
AND t1.id <> t2.id
GROUP BY SUBSTRING_INDEX(t1.`url`,'.',2)
ORDER BY hitcount ASC
LIMIT 1
EDIT
Just checked on this, and it doesn't quite work.
I came up with this alternative, which uses a subquery to group all the domains together and get a count.
SELECT subq.count as hitcount,SUBSTRING_INDEX(t1.`url`,'.',2) as domain
FROM hits t1
INNER JOIN
(SELECT COUNT(*) as count,
SUBSTRING_INDEX(`url`,'.',2) as domain
FROM hits GROUP BY SUBSTRING_INDEX(`url`,'.',2)
) subq
ON subq.domain = SUBSTRING_INDEX(t1.`url`,'.',2)
GROUP BY SUBSTRING_INDEX(t1.`url`,'.',2)
ORDER BY hitcount ASC
LIMIT 1
working fiddle
Given your sample data (ignoring the parentheses, because I have no idea what those are doing), this query should do what you want:
select substring_index(url, '.', 2) as domain, count(*) as cnt
from table t
group by substring_index(url, '.', 2)
order by cnt desc
limit 1;

Alternative to mysql WHERE IN SELECT GROUP BY when wanting max value in group by

I have the following query, which was developed from a hint found online because of a problem with a GROUP BY returning the maximum value; but it's running really slowly.
Having looked online I'm seeing that WHERE IN (SELECT.... GROUP BY) is probably the issue, but, to be honest, I'm struggling to find a way around this:
SELECT *
FROM tbl_berths a
JOIN tbl_active_trains b on a.train_uid=b.train_uid
WHERE (a.train_id, a.TimeStamp) in (
SELECT a.train_id, max(a.TimeStamp)
FROM a
GROUP BY a.train_id
)
I'm thinking I possibly need a derived table, but my experience in this area is zero and it's just not working out!
you can move that to a SUBQUERY and also select only required columns instead of All (*)
SELECT a.train_uid
FROM tbl_berths a
JOIN tbl_active_trains b on a.train_uid=b.train_uid
JOIN (SELECT a.train_id, max(a.TimeStamp) as TimeStamp
FROM a
GROUP BY a.train_id )T
on a.train_id = T.train_id
and a.TimeStamp = T.TimeStamp

Dynamic SQL with embedded table

I have a query which does the job but instead of the trans_inventory (which is an ID of the location) I need to get the location_name.
This one is working to get the id
SELECT *
FROM {TABLE}
WHERE trans_product = 646
ORDER BY trans_date2 DESC Limit 1
But I wonder if I can do it this way, somehow embed the location table, I have tried but below doesn't work
SELECT *, site_location.location_name
FROM site_trans
cross join
(select *
From site_location)
site_location
WHERE trans_product=646 ORDER BY trans_date2 DESC Limit 1
sl.location in the JOIN must be the site_location location id field name - I used locastion_id but it might be likely id as well. I used LEFT JOIN to avoid missing site_trans in case there is no matching location id.
SELECT s.*, sl.location_name
FROM site_trans AS s
LEFT JOIN site_location AS sl ON sl.location_id = s.location_id
WHERE s.trans_product=646
ORDER BY s.trans_date2 DESC
Limit 1

mysql random least used

I need to select a record at random but not one already selected before unless all records have been selected.
Table Setup:
_word (id, nam)
_word_tm (id, word_id, tm)
Every time a word is used it is loaded into _word_tm. What I am wanting to do is make sure I use all the words before I reuse an already used word.
What I am really looking for is something like the below but just trying to figure out how to mesh.
select w.nam FROM _word w LEFT JOIN _word_tm wt ON w.id = wt.word_id ORDER BY count(wt.id) asc, rand() limit 1
First, find out how many times the least-used words have been used:
select _word.id, count(*) c from _word
left join _word_tm on _word.id=_word_tm.word_id order by c limit 1;
Store that value (from c) in a variable $least_used. Then get all the words used that many times, in random order:
select _word.id, count(*) c from _word
left join _word_tm on _word.id=_word_tm.word_id
group by _word.id having c <= {$least_used}
order by rand() limit 1;
You should be able to do something like this:
SELECT * FROM word_table WHERE word NOT IN (SELECT word FROM words_used) ORDER BY rand() LIMIT 1;
You will need to have an updated copy of MySQL for this to work. Also, you would need to include a line of code afterward or before hand to clear/reset your words_used table once it had the same contents as word_table.
You can try the following SQL to return a random row
select A.nam
from _word A
where A.id not in (select B.id from _word_tm B)
order by rand()
limit 1
if the above does not return any reocrd , do a simple
select A.nam
from _word A
order by rand()
limit 1