Make a select with max and min passing condition to each of the two

Make a select with max and min passing condition to each of the two


6

When a post is accessed, I need, in addition to returning the information of this posts, to return the previous one if it exists and the next one.

I would like to know if there is a way to select MAX(id) and MIN(id) in a single query/select, passing a condition for each one of them. Example of what I’m trying to do in Laravel and I’ll write it in SQL to make it easier too

Laravel:

$query = Post::query();
$query = $query->from('posts')->select(DB::raw('MAX(id), MIN(id)'))->whereRaw("id < {$id} and id > {$id}")->first();

SQL:

select MAX(id), MIN(id) from `posts` where id < 5 and id > 5 limit 1

The id variable is the post id value. In this example, it has the value 5. The query I’m doing is to get the MAX and MIN referring to this id, but I also need to get the info of the post that the user accessed.

The DB has post id number 4 and number 6. That is, I need to get the information from posts number 4, 5 and 6 in this example.

The where condition will never be true, but I cannot use or. The first condition is for MAX and the second for MIN. If I use the or, the biggest id will come of the DB.

I need to get the min and max value compared to a value. That is, as I explained above. If the id is 5, I need to get the largest existing id() below that value and I need to get the smallest value above it. In my case, from the information I have in the DB, it would be id 4, 5 and 6

Is it possible in a single consultation or do I really have to do more than one?

Share

5

  • 1

    id < 5 and id > 5 is never TRUE.

    – jarlh

    15 hours ago


  • Yes, but I cannot use or. The first condition is for MAX and the second for MIN. If I use the or. The biggest id will come of the DB

    – Gabriel Edu

    15 hours ago


  • A minimal reproducible example would make this much clearer.

    – jarlh

    15 hours ago

  • 1

    I don't know how to be clearer. I need to get the min and max value compared to a value. That is, as I explained above. If the id is 5, I need to get the largest existing id() below that value and I need to get the smallest value above it. In my case, from the information I have in the DB, it would be id 4, 5 and 6

    – Gabriel Edu

    15 hours ago

  • Sample table data and the expected result – all as formatted text (no images, no links).

    – jarlh

    15 hours ago

3 Answers
3

Reset to default


6

Yes, you can do it with case-when

select MAX(
           CASE
               WHEN id < 5 THEN id
               ELSE NULL
           END
       ), MIN(
           CASE
               WHEN id > 5 THEN id
               ELSE NULL
           END
       )
from `posts` 
where id <> 5

EDIT

Laravel equivalent, as shared by Gabriel Edu in the comment-section:

$query = Post::query();
$query = $query->from('posts')->
         select(DB::raw("MAX(CASE WHEN id < {$id} THEN id ELSE null END), MIN(CASE WHEN id > {$id} THEN id ELSE null END)"))->first(); 

Share

4

  • 1

    Very good, but I didn't know that. It helped a lot. Since this is for Laravel, I'll add it here as I did to help someone else. CODE: $query = Post::query(); $query = $query->from('posts')->select(DB::raw("MAX(CASE WHEN id < {$id} THEN id ELSE null END), MIN(CASE WHEN id > {$id} THEN id ELSE null END)"))->first();

    – Gabriel Edu

    15 hours ago


  • 1

    @GabrielEdu thanks for the Laravel code chunk, added it into my answer. If this answer solved your problem, then you might consider accepting it as the correct answer.

    – Lajos Arpad

    15 hours ago

  • I think that the LIMIT 1 clause could be removed in the query.

    – UltraCommit

    13 hours ago

  • 1

    @UltraCommit yes, your observation is correct, removed it.

    – Lajos Arpad

    13 hours ago


3

The LEAD() and LAG() function in MySQL are used to get preceding and succeeding value of any row within its partition.

Try this:

  SELECT ID,
         LAG (id) OVER (ORDER BY NULL) ONE_SHIFT_FORWARD,
         LEAD (id) OVER (ORDER BY NULL) ONE_SHIFT_BACKWARD
    FROM POSTS
ORDER BY ID ASC;

SELECT *
  FROM (  SELECT ID,
                 LAG (id) OVER (ORDER BY NULL) ONE_SHIFT_FORWARD,
                 LEAD (id) OVER (ORDER BY NULL) ONE_SHIFT_BACKWARD
            FROM POSTS
        ORDER BY ID ASC)
 WHERE id = 5;

Share

2

  • 2

    Very good, in SQL it works, but in Laravel I couldn't reproduce this. I researched, tested, but this way it gives 'ASC or DESC' error. But thanks a lot for the alternative.

    – Gabriel Edu

    15 hours ago

  • Try to remove the ASC word. Write only: ORDER BY ID (without the ASC word, which means ASCENDING).

    – UltraCommit

    13 hours ago



2

You may use lead and lag to access the values before and after the current row.
You may then use those to select the post with a given id and the values before and after in a single select.

The following query

select * 
    from (
    select 
        p.*,
        lead(id) over(order by id) _lead,
        lag(id) over(order by id) _lag
    from post p
) x
where 23 in (id, _lead, _lag);

results in

id text _lead _lag
15 fifteen 23 10
23 twentythree 24 15
24 twentyfour 50 23

With the following setup:

Schema (MySQL v8.0)

create table post (
    id integer,
    text varchar(50)
);
    
insert into post(id, text)
values
( 10, 'ten'),
( 15, 'fifteen'),
( 23, 'twentythree'),
( 24, 'twentyfour'),
( 50, 'fifty');

View on DB Fiddle

Share

1

  • 1

    Very good, in SQL it works, but in Laravel I couldn't reproduce this. I researched, tested, but this way it gives 'ASC or DESC' error. But thanks a lot for the alternative.

    – Gabriel Edu

    15 hours ago



Your Answer


Post as a guest

Required, but never shown


By clicking тАЬPost Your AnswerтАЭ, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged

or ask your own question.

Leave a Reply

Your email address will not be published. Required fields are marked *