Thử thách thời gian - Lược đồ cơ sở dữ liệu cuộc hẹn với bác sĩ


8

Tôi đang cố gắng thực hiện một hệ thống chỉ định bác sĩ nơi bệnh nhân trực tuyến sẽ có hai lựa chọn:

  1. Lần đầu tiên ghé thăm (chuyến thăm này nên là 20 phút)
  2. Theo dõi chuyến thăm (chuyến thăm này nên là 10 phút)

Các ràng buộc:

  1. Giá sẽ khác nhau dựa trên héo lần đầu tiên / theo dõi
  2. Bác sĩ có thể có thời gian nghỉ giữa các khe
  3. Giao diện hệ thống sẽ hỗ trợ cả hai tùy chọn khi đặt chỗ
  4. chúng ta cần ít nhất khoảng cách thời gian giữa các vị trí để tạo tối đa. sử dụng sẵn có của bác sĩ

Mô hình chúng tôi đã hoàn thành cho đến nay dựa trên việc tạo bảng Doctor Slot giống như bên dưới Lược đồ bàn bác sĩ Slots

Xem xét ví dụ dưới đây nếu DoctorSlot là N và N = 10 phút

Trường hợp lần đầu tiên nếu (N * 2) == Miễn phí Đặt khối chặn hai vị trí thay vì một

Theo dõi trường hợp nếu (N) == Đặt chỗ miễn phí Chặn một khe

Bác sĩ mẫu: 9:00 đến 9:10 Bác sĩ: 9:10 đến 9:20 Bác sĩ: 9:20 đến 9:30

Trường hợp lần đầu tiên hiển thị cho bệnh nhân 9:00 đến 9:20

Thách thức:

  • Nối thời gian từ 9:00 đến 9:20 (có thể có thời gian đệm (nghỉ cho bác sĩ) giữa các vị trí cho bác sĩ)
  • Chúng tôi sẽ có hai ID vị trí từ cơ sở dữ liệu thay vì một (mà SlotID sẽ được sử dụng với Đơn hàng)
  • Cách hiển thị người dùng trong thời gian chạy dựa trên trường hợp của anh ta, chúng tôi sẽ sử dụng và cập nhật giá sau này cho phù hợp
  • Nếu người dùng đặt một khe thời gian đầu tiên, thì một người dùng khác đã đặt theo dõi, sẽ có những khoảng trống và cách xử lý thời gian trong SQL Server trong cơ sở dữ liệu

Câu hỏi:

  1. Lược đồ cơ sở dữ liệu tốt nhất để đạt được một giải pháp thỏa mãn tất cả các kịch bản có thể là gì?
  2. Phương pháp tốt nhất để đối phó với thực thể thời gian từ SQL Server / ASP.NET POV là gì?

Câu trả lời:


5

Tôi sẽ đề nghị một Appointmentbảng lưu trữ các cuộc hẹn hiện tại cho mỗi bác sĩ. Chúng tôi có thể thêm một số ràng buộc trên bảng này để giới hạn thời gian bắt đầu cuộc hẹn thậm chí là mười phút (ví dụ: 9.00, 9.10, 9.20) cộng với một số kiểm tra ý thức thông thường khác như EndTimesau StartTimevà bác sĩ không thể có hai cuộc hẹn bắt đầu cùng một lúc . Giả sử rằng bạn cũng muốn các bác sĩ chỉ làm việc trong khoảng thời gian từ 9 giờ sáng đến 5 giờ chiều, bởi vì mọi người đều cần sự cân bằng giữa công việc và cuộc sống.

CREATE TABLE Appointment (
    DoctorID char(1) NOT NULL,
    [Date] date NOT NULL,
    StartTime time(0) NOT NULL CONSTRAINT CHK_StartTime_TenMinute CHECK (DATEPART(MINUTE, StartTime)%10 = 0 AND DATEPART(SECOND, StartTime) = 0),
    EndTime time(0) NOT NULL CONSTRAINT CHK_EndTime_TenMinute CHECK (DATEPART(MINUTE, EndTime)%10 = 0 AND DATEPART(SECOND, EndTime) = 0),
    Status char(1) NOT NULL,
    UserID char(1) NOT NULL,
    Price int NOT NULL,
    CONSTRAINT PK_Appointment PRIMARY KEY CLUSTERED (DoctorID, [Date], StartTime),
    CONSTRAINT CHK_StartTime_BusinessHours CHECK (DATEPART(HOUR, StartTime) > = 9 AND DATEPART(HOUR, StartTime) < = 16),
    CONSTRAINT CHK_EndTime_BusinessHours CHECK (DATEPART(HOUR, EndTime) > = 9 AND DATEPART(HOUR, DATEADD(SECOND, -1, EndTime)) < = 16),
    CONSTRAINT CHK_EndTime_After_StartTime CHECK (EndTime > StartTime));
CREATE INDEX iDoctor_End ON Appointment (DoctorID, [Date], EndTime);

Chúng ta có thể chèn một số dữ liệu vào bảng này để xem nó trông như thế nào. Lưu ý rằng chèn thứ ba sẽ thất bại vì nó bị ngăn chặn bởi ràng buộc của chúng tôi. Bác sĩ không thể có hai cuộc hẹn bắt đầu cùng một lúc.

INSERT INTO Appointment VALUES ('A', '20170420', '09:00:00', '09:10:00', 'P', '1', '0');
INSERT INTO Appointment VALUES ('A', '20170420', '09:20:00', '09:40:00', 'C', '2', '10');
INSERT INTO Appointment VALUES ('A', '20170420', '09:00:00', '09:20:00', 'C', '2', '10');

Giả sử rằng bạn có một bảng số. Nếu bạn không có nhiều người khác đã mô tả cách tạo một cái. Nếu vẫn thất bại, điều này có thể tạo ra một cái cho bạn nhưng có lẽ đó không phải là cách tốt nhất.

CREATE TABLE Numbers (Number int PRIMARY KEY CLUSTERED);
DECLARE @number int = 0;
WHILE @number < 1000
BEGIN
    INSERT INTO Numbers VALUES (@number);
    SET @number += 1;
END 

Bây giờ nếu chúng tôi muốn xem các vị trí miễn phí cho một bác sĩ cụ thể, tất cả những gì chúng tôi cần làm là chỉ định bác sĩ nào và thời gian mà chúng tôi đang tìm kiếm là bao lâu:

DECLARE @doctorID char(1) = 'A';
DECLARE @length tinyint = 20;
WITH Slots AS (
    SELECT StartTime = DATEADD(MINUTE, ((DATEPART(MINUTE, GETDATE())/10)+1+Number)*10, DATEADD(HOUR, DATEPART(HOUR, GETDATE()), CONVERT(smalldatetime, CONVERT(date, GETDATE())))),
           EndTime = DATEADD(MINUTE, @length, DATEADD(MINUTE, ((DATEPART(MINUTE, GETDATE())/10)+1+Number)*10, DATEADD(HOUR, DATEPART(HOUR, GETDATE()), CONVERT(smalldatetime, CONVERT(date, GETDATE())))))
      FROM Numbers)
SELECT TOP 15 DoctorID = @doctorID,
    s.StartTime,
    s.EndTime
  FROM Slots AS s
  WHERE NOT EXISTS (SELECT 1 
                      FROM Appointment AS a
                      WHERE (CONVERT(time(0), s.StartTime) < a.EndTime AND CONVERT(time(0), s.EndTime) > a.StartTime)
                        AND a.DoctorID = @doctorID
                        AND a.[Date] = CONVERT(date, s.StartTime))
    AND DATEPART(HOUR, s.StartTime) >= 9
    AND DATEPART(HOUR, DATEADD(MINUTE, -1, s.EndTime)) <= 16
ORDER BY s.StartTime;

Điều đó có vẻ hơi khó xử, vì vậy nếu bất cứ ai có thể cải thiện logic ngày đó vui lòng nhận đề xuất.

Nếu một bác sĩ muốn nghỉ ngơi, sau đó nhập giờ nghỉ như một cuộc hẹn và nó sẽ không có sẵn để đặt.

Lưu ý rằng các ràng buộc bảng không thực thi các cuộc hẹn không chồng chéo. Điều này là có thể nhưng nó phức tạp hơn. Nếu đây là hệ thống của tôi, tôi sẽ nghĩ về một số hệ thống (ví dụ như trình kích hoạt) để cuối cùng xác minh rằng cuộc hẹn không trùng với cuộc hẹn hiện tại tại thời điểm chèn, nhưng điều đó tùy thuộc vào bạn.


Mặc dù tôi đã giải quyết vấn đề này vì đây là một câu hỏi cũ và tôi đã sử dụng một cách tiếp cận khác, nhưng câu trả lời của bạn chính xác là những gì tôi đang tìm kiếm. cảm ơn vì đã giúp đỡ :)
theK

1
@ user3291143 Vâng, tôi đã lưu ý rằng đó là một câu hỏi cũ, nhưng nó đã được đưa lên trang nhất vì vậy tôi nghĩ rằng đáng để chụp một câu trả lời kỹ lưỡng hơn để tham khảo trong tương lai. Cảm ơn vì đã phản hồi.
mendosi

@ theK. Xin vui lòng chia sẻ mã của bạn như thế nào bạn đã thực hiện
iCoders

1

Đây là phiên bản tương đương về chức năng (và IMO dễ đọc hơn) của mã mendosi cho MariaDB / MySQL, với một số giải trình bổ sung và logic hơi đơn giản hóa trong một số lĩnh vực.

Tạo một Numbersbảng nếu bạn chưa có:

CREATE TABLE Numbers (number INT UNSIGNED PRIMARY KEY);

DELIMITER //

CREATE PROCEDURE populateNumbers()
BEGIN
    SET @x = 0;
    WHILE @x < 1024 DO
        INSERT INTO Numbers VALUES (@x);
        SET @x = @x + 1;
    END WHILE;
    SET @x = NULL;
END; //

DELIMITER ;

CALL populateNumbers;
DROP PROCEDURE populateNumbers;

Đây là một lược đồ đủ cho một Appointmentbảng. Trong giây lát, chúng tôi cũng sẽ thêm một trình INSERTkích hoạt để đảm bảo rằng các mục mới không xung đột với các mục hiện có.

CREATE TABLE Appointment (
    doctorID    INT UNSIGNED    NOT NULL,
    `date`      DATE            NOT NULL,
    startTime   TIME(0)         NOT NULL,
    endTime     TIME(0)         NOT NULL,

    CONSTRAINT PRIMARY KEY (doctorID, `date`, startTime),

    CONSTRAINT mustStartOnTenMinuteBoundary CHECK (
        EXTRACT(MINUTE FROM startTime) % 10 = 0
        AND EXTRACT(SECOND FROM startTime) = 0
    ),
    CONSTRAINT mustEndOnTenMinuteBoundary CHECK (
        EXTRACT(MINUTE FROM endTime) % 10 = 0
        AND EXTRACT(SECOND FROM endTime) = 0
    ),
    CONSTRAINT cannotStartBefore0900 CHECK (
        EXTRACT(HOUR FROM startTime) >= 9
    ),
    CONSTRAINT cannotEndAfter1700 CHECK (
        EXTRACT(HOUR FROM (startTime - INTERVAL 1 SECOND)) < 17
    ),
    CONSTRAINT mustEndAfterStart CHECK (
        endTime > startTime
    )
);

Đầu tiên, chúng tôi xác định một chức năng để xác định xem một khe thời gian nhất định có thể được phân bổ như một cuộc hẹn mới hay không:

DELIMITER //

CREATE FUNCTION slotIsAvailable(
    doctorID            INT,
    slotStartDateTime   DATETIME,
    slotEndDateTime     DATETIME
) RETURNS BOOLEAN NOT DETERMINISTIC
BEGIN
    RETURN CASE WHEN EXISTS (
        -- This table will contain records iff the slot clashes with an existing appointment
        SELECT TRUE
        FROM Appointment AS a
        WHERE
                CONVERT(slotStartDateTime, TIME) < a.endTime   -- These two conditions will both hold iff the slot overlaps
            AND CONVERT(slotEndDateTime,   TIME) > a.startTime -- with the existing appointment that it's being compared to
            AND a.doctorID = doctorID
            AND a.date = CONVERT(slotStartDateTime, DATE)
    ) THEN FALSE ELSE TRUE
    END;
END; //

DELIMITER ;

Bây giờ đây là trình INSERTkích hoạt được đề cập trước đó để đảm bảo không có cuộc hẹn xung đột nào được lưu trữ:

DELIMITER //

CREATE TRIGGER ensureNewAppointmentsDoNotClash
    BEFORE INSERT ON Appointment
    FOR EACH ROW
BEGIN
    IF NOT slotIsAvailable(
        NEW.doctorID,
        CAST( CONCAT(NEW.date, ' ', NEW.startTime)  AS DATETIME ),
        CAST( CONCAT(NEW.date, ' ', NEW.endTime)    AS DATETIME )
    ) THEN
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Appointment clashes with an existing appointment!';
    END IF;
END; //

DELIMITER ;

Bây giờ Appointmentbảng đã được thiết lập đúng, chúng ta có thể chèn một số mục mẫu hợp lệ:

INSERT INTO Appointment VALUES (1, '2019-10-06', '09:20', '09:30');
INSERT INTO Appointment VALUES (1, '2019-10-06', '09:40', '09:50');
INSERT INTO Appointment VALUES (1, '2019-10-06', '11:00', '11:20');
INSERT INTO Appointment VALUES (1, '2019-10-06', '11:20', '11:40');
INSERT INTO Appointment VALUES (1, '2019-10-06', '11:40', '12:00');
INSERT INTO Appointment VALUES (1, '2019-10-06', '13:00', '14:00');
INSERT INTO Appointment VALUES (1, '2019-10-06', '16:00', '16:40');

Nếu bạn cố gắng chèn một mục cuộc hẹn không hợp lệ, một lỗi sẽ được đưa ra do kết quả của trình ensureNewAppointmentsDoNotClashkích hoạt. Trong thực tế, trình kích hoạt này sẽ đưa ra một lỗi ngay cả trước khi ràng buộc khóa chính được kiểm tra, do đó có thể được coi là dư thừa; đối với giải pháp của mình, tôi đã chọn có một trường ID cho Appointmentbảng, thay vì sử dụng khóa chính ghép.

Bây giờ đây là thủ tục để có được một tập hợp các khe thời gian có sẵn với độ dài nhất định, với một bác sĩ nhất định. Lưu ý rằng chúng tôi sử dụng slotIsAvailablechức năng của chúng tôi mà chúng tôi đã xác định trước đó và cũng được sử dụng trong trình INSERTkích hoạt của chúng tôi .

-- The ID of the doctor to book the appointment with.
SET @doctorID = 1;

-- The moment from which to start searching for availble time slots
SET @searchStart = CURRENT_TIMESTAMP;

-- The duration of the appointment to book, in minutes.
SET @duration = 20;

WITH
    SlotStart AS (
        -- This table will list all the 10-minute-aligned timestamps that occur after `@searchStart`
        SELECT
            CONVERT(@searchStart, DATE)
            + INTERVAL (EXTRACT(HOUR FROM @searchStart)) HOUR
            + INTERVAL ( EXTRACT(MINUTE FROM @searchStart) DIV 10 + number + 1 ) * 10 MINUTE
        AS startDateTime
        FROM Numbers
    ),
    Slot AS (
        SELECT
            startDateTime,
            startDateTime + INTERVAL @duration MINUTE   AS endDateTime
        FROM SlotStart
    ),
    AvailableSlot AS (
        SELECT
            @doctorID   AS doctorID,
            startDateTime,
            endDateTime
        FROM Slot AS s
        WHERE
                slotIsAvailable(@doctorID, s.startDateTime, s.endDateTime)
            AND EXTRACT(HOUR FROM s.startDateTime) >= 9
            AND EXTRACT(HOUR FROM (s.endDateTime - INTERVAL 1 MINUTE)) <= 16
    )
SELECT *
    FROM AvailableSlot
    WHERE
            CONVERT(startDateTime, DATE) = CONVERT(@searchStart, DATE)
        AND CONVERT(endDateTime,   DATE) = CONVERT(@searchStart, DATE)
    ORDER BY startDateTime ASC;

Truy vấn trên, với các bản ghi mẫu ở trên cho Appointment@searchStartbằng với '2019-10-06 06:00', mang lại:

+----------+---------------------+---------------------+
| doctorID | startDateTime       | endDateTime         |
+----------+---------------------+---------------------+
|        1 | 2019-10-06 09:00:00 | 2019-10-06 09:20:00 |
|        1 | 2019-10-06 09:50:00 | 2019-10-06 10:10:00 |
|        1 | 2019-10-06 10:00:00 | 2019-10-06 10:20:00 |
|        1 | 2019-10-06 10:10:00 | 2019-10-06 10:30:00 |
|        1 | 2019-10-06 10:20:00 | 2019-10-06 10:40:00 |
|        1 | 2019-10-06 10:30:00 | 2019-10-06 10:50:00 |
|        1 | 2019-10-06 10:40:00 | 2019-10-06 11:00:00 |
|        1 | 2019-10-06 12:00:00 | 2019-10-06 12:20:00 |
|        1 | 2019-10-06 12:10:00 | 2019-10-06 12:30:00 |
|        1 | 2019-10-06 12:20:00 | 2019-10-06 12:40:00 |
|        1 | 2019-10-06 12:30:00 | 2019-10-06 12:50:00 |
|        1 | 2019-10-06 12:40:00 | 2019-10-06 13:00:00 |
|        1 | 2019-10-06 14:00:00 | 2019-10-06 14:20:00 |
|        1 | 2019-10-06 14:10:00 | 2019-10-06 14:30:00 |
|        1 | 2019-10-06 14:20:00 | 2019-10-06 14:40:00 |
|        1 | 2019-10-06 14:30:00 | 2019-10-06 14:50:00 |
|        1 | 2019-10-06 14:40:00 | 2019-10-06 15:00:00 |
|        1 | 2019-10-06 14:50:00 | 2019-10-06 15:10:00 |
|        1 | 2019-10-06 15:00:00 | 2019-10-06 15:20:00 |
|        1 | 2019-10-06 15:10:00 | 2019-10-06 15:30:00 |
|        1 | 2019-10-06 15:20:00 | 2019-10-06 15:40:00 |
|        1 | 2019-10-06 15:30:00 | 2019-10-06 15:50:00 |
|        1 | 2019-10-06 15:40:00 | 2019-10-06 16:00:00 |
|        1 | 2019-10-06 16:40:00 | 2019-10-06 17:00:00 |
+----------+---------------------+---------------------+

0

Trước hết, tại sao khe thời gian của bạn có độ dài cố định? Ngay cả với thiết kế hiện tại, bạn có thể có một vị trí với start_time = 9:00 và end_time = 9:20 và nó có thể là một id. Hơn nữa, bạn có thể tạo một bảng cầu nối để kết nối loại bài kiểm tra (ví dụ: loại 1 = bài kiểm tra đầu tiên, loại 2 = bài kiểm tra, v.v.) với độ dài của các vị trí, như thế này:
tests_type slot_length_in_minutes
1 20
2 10
3 15
Trong trường hợp này, khi bạn đặt thời gian cho bài kiểm tra, trong giao diện người dùng, bạn sẽ chọn loại bài kiểm tra từ một số loại hộp chọn (hộp tổ hợp) và sau đó tìm kiếm một vị trí miễn phí có kích thước phù hợp.

Ngoài ra, bạn không cung cấp cho chúng tôi cấu trúc bảng đặt chỗ của bạn, vì vậy chúng tôi có thể giúp bạn điều đó. Nhưng tôi đoán bạn có một số loại khe thời gian ở đó, và cũng là id của một bác sĩ được chọn ...? Trong trường hợp đó, chỉ bằng cách tham gia các khe thời gian và đặt chỗ có thể hiển thị cho bạn thời gian biểu hoàn chỉnh. Hy vọng nó đã được giúp đỡ. Nếu không, hãy hỏi thêm.


Tôi muốn thay đổi vị trí miễn phí có kích thước phù hợp này dựa trên loại người dùng đã chọn, ví dụ: nếu người dùng chọn "cuộc hẹn lần đầu" thì anh ta có thể thấy các vị trí trong 20 phút nhưng nếu anh ta chọn "Cuộc hẹn lần thứ hai '' anh sẽ thấy một khe cắm 10 phút chiều dài
Thek
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.