A range within another range

1

I have a problem and I'm looking for the best way to resolve it.

I have Min and Max values coming from a database: Ex:

 id      Min    Max
 1        5     10
 2       20     25
 3       60     90 
 4       91     99

If you enter a new Min and Max it should not overlap a range that has already been set eg from 11-19 is allowed, from 24-50 not because it would enter the 20-25 range.

Any ideas how to solve something like this?

    
asked by anonymous 04.06.2017 / 23:31

1 answer

3

Let's break down your problem into parts:

  • Avoid entering a value if any conditions occur
  • Identify intersection existence in real number ranges
  • So we can treat each point independently independently.

    Conditioned insertion

    A conditional insert is a insert with a conditional clause; I have already seen insert-select being used for this purpose, conditioning the insertion to the where , join or similar things of select or CTE . Removing these alternatives, I do not know any other way to do a conditional insert using DML only. Since you have not specified DBMS, I am not going to use any trigger or procedure for this insertion here.

    A conditional insert is similar to insertion with values, but instead of specifying a tuple with values to insert, the return of a query will be inserted.

    For example, imagine a report that depends on a very heavy query. To not run the query multiple times, store its result in a table:

    INSERT INTO relatorio_bacana (mes_inicio, mes_fim, total)
    SELECT
        '03' AS mes_inicio,
        '04' AS mes_fim,
        sum(d.vr) AS total
    FROM
        dados d
    WHERE
        '03' <= d.mes AND
        d.mes < '04'
    

    In general terms, this is an insert with selection. Note that a conditional has been placed in the selection: the month column must be between the start and end months. Note that it is possible to decrease the verbosity of constant terms inserted: we do not need to write twice the start month, nor twice the end month; we can write each one just once, using a CTE.

    WITH q AS (
        SELECT
            '03' AS mes_inicio,
            '04' AS mes_fim
    )
    INSERT INTO relatorio_bacana (mes_inicio, mes_fim, total)
    SELECT
        q.mes_inicio AS mes_inicio,
        q.mes_fim AS mes_fim,
        sum(d.vr) AS total
    FROM
        dados d, q
    WHERE
        q.mes_inicio <= d.mes AND
        d.mes < q.mes_fim
    

    Intersection detection between ranges

    There are 7 possible relationships between two ranges:

  • Identical:

    <--->
    <--->
    
  • The first contains the second in its own way:

    <----------->
         <----->
    
  • The first is contained in the second in its own way:

          <--->
    <---------->
    
  • Intersection at the beginning of the first:

          <----->
    <------->
    
  • Intersection at the end of the first:

    <------>
          <------->
    
  • No intersection, strictly minor:

     <--->
                   <----->
    
  • No intersection, strictly greater:

                      <--->
    <--->
    
  • As we are working with closed intervals (this was my understanding of the question), it is only necessary that one of the ends coincides so that there is pertinence (minor with major / minor with major) or partial intersection with less). If you mix the opening of the ends, you would need more care.

    Be m the smallest value in the range and M the largest value in the range. Be m1 and M1 relative to the first interval, and analogously m2 and M2 to the second.

    If m1 is in (m2,M2) then we have a case of its own pertinence (3) or intersection at the beginning of the interval (4). Since we are not interested in knowing what kind of relationship exists between the two ranges, that is enough.

    If m2 is in (m1,M1) then we have a case of our own pertinence (2) or intersection at the end of the interval (5).

    Consideration with closed intervals ensures extreme cases of intersections and equality of intervals. Therefore, in order to have some form of intersection (cases 1 to 5 of the 7 possible relations), the following relation must be satisfied:

    /* m1 dentro de [m2,M2] para casos 1,3,4 */
    (m2 <= m1 E m1 <= M2)
    
        OU
    
    /* m2 dentro de [m1,M1] para casos 1,2,5 */
    (m1 <= m2 E m2 <= M1)
    

    In sql, if the table is called from the table:

    SELECT
        t1.id,
        t2.id
    FROM
        tabela t1,
        tabela t2
    WHERE
        (t2.min <= t1.min AND t1.min <= t2.max)
             OR
        (t1.min <= t2.min AND t2.min <= t1.max)
    

    Thus, for each intersection in the tabela table, the ids of the lines that generated this intersection will be printed.

    Putting it all together

    We want to insert a new line only if it does not intersect a previously existing one (considering the auto generated id):

    WITH novos_dados AS (
        SELECT
            11 AS min,
            19 AS max
    )
    INSERT INTO tabela (min, max)
    SELECT
         n.min,
         n.max
     FROM
         novos_dados n
     WHERE NOT EXISTS (
          SELECT
              1
          FROM
               tabela t
          WHERE
              (t.min <= n.min AND n.min <= t.max)
                   OR
              (n.min <= t.min AND t.min <= n.max)
     )
    
        
    05.06.2017 / 06:02