1

In MySQL, I'm performing the following query:

SELECT DISTINCT substring_index(gponport, '/', -1) AS ONU
FROM pon
WHERE he = '10.52.8.5'
AND gponport LIKE '16/1/%'
ORDER BY cast(ONU as unsigned)

Where the format of the gponport is (example): 16/1/5

The output of the query is something like:

+------+
| ONU  |
+------+
| 1    |
| 2    |
| 3    |
| 4    |
| 5    |
| 6    |
| 7    |
| 8    |
| 9    |
| 10   |
+------+

I need to retrieve the next onu available in the sequenze. In above example, the expected output is 11.

...but, if the sequence is broken, example:

+------+
| ONU  |
+------+
| 1    |
| 2    |
| 3    |
| 4    |
| 6    |
| 7    |
| 8    |
| 9    |
| 10   |
+------+

Then I need the query to return number 5 instead.

3
  • Then I need the query to return number 5 instead. And if the sequence have no 1 value then you need this value to be returned, is it? Commented Aug 25, 2021 at 10:37
  • I understand that you made sql-related question, but problems like this is much better handled (and controlled) in application layer, e.g. Go/PHP/Nodejs/whatever data programming/processing language you prefer. Commented Aug 25, 2021 at 12:22
  • @TomiL thanks for sharing, but I disagree on your statement that one or the other is "better". This is not necessarily a heavy query, and as such not necessarily better or worse expressed in one place instead of the other. It's your opinion as I see it, not a fact... Commented Aug 28, 2021 at 11:52

3 Answers 3

2

If your value always starts with 1, you can use the row_number() solution (mentioned in the other answer) adapted to your query:

SELECT COALESCE(MIN(CASE WHEN seqnum <> onu THEN seqnum END),
                max(onu)
               )
FROM (SELECT p.*,
             ROW_NUMBER() OVER (ORDER BY onu) as seqnum,
             MAX(onu) OVER () as max_onu
      FROM (SELECT DISTINCT CAST(substring_index(gponport, '/', -1) as unsigned) AS ONU
            FROM pon
            WHERE he = '10.52.8.5' AND
                  gponport LIKE '16/1/%'
           ) p
      ) p
Sign up to request clarification or add additional context in comments.

2 Comments

ty for the answer, the first select I noticed an error , I guess the max is expected to be the second select, not the second argument to min. But additionally we can not use OVER, old version of mysql.. legacy :( (1064): Syntax error near ‘(ORDER BY onu) as seqnum, MAX(onu) OVER () as max_onu FROM ’ at line 3 ty though, I will look into it more to see how I can adopt it - if I can adopt it.
@superhero . . . No, it was wishful coding. I left out the COALESCE().
1

I created an example based on your second sample data. Take a look at it and see if it helps you or motivates you to find the perfect solution:

Mysql window functions

CREATE TEMPORARY TABLE onu
(
    val INT
);

INSERT INTO onu
VALUES (1),
       (2),
       (3),
       (4),
       (6),
       (7),
       (8),
       (9),
       (10);


SELECT rnk
FROM (SELECT val, (row_number() OVER (ORDER BY val)) AS rnk FROM onu) a
WHERE rnk != val
LIMIT 1;

result:

5

4 Comments

ty, but this is almost like the legacy solution I'm already using for the past years, I'm trying to find a "one query" solution for a refactor. still +1 though, it does solve the issue.
@superhero . . . This doesn't return 11 if there are no gaps.
Yes as I said it's not the perfect answer and only works with the second sample. Just to give you some idea
I think Gordon referred to what I wrote, that it "does solve the issue". It's a mistake by me to say it does, but I agree with the author, this example serves a purpose, I should have written what I'm already using, but that's a very ... messy ... piece of query :)
1

Common Table Expressions (recursive queries) together with COALESCE function will do the trick ;)

CREATE TABLE onu
(
    val INT
);

INSERT INTO onu
VALUES (1), (2), (3),
       (4), (6), (7),
       (8), (9), (10);

WITH RECURSIVE CTE AS
(
  -- initial part
  SELECT MIN(val) startonu, MAX(val)+1 endonu
  FROM onu
  -- recursive part
  UNION ALL
  SELECT startonu +1 AS startonu, endonu
  FROM CTE
  WHERE startonu <= endonu
)
SELECT COALESCE(MIN(c.startonu), MAX(c.startonu)) nextval
FROM CTE c
WHERE NOT EXISTS
  (
    SELECT val
    FROM onu o
    WHERE o.val=c.startonu
  );
-- returns 5

Now, add a value of 5 and try above query again. Then it will return a value of 11 ;)

See: db<>fiddle

[EDIT]

+1 in this line SELECT MIN(val) startonu, MAX(val)+1 endonu is surplus.

1 Comment

Have not tried this yet, already implemented a solution in code to be able to move on, but this looks like what I'm looking for. Just worried about possible legacy issues I'm facing with what we are discussing here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.