I have a table containing 392871961 rows like this.
p_id r_p_id weight
1 2 0.223923923
1 3 0.12923923
1 4 0.423926924
1 5 0.143921921
1 6 0.323923922
1 7 0.223923728
.
.
.
2 1 0.123923921
2 3 0.023923922
2 4 0.223923926
2 5 0.323923928
2 6 0.223923921
2 7 0.423923921
.
.
.
3 1 0.023923925
3 2 0.223923922
3 4 0.123923926
3 5 0.223923929
3 6 0.123923921
3 7 0.523923922
.
.
.
p_id goes up to a little over 6000 digit. weight represents some kind of priority between p_id and r_p_id.
I have two queries like this. A user is going to provide list of p_ids. It would be less than 10 p_ids.
first query = select r_p_id, weight from table where p_id in (....) order by weight
second query = select r_p_id, (weight*0.8) as m_weight from table where p_id in (....) order by (weight * 0.8)
I would like to combine these queries to return the r_p_id in ascending order by the sum of (weight from the first query) + (weight from the second query).
Can anybody tell me how to do it?
I'd appreciated it.
Try this
select t1.r_p_id, (t1.weight+t2.m_weight) as total_weight
from
(select r_p_id, weight from table where p_id in (....) ) as t1,
(select r_p_id, (weight*0.8) as m_weight from table where p_id in (....)) as t2
where t1.r_p_id=t2.r_p_id
order by (t1.weight+t2.m_weight)
OR using JOIN keyword
select t1.r_p_id, (t1.weight+t2.m_weight) as total_weight
from
(select r_p_id, weight from table where p_id in (....) ) as t1 INNER JOIN
(select r_p_id, (weight*0.8) as m_weight from table where p_id in (....)) as t2 ON t1.r_p_id=t2.r_p_id
order by (t1.weight+t2.m_weight)
You'l need to use JOIN between results of two queries.
EDIT:
Since MySQL supports order by [alias-name] as dg99 suggested, you can use directly order by total_weight
select t1.r_p_id, (t1.weight+t2.m_weight) as total_weight
from
(select r_p_id, weight from table where p_id in (....) ) as t1,
(select r_p_id, (weight*0.8) as m_weight from table where p_id in (....)) as t2
where t1.r_p_id=t2.r_p_id
order by total_weight
Um, maybe I'm missing something, but wouldn't it just be the following?
select r_p_id, weight, weight*0.8 AS m_weight from table where p_id in (....) order by weight
Mathmatically, you'll get the same order whether you do "order by weight" or "order by weight + 0.8*weight"
select r_p_id, weight, (weight*0,8) as m_weight, (weight*1.8) as total_weight
from table where p_id in (...) order by total_weight asc
weight + weight * .8 = weight * 1.8, so there's a quick sum of that column. Which of the weight columns you order by doesn't matter because they are all the same order
Related
I have two tables
tbl1:
id
name
tid
1
some text
1
tbl2:
tid
level
related_id
1
1
4
1
2
5
1
3
6
I want to join tbl1 to tbl2 on tbl1.tid = tbl2.tid, I only want one row joined from tbl2 based on the level for example I want the least level first that is level 1 row joined
joined table
id
name
tid
level
related_id
1
some text
1
1
4
is it possible to achieve this?
Try the following CTE, noting that row_number() function is supported only with MySQL v8.0 and higher.
with cte as
(select
tbl1.id, tbl1.name, tbl2.tid, tbl2.level_, tbl2.related_id
, row_number() over (partition by tbl1.id order by level_) as rn
from tbl1 inner join tbl2
on tbl1.tid=tbl2.tid)
select cte.id, cte.name, cte.tid, cte.level_, cte.related_id
from cte where rn=1
See the result from db-fiddle.
In comments, I suggested to use the min(level) over (partition by tid) function in a where clause, but this will not give the required results unless the level field is unique, where it's not the case as I guess.
Structure is:
CREATE TABLE current
(
id BIGINT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
symbol VARCHAR(5),
UNIQUE (id), INDEX (symbol)
) ENGINE MyISAM;
id
symbol
1
A
2
B
3
C
4
C
5
B
6
A
7
C
8
C
9
A
10
B
I am using the following
SELECT *
FROM current
WHERE id
IN
(
SELECT MAX(id)
FROM current
GROUP BY symbol
)
to return the last records in a table.
id
symbol
8
C
9
A
10
B
How can I return the next-to-last results in a similar fashion?
I know that I need
ORDER BY id DESC LIMIT 1,1
somewhere, but my foo is weak.
I would want to return
id
symbol
5
B
6
A
7
C
For versions of MySql prior to 8.0, use a subquery in the WHERE clause to filter out the max id of each symbol and then aggregate:
SELECT MAX(id) id, symbol
FROM current
WHERE id NOT IN (SELECT MAX(id) FROM current GROUP BY symbol)
GROUP BY symbol
ORDER BY id;
See the demo.
SELECT *
FROM current
WHERE id IN (
SELECT DISTINCT T.id FROM current AS T
WHERE id=(
SELECT id FROM current
WHERE symbol=T.symbol
ORDER BY id DESC LIMIT 1,1
)
)
Easy if your MySql can use ROW_NUMBER. (MySql 8)
Just make it sort descending, then take the 2nd.
WITH CTE AS (
SELECT *
, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY id DESC) AS symbol_rn
FROM current
)
SELECT id, symbol
FROM CTE
WHERE symbol_rn = 2
ORDER BY id;
In MySql 7.5 you can simply self-join on the symbol, and group by.
Then the 2nd last will have 1 higher id.
SELECT c1.id, c1.symbol
FROM current c1
LEFT JOIN current c2
ON c2.symbol = c1.symbol
AND c2.id >= c1.id
GROUP BY c1.id, c1.symbol
HAVING COUNT(c2.id) = 2
ORDER BY c1.id;
id
symbol
5
B
6
A
7
C
db<>fiddle here
The performance will really benefit from an index on symbol.
You can try this;
SELECT *
FROM current
WHERE id
IN (SELECT MAX(id)
FROM current
GROUP BY symbol)
ORDER BY id DESC LIMIT 1,3
limit 1,3 says; get the last 3 results excluding the last result. You can change the numbers.
How to get the MAX value in every albumID(45, 12, 22, 8) in the following table?
I tried with this query.
But it returned me the first value, not max value.(3, 6, 5, 6)
SELECT
*
FROM
(
SELECT
*
FROM
contentnew
WHERE
genreID = 1
ORDER BY
albumID DESC,
reg_count DESC
) AS newTB
GROUP BY
albumID;
Look this
If I use the
Once you group by, you can apply aggregate functions such as max on each group. In your example try:
SELECT albumID, max(reg_count) as max_count
FROM contentnew
GROUP BY albumID
This will project each albumID with the max_count in the group. In the select statement you can only use aggregate functions. The reason why we are able to project (or print) albumID is because this is the column we grouped by.
Following comments:
SELECT *
FROM contentnew as c1
WHERE c1.reg_count < (
SELECT max(c2.reg_count)
FROM contentnew as c2
WHERE c1.albumID = c2.albumID
GROUP BY c2.albumID)
You can try
select max(reg_count) from contentnew group by albumID
You are almost there, one thing that might be helpful is to use row_number() function, if you want every column from the table.
with contentnew_test
as
(
select row_number() over (partition by albumId order by reg_count desc) row
,* from
contentnew
)
select * from contentnew_test where row = 1 order by reg_count desc;
I used this as a reference as not sure about the syntax
https://www.mysqltutorial.org/mysql-window-functions/mysql-row_number-function/
Subquery will give you a result set something like this:
row albumId reg_count ...
1 1 8 ...
2 1 7 ...
3 1 3 ...
4 1 1 ...
1 2 22 ...
2 2 9 ...
3 2 6 ...
4 2 1...and so on.
I have the following data inside a table:
id person_id item_id price
1 1 1 10
2 1 1 20
3 1 3 50
Now what I want to do is group by the item ID, select the id that has the highest value and take the price.
E.g. the sum would be: (20 + 50) and ignore the 10.
I am using the following:
SELECT SUM(`price`)
FROM
(SELECT id, person_id, item_id, price
FROM `table` tbl
INNER JOIN person p USING (person_id)
WHERE p.person_id = 1
ORDER BY id DESC) x
GROUP BY item_id
However, this query is still adding (10 + 20 + 50), which is obviously not what I need to have.
Any ideas to where I am going wrong?
Here is what you are trying to achieve. First you need grouping in a subquery and not in outer query. In outer query you need only sum:
SELECT SUM(`price`)
FROM
(SELECT MAX(price) as price
FROM `table` tbl
INNER JOIN person p USING (person_id)
WHERE p.person_id = 1
GROUP BY item_id) x
http://sqlfiddle.com/#!9/40803/5
SELECT SUM(t1.price)
FROM tbl t1
LEFT JOIN tbl t2
ON t1.person_id= t2.person_id
AND t1.item_id = t2.item_id
AND t1.id<t2.id
WHERE t1.person_id = 1
AND t2.id IS NULL;
I'm not sure if this is the only requirement you have. If so, try this.
SELECT SUM(price)
FROM
(SELECT MAX(price)
FROM table
WHERE person_id = 1
GROUP BY item_id)
First of all - you don't need the person table, because the other table already contains the person_id. So i removed it from the examples.
Your query returns a sum of prices for each item.
If you replace SELECT SUM(price) with SELECT item_id, SUM(price) you wil get
item_id SUM(`price`)
1 30
3 50
But that is not what you want. Neither is it what you wrote in the question " (10 + 20 + 50)".
Now replacing the first line with SELECT id, item_id, SUM(price) you will get one row for each item with the highest id.
id item_id price
2 1 20
3 3 50
This works because of the "undocumented feature" of MySQL, wich allows you to select columns that are not listed in the GROUP BY clause and get the first row from the subselect each group (each item in this case).
Now you only need to sum the price column in an additional outer select
SELECT SUM(price)
FROM (
SELECT id, item_id ,price
FROM (
SELECT id, person_id, item_id, price
FROM `table` tbl
WHERE tbl.person_id = 1
ORDER BY id DESC ) x
GROUP BY item_id
) y
However i do not recomend to use that "feature". While it still works on MySQL 5.6, you never know if that will work with newer versions. It already doesn't work on MariaDB.
Instead you can determite the MAX(id) for each item in an subselect, select only the rows with the determined ids and get the summed price of them.
SELECT SUM(`price`)
FROM `table` tbl
WHERE tbl.id IN (
SELECT MAX(tbl2.id)
FROM `table` tbl2
WHERE tbl2.person_id = 1
GROUP BY tbl2.item_id
)
Another solution (wich internaly does the same) is
SELECT SUM(`price`)
FROM `table` tbl
JOIN (
SELECT MAX(tbl2.id) as id
FROM `table` tbl2
WHERE tbl2.person_id = 1
GROUP BY tbl2.item_id
) x ON x.id = tbl.id
Alex's solution also works fine, if the groups (number of rows per person and item) are rather small.
You have used group by in main query, but it is on subquery like
SELECT id, person_id, item_id, SUM(`price`) FROM ( SELECT MAX(price) FROM `table` tbl WHERE p.person_id = 1 GROUP BY item_id ) AS x
I have this table
id fruit
---------
1 apple
2 banana <--
3 apple
4 apple
5 apple
6 apple
7 banana <----
8 apple
9 banana
10 apple
And I want to select rows until 2 bananas are found, like
SELECT id FROM table_fruit UNTIL number_of_bananas = 2
So the result would be 1,2,3,4,5,6,7
How could I achieve this?
thanks
I wish I could give credits to all of you who answered my question. I'v tested all of them, and they all work perfectly (got the expected result).
Though answers of Devart and ypercube seem a little bit complex and difficult for me to understand.
And since AnandPhadke was the first one provided a working solution, I'll choose his answer as accepted.
You guys are awesome, thanks!
Try this query -
SELECT id, fruit FROM (
SELECT
b.*, #b:=IF(b.fruit = 'banana', 1, 0) + #b AS banana_number
FROM
bananas b,
(SELECT #b := 0) t
ORDER BY id) t2
WHERE
banana_number < 2 OR banana_number = 2 AND fruit = 'banana'
SQLFiddle demo
select * from tables where id <=
(
select id from (
select id from tables where fruit='banana'
order by id limit 2) a order by id desc limit 1
)
SQLFIDDLE DEMO
#Devart's answer is perfect but it's an alternative option to we can use:
SELECT * FROM table_fruit WHERE id <=
(
SELECT id FROM
(SELECT id FROM table_fruit WHERE fruit='banana' ORDER BY id LIMIT 2) a
ORDER BY ID DESC LIMIT 1
);
Or using MAX
SELECT * FROM table_fruit WHERE id <=
(
SELECT MAX(id) FROM
(SELECT id FROM table_fruit WHERE fruit='banana' ORDER BY id LIMIT 2) a
);
See this SQLFiddle
select * from table_fruit where id <=
(
select max(id) from
(select id from table_fruit where fruit='banana' order by id limit 2) t
)
If there are less than 2 rows with 'banana', this will return all rows of the table:
SELECT t.*
FROM table_fruit AS t
JOIN
( SELECT MAX(id) AS id
FROM
( SELECT id
FROM table_fruit
WHERE fruit = 'banana'
ORDER BY id
LIMIT 1 OFFSET 1
) AS lim2
) AS lim
ON t.id <= lim.id
OR lim.id IS NULL ;