I've set-up a simple query to get my menu items ordered by the corresponding language message content, but weirdly enough this doesn't set the row numbers correctly. Here's the query:
SET #idx = 0;
SELECT (#idx:=#idx + 1) AS neworderindex, a.id, a.messagecontent FROM (
SELECT m.id, l.messagecontent
FROM app_menuitem m
INNER JOIN app_language l ON m.id = l.messagekey AND l.locale = 'it'
WHERE m.parent_id = 'some.parent.id'
ORDER BY l.messagecontent
) a;
I tried executing the subquery first and the results are ordered correctly, I just don't know what's going on with the #idx variable.
Use window functions:
SELECT ROW_NUMBER() OVER (ORDER BY l.messagecontent) as neworderindex,
m.id, l.messagecontent
FROM app_menuitem m INNER JOIN
app_language l
ON m.id = l.messagekey AND l.locale = 'it'
WHERE m.parent_id = 'some.parent.id'
ORDER BY l.messagecontent;
This functionality was released in 2016 with MariaDB 10.2.0.
I have the following tables:
Task (id,....)
TaskPlan (id, task_id,.......,end_at)
Note that end_at is a timestamp and that one Task has many TaskPlans. I need to query for the MAX end_at for each Task.
This query works fine, except when you have the same exact timestamp for different TaskPlans. In that case, I would be returned multiple TaskPlans with the MAX end_at for the same Task.
I know this is an unlikely situation, but is there anyway I can limit the number of results for each task_id to 1?
My current code is:
SELECT * FROM Task AS t
INNER JOIN (
SELECT * FROM TaskPlan WHERE end_at in (SELECT MAX(end_at) FROM TaskPlan GROUP BY task_id )
) AS pt
ON pt.task_id = t.id
WHERE status = 'plan';
This works, except in the above situation, how can this be achieved?
Also in the subquery, instad of SELECT MAX(end_at) FROM TaskPlan GROUP BY task_id, is it possible to do something like this so I can use TaskPlan.id for the where in clause?
SELECT id, MAX(end_at) FROM TaskPlan GROUP BY task_id
When I try, it gives the following error:
SQL Error [1055] [42000]: Expression #1 of SELECT list is not in GROUP
BY clause and contains nonaggregated column 'TaskPlan.id' which is not
functionally dependent on columns in GROUP BY clause; this is
incompatible with sql_mode=only_full_group_by
Any explaination and suggestion would be much welcome!
Note on duplicate label: (Now reopened)
I already studied the this question, but it does not provide an answer for my situation where there are multiple max values in the result and it needs to be filtered out to include only one result row per group.
Use the id rather than the timestamp:
SELECT *
FROM Task AS t INNER JOIN
(SELECT tp.*
FROM TaskPlan tp
WHERE tp.id = (SELECT tp2.id FROM TaskPlan tp2 WHERE tp2.task_id = tp.task_id ORDER BY tp2.end_at DESC LIMIT 1)
) tp
ON tp.task_id = t.id
WHERE status = 'plan';
Or use in with tuples:
SELECT *
FROM Task AS t INNER JOIN
(SELECT tp.*
FROM TaskPlan tp
WHERE (tp.task_id, tp.end_at) in (SELECT tp2.task_id, MAX(tp2.end_at)
FROM TaskPlan tp2
GROUP BY tp2.task_id
)
) tp
ON tp.task_id = t.id
WHERE status = 'plan';
If you want to get a list of task ID's with MAX end_at for each, run the query below:
SELECT t.id, MAX(tp.end_at) FROM Task t JOIN TaskPlan tp on t.id = tp.task_id GROUP BY t.id;
EDIT:
Now, I know what exactly you are going to do.
If the TaskPlan table is so big, you can avoid the 'GROUP BY' and run the query below that is very efficient:
SET #first_row := 0;
SET #task_id := 0;
SELECT * FROM Task t JOIN (
SELECT tp.*
, IF(#task_id = tp.`task_id`, #first_row := 0, #first_row := 1) AS temp
, #first_row AS latest_record
, #task_id := tp.`task_id`
FROM TaskPlan tp ORDER BY task_id, end_at DESC) a ON t.task_id = a.task_id AND a.latest_record = 1;
Try this query:
select t.ID , tp1.end_at
from TASK t
left join TASKPLAN tp1 on t.ID = tp1.id
left join TASKPLAN tp2 on t.ID = tp2.id and tp1.end_at < tp2.end_at
where tp2.end_at is null;
SELECT SUM(commission) as regularincome FROM `tbl_member_commission` where mem_id=2 AND MONTH(cdate) = MONTH(CURRENT_DATE())
SELECT SUM(commission) as crowdfund FROM `tbl_member_comm_month` where mem_id=2 AND MONTH(cdate) = MONTH(CURRENT_DATE())
note:- both of tables have these same column names : commission, mem_id, cdate
If this is the only record in each subquery, you can use CROSS JOIN:
select a.regularincome, b.crowdfund
FROM
(SELECT SUM(commission) as regularincome FROM `tbl_member_commission` where mem_id=2 AND MONTH(cdate) = MONTH(CURRENT_DATE())) as a
cross join
(SELECT SUM(commission) as crowdfund FROM `tbl_member_comm_month` where mem_id=2 AND MONTH(cdate) = MONTH(CURRENT_DATE())) as b
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)
)
I am rather new with SQL and I can't for the life of me figure out why I can't do what I'm trying here. I'm trying get the least number of free seats in a package-trip, and group them by the package ID.
The first of the subqueries work, but the second is the one I can't figure out. It says
Unkown column SuperPaket in 'where clause'.
select Paket.PaketID as "SuperPaket", Beskrivning, Resa.AvgångStad, Resa.AvgångTid, Resa.AvgångDatum,
(select AnkomstTid from Resa where Resa.ResID in
(select ResID from PaketResa where PaketResa.PaketID = SuperPaket and Ordningsnr =
(select max(Ordningsnr) from PaketResa where PaketResa.PaketID = SuperPaket))) "AnkomstTid",
(select min(LedigaPlatser) from
(select sum(AntalPlatser - Count) "LedigaPlatser", ResID from(
select Bokning.ResID, AntalPlatser, sum(Bokning.AntalBiljetter) as Count from
(Resa inner join Bokning on Resa.ResID = Bokning.ResID) where Bokning.ResID in
(select PaketResa.ResID from PaketResa where PaketResa.PaketID = SuperPaket)
group by Bokning.ResID order by Count desc)
as CountTable group by ResID)
as T) "LedigaPlatser"
from ((Paket inner join PaketResa on Paket.PaketID = PaketResa.PaketID) inner join Resa on PaketResa.ResID = Resa.ResID) group by Paket.PaketID;
Why does this work for the first subquery but not the second one?
Update..
The error seems to appear when I put a "sub-subquery" in a from clause. I don't know how to rewrite the problematic query to fix this problem though, and I would really appreciate some help. Thanks..
I don't think you can use column alias in a where clause in mysql and oracle
From mysql docs
Standard SQL disallows references to column aliases in a WHERE clause.
This restriction is imposed because when the WHERE clause is
evaluated, the column value may not yet have been determined.
You can use the alias in GROUP BY, ORDER BY, or HAVING clauses to
refer to the column:
I have no idea about sql server
Added :
Your subquery probably not getting Paket.PaketId.
Try modifying from clause of most outer query by adding
(select Paket.PaketID as "SuperPaket" from Paket)
as below
from (
(Paket inner join PaketResa on Paket.PaketID = PaketResa.PaketID)
inner join Resa on PaketResa.ResID = Resa.ResID),
(select Paket.PaketID as "SuperPaket" from Paket)
group by Paket.PaketID;
Also remove alias from first select and just say select SuperPaket
your column is Paket.PaketID not "SuperPaket" so use Paket.PaketID wherever you used superpaket
select Paket.PaketID as "SuperPaket", Beskrivning, Resa.AvgångStad,
Resa.AvgångTid, Resa.AvgångDatum,
(select AnkomstTid from Resa where Resa.ResID in
(select ResID from PaketResa where PaketResa.PaketID = Paket.PaketID and Ordningsnr =
(select max(Ordningsnr) from PaketResa where PaketResa.PaketID = Paket.PaketID))) "AnkomstTid",
(select min(LedigaPlatser) from
(select sum(AntalPlatser - Count) "LedigaPlatser", ResID from(
select Bokning.ResID, AntalPlatser, sum(Bokning.AntalBiljetter) as Count from
(Resa inner join Bokning on Resa.ResID = Bokning.ResID) where Bokning.ResID in
(select PaketResa.ResID from PaketResa where PaketResa.PaketID = Paket.PaketID)
group by Bokning.ResID order by Count desc)
as CountTable group by ResID)
as T) "LedigaPlatser"
from ((Paket inner join PaketResa on Paket.PaketID = PaketResa.PaketID) inner join Resa on PaketResa.ResID = Resa.ResID) group by Paket.PaketID;