MySQL: Verfügbarkeit über einen bestimmten Zeitraum

Saturnus

Grünschnabel
Hallo,

Ich habe Bestellungen in einer Tabelle gespeichert, die über ein Datum von / Datum bis verfügen (Mietobjekte).
Von jeden Mietobjekt gibt es eine max. verfügbare Anzahl.
Nun möchte ich bei einer neuen Bestellung prüfen, ob für den Zeitraum (von/ bis) des gewünschten Objekts noch genügend vorhanden sind.
Wie kann ich die gesamte Anzahl erhalten im Abgleich des Zeitraums den der User eingibt. Überschneidungen müssten berücksichtigt werden.
Kommt hier eine Abfrage mit "BETWEEN" zum Zug oder ">=" und "<="? Ich erhalte verschiedene Angaben...
Wäre für Tipps dankbar!
 

Yaslaw

alter Rempler
Moderator
OK, mein Tutorial als Grundlage.
SQL:
with used as ( 
      select p.*
    from periodes p, input i
    where i.item_id = p.item_id
        and LEAST(p.end, i.end) >= GREATEST(p.start, i.start)
)
select count(*) < it.quantity
from used u, items it
where u.item_id = it.item_id
group by it.quantity

Meine Testdaten, da ich ja keine Ahnung habe wie das Zeug bei dir aufgebaut ist und heisst
SQL:
-- Periode die geprüft werden soll
CREATE TABLE input
    (`item_id` int, `start` datetime, `end` datetime)
;
INSERT INTO input (`item_id`, `start`, `end`)
VALUES (1, '2022-12-12', '2022-12-12');

-- Definition der Objekte und ihre Anzahl
CREATE TABLE items
    (`item_id` int, `item` varchar(1), `quantity` int)
;
    
INSERT INTO items (`item_id`, `item`, `quantity`)
VALUES
    (1, 'A', 2),
    (2, 'B', 1)
;

-- Besetzte Perioden
CREATE TABLE periodes
    (`id` int, `item_id` int, `start` datetime, `end` datetime)
;
    
INSERT INTO periodes (`id`, `item_id`, `start`, `end`)
VALUES
    (1, 1, '2022-12-01', '2022-12-05'),
    (2, 1, '2022-12-02', '2022-12-20'),
    (3, 2, '2022-11-13', '2022-12-02'),
    (4, 2, '2022-12-03', '2022-12-15'),
    (5, 1, '2022-12-13', '2022-12-14')
;
 

Saturnus

Grünschnabel
Wie muss ich das Beispiel umschreiben, wenn der User ein "von/bis" Datum eingibt und daraus eine Gesamt Summe entstehen soll (MySQL)? Ich muss ja prüfen, wie viele Objekte für den genannten Zeitraum bereits bestellt sind.
 

Yaslaw

alter Rempler
Moderator
Das ist im With-Part des SQL Statements.
Alle Ausleihen in der Zeit (Tabelle Input)
SQL:
      select p.*
    from periodes p, input i
    where i.item_id = p.item_id
        and LEAST(p.end, i.end) >= GREATEST(p.start, i.start)
 

Zvoni

Erfahrenes Mitglied
Ich versuche gerade mein Hirn zu verbiegen, um zu verstehen wie das funktioniert.
Mir ist klar, das LEAST/GREATEST im Prinzip pivotierte MIN/MAX sind.
Aber die Logik entzieht sich mir gerade.... :)
 

Yaslaw

alter Rempler
Moderator
Hast recht. Kommt drauf an, ob man eine Berührung als Schnitt bezeichnen will oder nicht. Wenn nicht, is es nur LEAST(p.end, i.end) > GREATEST(p.start, i.start)

Grob gesagt. Wenn das Kleinste Ende der beiden Perioden grösser ist als der Grösste Start, dann überschneiden sie sich.
Hier mal die grafische Ansicht dazu
1670582354704.png
 

Zvoni

Erfahrenes Mitglied
Irgendwas stimmt da nicht.
Da ich kein MySQL habe, hab ichs versucht in SQLite nachzubilden (bitte korrigieren, falls ein Fehler)
SQL:
with used as ( 
      select p.*
    from periodes p, input i
    where i.item_id = p.item_id and 
    (case when p.end<i.end then p.end else i.end end) >=(case when p.start>i.start then p.start else i.start end)
)
select count(*) < it.quantity
from used u, items it
where u.item_id = it.item_id
group by it.quantity
ergibt für Yaslaws Beispieldaten 1 (Korrekt)
Änder ich aber die Menge in Item A von 2 auf 3, bekomme ich immer noch als Ergebnis 1
Hä???? Müsste doch zwei heraus kommen.
 

Yaslaw

alter Rempler
Moderator
Mein Resultat ist ein A>B. Gibt also nur True oder False zurück und nicht die Restmenge 0=>keine mehr Frei, 1=>Hat noch Items. Um die Restmenge zu berechnen nimm select it.quantity - count(*) ...