MySQL ile kullanılabilirliği (PHP) aranıyor?

2 Cevap php

Ben iki MySQL icar birimleri ve rezervasyon temsil (MyISAM) tabloları vardır:

  • LettingUnits (ID, Name, vs ..)
  • LettingUnitBookings (ID, F_LU_ID, Start, End)

F_LU_ID birimine bir yabancı anahtar nerede.

Belirli bir zaman diliminde mevcut birimler aramak için en iyi yolu nedir? Arama Başlat, End ve Süresi geçirilir.

  • Rezervasyon = eski başlangıcını başlayın
  • Rezervasyon sonu = Son uç
  • Rezervasyon süresi = Süre

Ben, bu MySQL bunu yapmak bile mümkün olmadığını bilmek ilgi duyarım ancak eğer değil o zaman PHP bunu yapmak için en iyi yoldur.

Example

Aşağıdaki cevaplar cevap ben bir örnek sorunu açıklamaya yardımcı olacaktır hissediyorum.

A LettingUnit:

  • (123, "Foo Cottage")

Bazı LettingUnitBookings:

  • (400, 123, 01/01/09, 05/01/09) - 5 günlük bir rezervasyon
  • (401, 123, 10/01/09, 20/01/09) - 10 günlük rezervasyon
  • (402, 123, 25/01/09, 30/01/09) - 5 günlük bir rezervasyon

: Biz ararsanız

  • Start = 01/01/09
  • End = 01/02/09
  • Süre = 5 (gün)

Sonra birim göstermek istiyorum. Arama aralığında 5 günlük bir rezervasyon için durumu var çünkü.

Süresi 10 hayır ise 10 ardışık unbooked gün arama aralığında olduğu gibi daha sonra birim görünmeyecektir.

2 Cevap

Burada çalışmak gibi görünüyor bir çözüm:

SELECT t.*, DATEDIFF(t.LatestAvailable, t.EarliestAvailable) AS LengthAvailable
FROM 
   (SELECT u.*,
      COALESCE(b1.End, @StartOfWindow) AS EarliestAvailable,
      COALESCE(b2.Start, @EndOfWindow) AS LatestAvailable
    FROM LettingUnits u
    LEFT OUTER JOIN LettingUnitBookings b1
      ON (u.ID = b1.F_LU_ID AND b1.End BETWEEN @StartOfWindow AND @EndOfWindow)
    LEFT OUTER JOIN LettingUnitBookings b2
      ON (u.ID = b2.F_LU_ID AND b2.Start BETWEEN @StartOfWindow AND @EndOfWindow
          AND b2.Start >= b1.End) -- edit: new term
    ) AS t
LEFT OUTER JOIN LettingUnitBookings x
  ON (t.ID = x.F_LU_ID AND x.Start < t.LatestAvailable AND x.End > t.EarliestAvailable)
WHERE x.ID IS NULL AND DATEDIFF(t.LatestAvailable, t.EarliestAvailable) >= @WindowSize;

Çıktısı:

+-----+-------------+-------------------+-----------------+-----------------+
| ID  | Name        | EarliestAvailable | LatestAvailable | LengthAvailable |
+-----+-------------+-------------------+-----------------+-----------------+
| 123 | Foo Cottage | 2009-01-05        | 2009-01-10      |               5 |
| 123 | Foo Cottage | 2009-01-20        | 2009-01-25      |               5 |
| 456 | Bar Cottage | 2009-01-20        | 2009-01-31      |              11 |
+-----+-------------+-------------------+-----------------+-----------------+

EXPLAIN ile bu analiz oldukça iyi dizinleri istihdam olduğunu göstermektedir:

+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------------------------+
| id | select_type | table      | type   | possible_keys | key     | key_len | ref   | rows | Extra                   |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------------------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL          | NULL    | NULL    | NULL  |    9 | Using where             |
|  1 | PRIMARY     | x          | ref    | F_LU_ID       | F_LU_ID | 8       | t.ID  |    2 | Using where; Not exists |
|  2 | DERIVED     | u          | system | NULL          | NULL    | NULL    | NULL  |    1 |                         |
|  2 | DERIVED     | b1         | ref    | F_LU_ID       | F_LU_ID | 8       | const |    0 |                         |
|  2 | DERIVED     | b2         | ref    | F_LU_ID       | F_LU_ID | 8       | const |    0 |                         |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------------------------+

given @ martin Clayton tarafından çözüm için EXPLAIN raporu ile karşılaştırın:

+----+--------------+---------------------+--------+---------------+---------+---------+------+------+---------------------------------+
| id | select_type  | table               | type   | possible_keys | key     | key_len | ref  | rows | Extra                           |
+----+--------------+---------------------+--------+---------------+---------+---------+------+------+---------------------------------+
|  1 | PRIMARY      | lu                  | system | PRIMARY,ID    | NULL    | NULL    | NULL |    1 |                                 |
|  1 | PRIMARY      | <derived2>          | ALL    | NULL          | NULL    | NULL    | NULL |    4 | Using where                     |
|  2 | DERIVED      | <derived3>          | ALL    | NULL          | NULL    | NULL    | NULL |    4 | Using temporary; Using filesort |
|  2 | DERIVED      | <derived5>          | ALL    | NULL          | NULL    | NULL    | NULL |    4 | Using where; Using join buffer  |
|  5 | DERIVED      | LettingUnitBookings | ALL    | NULL          | NULL    | NULL    | NULL |    3 |                                 |
|  6 | UNION        | LettingUnitBookings | index  | NULL          | F_LU_ID | 8       | NULL |    3 | Using index                     |
| NULL | UNION RESULT | <union5,6>          | ALL    | NULL          | NULL    | NULL    | NULL | NULL |                                 |
|  3 | DERIVED      | LettingUnitBookings | ALL    | NULL          | NULL    | NULL    | NULL |    3 |                                 |
|  4 | UNION        | LettingUnitBookings | index  | NULL          | F_LU_ID | 8       | NULL |    3 | Using index                     |
| NULL | UNION RESULT | <union3,4>          | ALL    | NULL          | NULL    | NULL    | NULL | NULL |                                 |
+----+--------------+---------------------+--------+---------------+---------+---------+------+------+---------------------------------+

Bu performans katiller vardır, çünkü genel olarak, Using filesort veya Using temporary zorlamak optimizasyon planlarını önlemek istiyor. Kullanan bir sorgu GROUP BY en azından MySQL, optimizasyon bu tür neden neredeyse kesindir.

Bu hoş değil.

  • Kendisine LettingUnitBookings tarihi
  • Her F_LU_ID için rezervasyon arasındaki boşlukların başlangıç ​​ve bitiş bul
  • Mevcut 'slot' - boşlukların boyutunu al
  • 'Dirsek' uygun bir yuva, bu için Aykırı tarihleri ​​eklemek varolan hiçbir rezervasyon vardır durum düşünün
  • LettingUnits masaya o projeksiyon katılın ve WHERE kriterleri (başlangıç, bitiş, süre) uygulanır

Ben hiç rezervasyonlar sahip BookingUnits eklemeyi ihmal ettik.

Bu gibi bakıyor biter:

SELECT @StartOfWindow := '2009-01-01',
       @EndOfWindow   := '2009-02-01',
       @WindowSize    := 5
;

SELECT
    lu.Name,
    Slots.*
FROM (
    SELECT
        lub1.F_LU_ID,
        DATE_ADD( MAX( lub2.date_time ), INTERVAL 1 DAY )     AS StartOfSlot,
        DATE_SUB( lub1.date_time, INTERVAL 1 DAY )            AS EndOfSlot,
        DATEDIFF( lub1.date_time, MAX( lub2.date_time ) ) - 1 AS AvailableDays
    FROM
    ( SELECT F_LU_ID, Start AS date_time FROM LettingUnitBookings
      UNION
      SELECT F_LU_ID, CAST( '9999-12-31' AS DATE ) FROM LettingUnitBookings
    ) AS lub1,
    ( SELECT F_LU_ID, End   AS date_time FROM LettingUnitBookings
      UNION
      SELECT F_LU_ID, CAST( '1000-01-01' AS DATE ) FROM LettingUnitBookings
    ) AS lub2
    WHERE
        lub2.date_time <= lub1.date_time
    AND lub2.F_LU_ID = lub1.F_LU_ID
    GROUP BY
        lub1.F_LU_ID,
        lub1.date_time
    ) Slots
JOIN LettingUnits lu
ON   lu.ID = Slots.F_LU_ID
WHERE
    Slots.AvailableDays >= @WindowSize
AND (
   (     DATEDIFF( Slots.EndOfSlot, @EndOfWindow )     >= @WindowSize
     AND DATEDIFF( @StartOfWindow, Slots.StartOfSlot ) >= @WindowSize
   )
   OR
   (     DATEDIFF( @EndOfWindow, Slots.StartOfSlot ) >= @WindowSize
     AND DATEDIFF( Slots.EndOfSlot, @StartOfWindow ) >= @WindowSize
   )
)

Verir

Name        F_LU_ID StartOfSlot EndOfSlot  AvailableDays
Foo Cottage 123     2009-01-06  2009-01-09 5
Foo Cottage 123     2009-01-21  2009-01-24 5

Umarım senin ihtiyaçlarına göre adapte edilebilir.

Rezervasyon önceki rezervasyon biter aynı gün başlayacak eğer Alternatif olarak, biraz uyum sağlayabilen ...

SELECT
    lu.Name,
    Slots.*
FROM (
    SELECT
        lub1.F_LU_ID,
        MAX( lub2.date_time ) AS StartOfSlot,
        lub1.date_time        AS EndOfSlot,
        DATEDIFF( lub1.date_time, MAX( lub2.date_time )) AS AvailableDays
    FROM
    ( SELECT F_LU_ID, Start AS date_time FROM LettingUnitBookings
      UNION
      SELECT F_LU_ID, CAST( '9999-12-31' AS DATE ) FROM LettingUnitBookings
    ) AS lub1,
    ( SELECT F_LU_ID, End   AS date_time FROM LettingUnitBookings
      UNION
      SELECT F_LU_ID, CAST( '1000-01-01' AS DATE ) FROM LettingUnitBookings
    ) AS lub2
    WHERE
        lub2.date_time <= lub1.date_time
    AND lub2.F_LU_ID = lub1.F_LU_ID
    GROUP BY
        lub1.F_LU_ID,
        lub1.date_time
    ) Slots
JOIN LettingUnits lu
ON   lu.ID = Slots.F_LU_ID
WHERE
    Slots.AvailableDays >= @WindowSize
AND
   (     DATEDIFF( Slots.EndOfSlot, @EndOfWindow )     >= @WindowSize
     AND DATEDIFF( @StartOfWindow, Slots.StartOfSlot ) >= @WindowSize
   )
   OR
   (     DATEDIFF( @EndOfWindow, Slots.StartOfSlot ) >= @WindowSize
     AND DATEDIFF( Slots.EndOfSlot, @StartOfWindow ) >= @WindowSize
   )