PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Cách tìm thời gian bắt đầu miễn phí đầu tiên khi đặt chỗ ở Postgres

Lược đồ đã điều chỉnh

CREATE EXTENSION btree_gist;
CREATE TYPE timerange AS RANGE (subtype = time);  -- create type once

-- Workers
CREATE TABLE worker(
   worker_id serial PRIMARY KEY
 , worker text NOT NULL
);
INSERT INTO worker(worker) VALUES ('JOHN'), ('MARY');

-- Holidays
CREATE TABLE pyha(pyha date PRIMARY KEY);

-- Reservations
CREATE TABLE reservat (
   reservat_id serial PRIMARY KEY
 , worker_id   int NOT NULL REFERENCES worker ON UPDATE CASCADE
 , day         date NOT NULL CHECK (EXTRACT('isodow' FROM day) < 7)
 , work_from   time NOT NULL -- including lower bound
 , work_to     time NOT NULL -- excluding upper bound
 , CHECK (work_from >= '10:00' AND work_to <= '21:00'
      AND work_to - work_from BETWEEN interval '15 min' AND interval '4 h'
      AND EXTRACT('minute' FROM work_from) IN (0, 15, 30, 45)
      AND EXTRACT('minute' FROM work_from) IN (0, 15, 30, 45)
    )
 , EXCLUDE USING gist (worker_id WITH =, day WITH =
                     , timerange(work_from, work_to) WITH &&)
);
INSERT INTO reservat (worker_id, day, work_from, work_to) VALUES 
   (1, '2014-10-28', '10:00', '11:30')  -- JOHN
 , (2, '2014-10-28', '11:30', '13:00'); -- MARY

-- Trigger for volatile checks
CREATE OR REPLACE FUNCTION holiday_check()
  RETURNS trigger AS
$func$
BEGIN
   IF EXISTS (SELECT 1 FROM pyha WHERE pyha = NEW.day) THEN
      RAISE EXCEPTION 'public holiday: %', NEW.day;
   ELSIF NEW.day < now()::date OR NEW.day > now()::date + 31 THEN
      RAISE EXCEPTION 'day out of range: %', NEW.day;
   END IF;

   RETURN NEW;
END
$func$ LANGUAGE plpgsql STABLE; -- can be "STABLE"

CREATE TRIGGER insupbef_holiday_check
BEFORE INSERT OR UPDATE ON reservat
FOR EACH ROW EXECUTE PROCEDURE holiday_check();

Những điểm chính

Bên cạnh
Ngoài việc đơn giản hóa đầu vào và kiểm tra các ràng buộc mà tôi mong đợi timerange để tiết kiệm 8 byte dung lượng so với tsrange kể từ time chỉ chiếm 4 byte. Nhưng hóa ra timerange chiếm 22 byte trên đĩa (25 trong RAM), giống như tsrange (hoặc tstzrange ). Vì vậy, bạn có thể sử dụng tsrange cũng. Nguyên tắc của truy vấn và ràng buộc loại trừ giống nhau.

Truy vấn

Được bao bọc trong một hàm SQL để xử lý tham số thuận tiện:

CREATE OR REPLACE FUNCTION f_next_free(_start timestamp, _duration interval)
  RETURNS TABLE (worker_id int, worker text, day date
               , start_time time, end_time time) AS
$func$
   SELECT w.worker_id, w.worker
        , d.d AS day
        , t.t AS start_time
        ,(t.t + _duration) AS end_time
   FROM  (
      SELECT _start::date + i AS d
      FROM   generate_series(0, 31) i
      LEFT   JOIN pyha p ON p.pyha = _start::date + i
      WHERE  p.pyha IS NULL   -- eliminate holidays
      ) d
   CROSS  JOIN (
      SELECT t::time
      FROM   generate_series (timestamp '2000-1-1 10:00'
                            , timestamp '2000-1-1 21:00' - _duration
                            , interval '15 min') t
      ) t  -- times
   CROSS  JOIN worker w
   WHERE  d.d + t.t > _start  -- rule out past timestamps
   AND    NOT EXISTS (
      SELECT 1
      FROM   reservat r
      WHERE  r.worker_id = w.worker_id
      AND    r.day = d.d
      AND    timerange(r.work_from, r.work_to) && timerange(t.t, t.t + _duration)
      )
   ORDER  BY d.d, t.t, w.worker, w.worker_id
   LIMIT  30  -- could also be parameterized
$func$ LANGUAGE sql STABLE;

Gọi:

SELECT * FROM f_next_free('2014-10-28 12:00'::timestamp, '1.5 h'::interval);

SQL Fiddle trên Postgres 9.3 ngay bây giờ.

Giải thích

  • Hàm nhận _start timestamp là thời gian bắt đầu tối thiểu và _duration interval . Hãy cẩn thận chỉ loại trừ thời gian sớm hơn vào bắt đầu ngày, không phải những ngày sau. Đơn giản nhất chỉ bằng cách thêm ngày và giờ:t + d > _start .
    Để đặt chỗ bắt đầu từ "bây giờ", chỉ cần chuyển now()::timestamp :

    SELECT * FROM f_next_free(`now()::timestamp`, '1.5 h'::interval);
    
  • Truy vấn con d tạo ngày bắt đầu từ giá trị đầu vào _day . Không bao gồm các ngày lễ.

  • Các ngày được kết hợp chéo với các phạm vi thời gian có thể được tạo trong truy vấn con t .
  • Điều đó được kết hợp chéo với tất cả các nhân viên hiện có w .
  • Cuối cùng, loại bỏ tất cả các ứng cử viên xung đột với các đặt chỗ hiện có bằng cách sử dụng NOT EXISTS chống bán nối và đặc biệt là toán tử chồng chéo && .

Có liên quan:



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Có thể tạo cơ sở dữ liệu postgres trong dòng lệnh nhưng không phải trong tập lệnh bash

  2. Làm cách nào để thay đổi cột PG thành NULLABLE TRUE?

  3. Lọc theo phạm vi ngày (cùng tháng và ngày) qua các năm

  4. PGTune Alternatives - ClusterControl PostgreSQL Configuration

  5. Có thể tạo bao nhiêu lược đồ trong postgres