I have this table
id | TOP | POS
1 | 60 | 1
2 | 50 | 2
3 | 40 | 3
4 | 30 | 4
5 | 20 | 5
6 | 10 | 6
and I need result like this:
id | TOP | POS
1 | 60 | 1
6 | 10 | 6
2 | 50 | 2
5 | 20 | 5
3 | 40 | 3
4 | 30 | 4
Basicaly I need order result by TOP column DESC but between each line put result from order by POS DESC column.
I tried use MOD but result was wrong
SELECT * FROM (
select
#row := #row + 1 as row,
a.*
from table a
order by TOP desc
) t ORDER BY IF(MOD(row, 2) = 1, TOP, POS);
In order to do what you want, you need two sortkeys: the row ordered by TOP and the row ordered by POS. This means tow queries you must join.
Then sort first by the lesser of these two keys (TOP60/POS1 has row #1 for TOP, TOP10/POS6 has row #1 for POS, so these two come first), and then by the TOP key (so TOP60/POS1 has precedence over TOP10/POS6).
select a1.*
from
(
select
#rowtop := #rowtop + 1 as row,
a.*
from mytable a
cross join (select #rowtop := 0) vars
order by top desc
) a1
join
(
select
#rowpos := #rowpos + 1 as row,
a.*
from mytable a
cross join (select #rowpos := 0) vars
order by pos desc
) a2 using(id)
order by least(a1.row, a2.row), a1.row;
REXTESTER DEMO: http://rextester.com/ZKOL97078
Related
I have this structure
ID Value Price
1 a 10
2 b 30
3 c 30
4 d 20
5 e 5
6 f 40
I have a select to get list ordered by price
SELECT * FROM table ORDER BY Price ASC
which gives me
ID Value Price
5 e 5
1 a 10
4 d 20
2 b 30
3 c 30
6 f 40
Now on my web page, a user clicks on an item with ID=2 and wants some details about it. However, on this detail page, I want to show N previous and next items in order. How can I obtain N previous and next items, given the ID and ORDER BY clause?
drop table if exists mytable;
create table mytable (ID serial primary key, Value char(1), Price int);
insert into mytable values
(1,'a',10),
(2,'b',30),
(3,'c',30),
(4,'d',20),
(5,'e',5),
(6,'f',40);
select t2.id, t2.value, t2.price
from (
select id, row_number() over (order by price asc) as rownum
from mytable
) as t1
join (
select *, row_number() over (order by price asc) as rownum
from mytable
) as t2
on t2.rownum between t1.rownum-2 and t1.rownum+2
where t1.id = 2
order by price asc;
Output, tested on MySQL 8.0.29:
+----+-------+-------+
| id | value | price |
+----+-------+-------+
| 1 | a | 10 |
| 4 | d | 20 |
| 2 | b | 30 |
| 3 | c | 30 |
| 6 | f | 40 |
+----+-------+-------+
How can I find a value that has been mentioned several times in a row.
ID |1_Jan|3_Jan|4_Jan|4_Jan|
12 | 2 | 3 | 2 | 4 |
31 | 3 | 4 | 3 | 1 |
25 | 1 | 1 | 1 | 1 |
26 | 3 | 3 | 3 | 3 |
In the case of this table, I want to get out ID 25 and 26 because here the values 1 and 3 have been used 3 or more times in a record.
I was also wondering how can I for example only get out ID 25 even if 26 also has 3 or more?
You can select the rows with all equal column values, and then order by common column value:
with cte as (select t.id, t.1_Jan r, (t.1_Jan = t.2_Jan) and (t.2_Jan = t.3_Jan) and (t.3_Jan = t.4_Jan) val from test_table t)
select c.id from cte c where c.val = 1 order by c.r limit 1;
Output:
id
25
See demo.
This answers the original version of the question.
One way is to unpivot and aggregate:
select id, val, count(*)
from ((select id, 1_jan as val from t) union all
(select id, 2_jan as val from t) union all
(select id, 3_jan as val from t) union all
(select id, 4_jan as val from t)
) t
group by id, val
having count(*) >= 3;
I have table named posts with 3 columns which are id, details, date which contains the following data in ascending order:
+----+----------+-------+
| id | details | date |
+----+----------+-------+
| 1 | details1 | date1 |
| 2 | details2 | date2 |
| 3 | details3 | date3 |
| 4 | details4 | date4 |
+----+----------+-------+
I want to select data in descending order but I want to leave the first row details, like I want to leave row 4th id details, details4, date4, but then I want to select data from id 3 to 2 in, like order by id desc limit 2 but leave the first row from last
You can use the query with ORDER BY DESC LIMIT 1, n
That way, n is the amount of rows you want to fetch, and you're skipping the first row of the result by using LIMIT 1, .
WITH a AS (
SELECT 1 i
UNION ALL
SELECT 2 i
UNION ALL
SELECT 3 i
UNION ALL
SELECT 4 i
)
, b as (
SELECT TOP 1 i FROM a ORDER BY i DESC
)
SELECT *
FROM A
EXCEPT
SELECT * FROM B
ORDER BY i DESC
Basically I need to get only the last 2 records for each user, considering the last created_datetime:
id | user_id | created_datetime
1 | 34 | '2015-09-10'
2 | 34 | '2015-10-11'
3 | 34 | '2015-05-23'
4 | 34 | '2015-09-13'
5 | 159 | '2015-10-01'
6 | 159 | '2015-10-02'
7 | 159 | '2015-10-03'
8 | 159 | '2015-10-06'
Returns (expected output):
2 | 34 | '2015-10-11'
1 | 34 | '2015-09-10'
7 | 159 | '2015-10-03'
8 | 159 | '2015-10-06'
I was trying with this idea:
select user_id, created_datetime,
$num := if($user_id = user_id, $num + 1, 1) as row_number,
$id := user_id as dummy
from logs group by user_id
having row_number <= 2
The idea is keep only these top 2 rows and remove all the others.
Any ideas?
Your idea is close. I think this will work better:
select u.*
from (select user_id, created_datetime,
$num := if(#user_id = user_id, #num + 1,
if(#user_id := id, 1, 1)
) as row_number
from logs cross join
(select #user_id := 0, #num := 0) params
order by user_id
) u
where row_number <= 2 ;
Here are the changes:
The variables are set in only one expression. MySQL does not guarantee the order of evaluation of expressions, so this is important.
The work is done in a subquery, which is then processed in the outer query.
The subquery uses order by, not group by.
The outer query uses where instead of having (actually, in MySQL having would work, but where is more appropriate).
Ok, let's say I have a table with photos.
What I want to do is on a page display the photo based on the id in the URI. Bellow the photo I want to have 10 thumbnails of nearby photos and the current photo should be in the middle of the thumbnails.
Here's my query so far (this is just an example, I used 7 as id):
SELECT
A.*
FROM
(SELECT
*
FROM media
WHERE id < 7
ORDER BY id DESC
LIMIT 0, 4
UNION
SELECT
*
FROM media
WHERE id >= 7
ORDER BY id ASC
LIMIT 0, 6
) as A
ORDER BY A.id
But I get this error:
#1221 - Incorrect usage of UNION and ORDER BY
Only one ORDER BY clause can be defined for a UNION'd query. It doesn't matter if you use UNION or UNION ALL. MySQL does support the LIMIT clause on portions of a UNION'd query, but it's relatively useless without the ability to define the order.
MySQL also lacks ranking functions, which you need to deal with gaps in the data (missing due to entries being deleted). The only alternative is to use an incrementing variable in the SELECT statement:
SELECT t.id,
#rownum := #rownum+1 as rownum
FROM MEDIA t, (SELECT #rownum := 0) r
Now we can get a consecutively numbered list of the rows, so we can use:
WHERE rownum BETWEEN #midpoint - ROUND(#midpoint/2)
AND #midpoint - ROUND(#midpoint/2) +#upperlimit
Using 7 as the value for #midpoint, #midpoint - ROUND(#midpoint/2) returns a value of 4. To get 10 rows in total, set the #upperlimit value to 10. Here's the full query:
SELECT x.*
FROM (SELECT t.id,
#rownum := #rownum+1 as rownum
FROM MEDIA t,
(SELECT #rownum := 0) r) x
WHERE x.rownum BETWEEN #midpoint - ROUND(#midpoint/2) AND #midpoint - ROUND(#midpoint/2) + #upperlimit
But if you still want to use LIMIT, you can use:
SELECT x.*
FROM (SELECT t.id,
#rownum := #rownum+1 as rownum
FROM MEDIA t,
(SELECT #rownum := 0) r) x
WHERE x.rownum >= #midpoint - ROUND(#midpoint/2)
ORDER BY x.id ASC
LIMIT 10
I resolve this by using the below code:
SELECT A.* FROM (
(
SELECT * FROM gossips
WHERE id < 7
ORDER BY id DESC
LIMIT 2
)
UNION
(
SELECT * FROM gossips
WHERE id > 7
ORDER BY id ASC
LIMIT 2
)
) as A
ORDER BY A.id
I don't believe that you can have an "order by" in different sections of a UNION. Could you just do something like this:
SELECT * FROM media where id >= 7 - 4 and id <= 7 + 4 ORDER BY id
I'm agree with the answer suggested by malonso(+1), but if you try it with id= 1, you will get only 5 thumbnails. I don't know if you want this behaviour. If you want always 10 thumbs, you can try:
select top 10 * from media where id > 7 - 4
The problem is that select top is database dependent (in this case is a sql server clause). Other database has similar clauses:
Oracle:
SELECT * media
FROM media
WHERE ROWNUM < 10
AND id > 7 - 4
MySQL:
SELECT *
FROM media
WHERE id > 7 - 4
LIMIT 10
So maybe you can use the last one.
If we do it, we will have the same problem if you want the last 10 thumbs. By example, If we have 90 thumbs and we give an id=88 ... You can solve it adding an OR condition. In MySQL will be something like:
SELECT *
FROM media
WHERE id > 7 - 4
OR (Id+5) > (select COUNT(1) from media)
LIMIT 10
If you're happy to use temp tables, your original query could be broken down to use them.
SELECT
*
FROM media
WHERE id < 7
ORDER BY id DESC
LIMIT 0, 4
INTO TEMP t1;
INSERT INTO t1
SELECT
*
FROM media
WHERE id >= 7
ORDER BY id ASC
LIMIT 0, 6;
select * from t1 order by id;
drop table t1;
Try union all instead. Union requires the server to ensure that the results are unique and this conflicts with your ordering.
I had to solve a similar problem, but needed to account situations where we always got the same number of rows, even if the desired row was near the top or bottom of the result set (i.e. not exactly in the middle).
This solution is a tweak from OMG Ponies' response, but where the rownum maxes out at the desired row:
set #id = 7;
SELECT natSorted.id
FROM (
SELECT gravitySorted.* FROM (
SELECT Media.id, IF(id <= #id, #gravity := #gravity + 1, #gravity := #gravity - 1) AS gravity
FROM Media, (SELECT #gravity := 0) g
) AS gravitySorted ORDER BY gravity DESC LIMIT 10
) natSorted ORDER BY id;
Here's a break down of what's happening:
NOTE: In the example below I made a table with 20 rows and removed ids 6 and 9 to ensure a gap in ids do not affect the results
First we assign every row a gravity value that's centered around the particular row you're looking for (in this case where id is 7). The closer the row is to the desired row, the higher the value will be:
SET #id = 7;
SELECT Media.id, IF(id <= #id, #gravity := #gravity + 1, #gravity := #gravity - 1) AS gravity
FROM Media, (SELECT #gravity := 0) g
returns:
+----+---------+
| id | gravity |
+----+---------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
| 7 | 6 |
| 8 | 5 |
| 10 | 4 |
| 11 | 3 |
| 12 | 2 |
| 13 | 1 |
| 14 | 0 |
| 15 | -1 |
| 16 | -2 |
| 17 | -3 |
| 18 | -4 |
| 19 | -5 |
| 20 | -6 |
| 21 | -7 |
+----+---------+
Next we order all the results by the gravity value and limit on the desired number of rows:
SET #id = 7;
SELECT gravitySorted.* FROM (
SELECT Media.id, IF(id <= #id, #gravity := #gravity + 1, #gravity := #gravity - 1) AS gravity
FROM Media, (SELECT #gravity := 0) g
) AS gravitySorted ORDER BY gravity DESC LIMIT 10
returns:
+----+---------+
| id | gravity |
+----+---------+
| 7 | 6 |
| 5 | 5 |
| 8 | 5 |
| 4 | 4 |
| 10 | 4 |
| 3 | 3 |
| 11 | 3 |
| 2 | 2 |
| 12 | 2 |
| 1 | 1 |
+----+---------+
At this point we have all the desired ids, we just need to sort them back to their original order:
set #id = 7;
SELECT natSorted.id
FROM (
SELECT gravitySorted.* FROM (
SELECT Media.id, IF(id <= #id, #gravity := #gravity + 1, #gravity := #gravity - 1) AS gravity
FROM Media, (SELECT #gravity := 0) g
) AS gravitySorted ORDER BY gravity DESC LIMIT 10
) natSorted ORDER BY id;
returns:
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 7 |
| 8 |
| 10 |
| 11 |
| 12 |
+----+