Join with Max and pre-Max row - mysql

I have two tables 'sites' and 'index_log'. Table 'sites' is a information about sites(userid, name, description ...) Table index_log have columns date, index_count and siteid. So I want to get last and previous index_log row for each site, where userid = 10. This's my variant:
SELECT ff.id,
ff.siteurl,
ff.last_count,
ff.last_date,
il2.index_count as previous_count,
MAX(il2.date) as previous_date
FROM (
SELECT s.siteurl,
s.id,
il.index_count as last_count,
MAX(il.date) as last_date
FROM sites s
LEFT JOIN index_logs il ON il.siteid = s.id
WHERE s.userid = 10
GROUP BY s.id
) as ff
LEFT JOIN index_logs il2 ON il2.siteid = ff.id AND il2.date < ff.last_date
GROUP BY ff.id
But in this variant index_count column(last and previous) do not match with max date row. I hope for your help.

this will gives you last 2 log entries per sites.id
; with CTE as
(
SELECT s.siteurl, s.id, il.index_count, il.date,
RN = ROW_NUMBER() OVER (PARTITION BY s.id ORDER BY il.date DESC)
FROM sites s
JOIN index_logs il ON il.siteid = s.id
WHERE s.userid = 10
)
SELECT *
FROM CTE
WHERE RN <= 2

Related

How to write a mysql with respect to the following conditions

I have 3 table
tmp (pid,title,price)
tmp_studyarea(areaid, tittle, tm_pid)
tmp_module(mid,title, duration, areaid)
I am trying to write a query where I can obtain (tmp.pid, tmp.title, tmp.price, SUM(tmp_module.duration Where tmp_module.areaid = tmpstudyarea.areaid and tmp_studyarea.tmp_pid = tmp.pid) Group by tmp.pid
Here is a query I wrote and i'm unable to get expected results. Help please
SELECT s.title, s.pid, SUM(duration) IN (SELECT a.tmpid, a.areaid, a.title, SUM(m.duration) as duration FROM tmp_studyarea AS a, tmp, tmp_module as m WHERE m.areaid = a.areaid AND a.tmpid = s.pid GROUP BY a.areaid) FROM tmp as s;
Here is my expected resultst
title
pid
duration
tmp Title 1
1
3000
tmp Title 3
4
1000
EDIT
Found a solution
SELECT DISTINCT s.title,s.pid, (SELECT SUM(m.duration) as duration FROM tmp_studyarea AS a, tmp_module as m WHERE m.areaid = a.areaid AND a.tmpid = s.pid GROUP BY a.tmpid) as duration FROM tmp as s, tmp_studyarea, tmp_module GROUP by pid
You must join the 3 tables properly and aggregate:
SELECT t.pid, t.title, t.price,
SUM(m.duration) total_duration
FROM tmp t
INNER JOIN tmp_studyarea s ON s.tmp_pid = t.pid
INNER JOIN tmp_module m ON m.areaid = s.areaid
GROUP BY t.pid, t.title, t.price

Get only 1 result per group of ID

I have a list of records (domestic_helper_idcards) and I want to return only one card per staff (domestic_helper_id) that is not deleted (is_deleted = 0), and that has the card_expiration_date furthest in the future (latest expiry date).
Have tried grouping and so on, but cant get it to work. Code below:
SELECT * FROM domestic_helper_idcard
where
is_deleted = 0
order by card_expiration_date desc
This returns the following (image):
I want only records with ID 4 and 5 to be returned. Anyone?
You could use a join with the subquery grouped by domestic_helper_id with an aggregated function eg: max()
SELECT d.*
FROM domestic_helper_idcard d
inner join (
select domestic_helper_id, max(id) max_id
from domestic_helper_idcard
where is_deleted = 0
group by domestic_helper_id
) t on t.domestic_helper_id = d.domestic_helper_id and t.max_id = d.id
order by d.card_expiration_date desc
and as suggested by Jens after clarification using max card_expiration_date
SELECT d.*
FROM domestic_helper_idcard d
inner join (
select domestic_helper_id, max(card_expiration_date) max_date
from domestic_helper_idcard
where is_deleted = 0
group by domestic_helper_id
) t on t.domestic_helper_id = d.domestic_helper_id and t.max_date = d.max_date
order by d.card_expiration_date desc

Optimize Query with JOINS and Subqueries

I want to speed up one of my slower queries.
The problem is that I can't access the outer colum value within a subquery.
What I have:
SELECT r.id AS room_id, r.room_name, coalesce(d.score,0) AS total_messages, d.latest
FROM cf_rooms_time_frames tf
INNER JOIN cf_rooms r on r.id = tf.room_id
INNER JOIN(
SELECT cf.room_id, count(*) as score, max(cf.id) as latest
FROM cf_rooms_messages cf
WHERE EXISTS(
SELECT NULL FROM cf_rooms_time_frames tf
WHERE tf.start <= cf.id AND ( tf.end IS NULL OR tf.end >= cf.id )
AND tf.room_id = cf.room_id AND tf.uid = 8
)
GROUP BY cf.room_id
ORDER BY latest
DESC ) d on d.room_id = r.id
WHERE tf.uid = 8
ORDER BY coalesce(latest, score) DESC LIMIT 0, 20
What I want:
SELECT r.id AS room_id, r.room_name, coalesce(d.score,0) AS total_messages, d.latest
FROM cf_rooms_time_frames tf
INNER JOIN cf_rooms r on r.id = tf.room_id
INNER JOIN(
SELECT cf.room_id, count(*) as score, max(cf.id) as latest
FROM cf_rooms_messages cf
/* line added here */
WHERE cf.room_id = tf.room_id
/* */
AND EXISTS(
SELECT NULL FROM cf_rooms_time_frames tf
WHERE tf.start <= cf.id AND ( tf.end IS NULL OR tf.end >= cf.id )
AND tf.room_id = cf.room_id AND tf.uid = 8
)
GROUP BY cf.room_id
ORDER BY latest
DESC ) d on d.room_id = r.id
WHERE tf.uid = 8
ORDER BY coalesce(latest, score) DESC LIMIT 0, 20
I think the markup explains what the query does.
It searches for "chatrooms" for a given user and orders them by the last message, gets the number of total message which ids are in a given range ( timeframes ), and the last message id.
I don't know why, but the first query returns all rows within the chatmessage table ( cf ) if I can trust EXPLAIN. It delivers the correct results but is kind of slow on a huge table.
I tested the second one with a "hardcoded" room_id and this one was very fast and doesn't "touched" the whole table.

SQL: How to get cells by 2 last dates from 3 different tables?

I have 3 tables (stars mach the ids from the table before):
product:
prod_id* prod_name prod_a_id prod_b_id prod_user
keywords:
key_id** key_word key_prod* kay_country
data:
id dat_id** dat_date dat_rank_a dat_traffic_a dat_rank_b dat_traffic_b
I want to run a query (in a function that gets a $key_id) that outputs all these columns but only for the last 2 dates(dat_date) from the 'data' table for the key_id inserted - so that for every key_word - I have the two last dat_dates + all the other variables included in my SQL query:
So... This is what I have so far. and I don't know how to get only the MAX vars. I tried using "max(dat_date)" in different ways that didn't work.
SELECT prod_id, prod_name, prod_a_id, prod_b_id, key_id, key_word, kay_country, dat_date, dat_rank_a, dat_rank_b, dat_traffic_a, dat_traffic_b
FROM keywords
INNER JOIN data
ON keywords.key_id = data.dat_id
INNER JOIN prods
ON keywords.key_prod = prods.prod_id
Is there a possability to do this with only one query?
EDIT (FOR IgorM):
public function newnew() {
$query = $this->db->query('WITH CTE AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY dat_id ORDER BY dat_date ASC) AS
RowNo FROM data
)
SELECT *
FROM CTE
INNER JOIN keywords
ON keywords.key_id = CTE.dat_id
INNER JOIN prods
ON keywords.key_prod = prods.prod_id
WHERE RowNo < 3
');
$result = $query->result();
return $result;
}
This is the error on the output:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'CTE AS ( SELECT *, ROW_NUMBER() OVER (' at line 1
WITH CTE AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY dat_id ORDER BY dat_date ASC) AS RowNo FROM data ) SELECT * FROM CTE INNER JOIN keywords ON keywords.key_id = CTE.dat_id INNER JOIN prods ON keywords.key_prod = prods.prod_id WHERE RowNo < 3
For SQL
WITH CTE AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY dat_id ORDER BY dat_date ASC) AS
RowNo FROM data
)
SELECT *
FROM CTE
INNER JOIN keywords
ON keywords.key_id = CTE.dat_id
INNER JOIN prods
ON keywords.key_prod = prods.prod_id
WHERE RowNo < 3
For MySQL (not tested)
SET #row_number:=0;
SET #dat_id = '';
SELECT *,
#row_number:=CASE WHEN #dat_id=dat_id THEN #row_number+1 ELSE 1 END AS row_number,
#dat_id:=dat_id AS dat_id_row_count
FROM data d
INNER JOIN keywords
ON keywords.key_id = d.dat_id
INNER JOIN prods
ON keywords.key_prod = prods.prod_id
WHERE d.row_number < 3
The other approach is self joining. I don't want to take credit for somebody else's job, so please look on the following example:
ROW_NUMBER() in MySQL
Look for the following there:
SELECT a.i, a.j, (
SELECT count(*) from test b where a.j >= b.j AND a.i = b.i
) AS row_number FROM test a
If you only want to do this for one key_id at a time (as alluded to in your responses to other answers) and only want two rows, you can just do:
SELECT p.prod_id,
p.prod_name,
p.prod_a_id,
p.prod_b_id,
k.key_id,
k.key_word,
k.key_country,
d.dat_date,
d.dat_rank_a,
d.dat_rank_b,
d.dat_traffic_a,
d.dat_traffic_b
FROM keywords k
JOIN data d
ON k.key_id = d.dat_id
JOIN prods p
ON k.key_prod = p.prod_id
WHERE k.key_id = :key_id /* Bind in key id */
ORDER BY d.dat_date DESC
LIMIT 2;
Whether you want this depends on your data structure and whether there is more than one key/prod combination per date.
Another option limiting just the data rows would be:
SELECT p.prod_id,
p.prod_name,
p.prod_a_id,
p.prod_b_id,
k.key_id,
k.key_word,
k.key_country,
d.dat_date,
d.dat_rank_a,
d.dat_rank_b,
d.dat_traffic_a,
d.dat_traffic_b
FROM keywords k
JOIN (
SELECT dat_id,
dat_date,
dat_rank_a,
dat_rank_b,
dat_traffic_a,
dat_traffic_b
FROM data
WHERE dat_id = :key_id /* Bind in key id */
ORDER BY dat_date DESC
LIMIT 2
) d
ON k.key_id = d.dat_id
JOIN prods p
ON k.key_prod = p.prod_id;
If you want some kind of grouped results for all the keywords, you'll need to look at the other answers.
I think a window function is the best way to go. without knowing a lot about the structure of the data you can try a subquery of what you are trying to restrict and then joining that to the rest of the data. Then within the where clause restrict the rows you pull back.
select p.prod_id, p.prod_name, p.prod_a_id, p.prod_b_id,
t.key_id, t.key_word, t.kay_country, t.dat_date,
t.dat_rank_a, t.dat_rank_b, t.dat_traffic_a, t.dat_traffic_b
from
(
select
k.key_id, k.key_word, k.kay_country, d.dat_date, d.dat_rank_a,
d.dat_rank_b, d.dat_traffic_a, d.dat_traffic_b,
row_number() over (partition by dat_id order by dat_date desc) as 'RowNum'
from keywords as k
inner join
data as d on k.key_id = d.dat_id
) as t
inner join
prods as p on t.key_prod = p.prod_id
where tmp.RowNum <=2
This is a "groupwise max" problem. Reference. CTE does not exist in MySQL.
I'm not totally clear on how your tables are linked, but here is a stab:
SELECT
*
FROM
( SELECT #prev := '', #n := 0 ) init
JOIN
( SELECT #n := if(k.key_id != #prev, 1, #n + 1) AS n,
#prev := k.key_id,
d.*, k.*, p.*
FROM data d
JOIN keywords k ON k.key_id = d.dat_id
JOIN prods p ON k.key_prod = p.prod_id
ORDER BY
k.key_id ASC,
d.dat_date ASC
) x
WHERE n <= 2
ORDER BY k.key_id, n;
you can use this query:
select prod_id, prod_name, prod_a_id, prod_b_id, key_id, key_word,
kay_country, dat_date, dat_rank_a, dat_rank_b, dat_traffic_a, dat_traffic_b
from keywords where dat_date in (
SELECT MAX(dat_date) FROM keywords temp_1
where temp_1.prod_id = keywords.prod_id
union all
SELECT MAX(dat_date) FROM keywords
WHERE dat_date NOT IN (SELECT MAX(dat_date ) FROM keywords temp_2 where
temp_2.prod_id = keywords.prod_id)
)

SQL INNER JOIN returning more rows than I need

This is my situation:
I have 2 tables, tickets and tickets-details. I need to retrieve the info inside "tickets" and just the LAST reply from "tickets-details" for each ticket, then show em in a table. My problem is that "ticket-details" returns a row per each reply and I'm getting more than one row per ticket. How can I achieve this in a single query ?
I tried adding DISTINCT into my SELECT but didn't.
I tried using GROUP BY id_ticket but didnt' work too because I wasn't getting the last reply from ticket-details
This is my query:
SELECT DISTINCT ti.id_ticket,ti.title,tiD.Reply,ti.status
FROM tickets ti
INNER JOIN ticket-details tiD ON ti.id_ticket = tiD.id_ticket
WHERE user = '$id_user' ORDER BY status desc
---------------------------------- EDIT-----------------------------------------------
my tables:
tickets(id_ticket, user, date, title, status)
ticket-details(id_ticketDetail, id_ticket, dateReply, reply)
Assuming that the max id_ticketDetail represents the most recent record in ticket-details you can try
SELECT ti.id_ticket,
ti.title,
tiD.Reply,
ti.status
FROM tickets ti JOIN
(
SELECT id_ticket, reply
FROM `ticket-details` d JOIN
(
SELECT MAX(id_ticketDetail) max_id
FROM `ticket-details`
GROUP BY id_ticket
) q ON d.id_ticketDetail = q.max_id
) tiD ON ti.id_ticket = tiD.id_ticket
WHERE ti.user = '$id_user'
ORDER BY ti.status DESC
or a version with max dateReply
SELECT ti.id_ticket,
ti.title,
tiD.Reply,
ti.status
FROM tickets ti JOIN
(
SELECT d.id_ticket, d.reply
FROM `ticket-details` d JOIN
(
SELECT id_ticket, MAX(dateReply) max_dateReply
FROM `ticket-details`
GROUP BY id_ticket
) q ON d.id_ticket = q.id_ticket
AND d.dateReply = q.max_dateReply
) tiD ON ti.id_ticket = tiD.id_ticket
WHERE ti.user = '$id_user'
ORDER BY ti.status DESC
Here is SQLFiddle demo for both queries.
I do not know Your datatabase model, but if ID is autoincremented you can extend your script with this condition:
SELECT ti.id_ticket,ti.title,tiD.Reply,ti.status
FROM tickets ti
INNER JOIN ticket-details tiD ON ti.id_ticket = tiD.id_ticket
WHERE user = '$id_user'
and tiD.id_ticket in (select max(a.id) from ticket-details a group by a.id_ticket)
ORDER BY status desc
Or if you have some kind of date attribute, change new condition to your date attribute (in my example it is ticked_date )
and tiD.ticked_date in (select max(a.ticked_date) from ticket-details a group by a.id_ticket)
If you need to grab a bunch of tickets with their ticket-details, adding a subquery would work (though it's a little inefficient):
SELECT DISTINCT
ti.id_ticket,ti.title,tiD.Reply,ti.status
FROM tickets ti INNER JOIN ticket-details tiD
ON ti.id_ticket = tiD.id_ticket
WHERE user = '$id_user' AND
tiD = (select id from ticket-details where ticket-details.id_ticket = ti.id_ticket ORDER BY ticket-details.date_field DESC LIMIT 1) ORDER BY status desc
If you only need to do this for one ticket at a time, it's as simple as ordering by the date field in ticket-details and throwing a limit in there:
SELECT DISTINCT
ti.id_ticket,ti.title,tiD.Reply,ti.status
FROM tickets ti INNER JOIN ticket-details tiD
ON ti.id_ticket = tiD.id_ticket
WHERE user = '$id_user' ORDER BY tiD.date_field, status DESC LIMIT 1