Cơ sở dữ liệu phân tích trang web MySQL của chúng tôi chứa bảng tóm tắt được cập nhật suốt cả ngày khi hoạt động mới được nhập. Chúng tôi sử dụng ON CẬP NHẬT KEY CẬP NHẬT để tóm tắt ghi đè các tính toán trước đó, nhưng gặp khó khăn vì một trong các cột trong UNIQUE KEY của bảng tóm tắt là một FK tùy chọn và chứa các giá trị NULL.MySQL ON CẬP NHẬT KEY TUYÊN BỐ với cột nullable trong khóa duy nhất
Các giá trị NULL này có nghĩa là "không có mặt và tất cả các trường hợp như vậy là tương đương". Tất nhiên, MySQL thường xử lý NULL như là "không xác định, và tất cả các trường hợp như vậy không tương đương".
cấu trúc cơ bản như sau:
An "Hoạt động" bảng chứa một mục nhập cho mỗi phiên, mỗi thuộc vào chiến dịch, với tùy chọn bộ lọc và giao dịch ID cho một số mục.
CREATE TABLE `Activity` (
`session_id` INTEGER AUTO_INCREMENT
, `campaign_id` INTEGER NOT NULL
, `filter_id` INTEGER DEFAULT NULL
, `transaction_id` INTEGER DEFAULT NULL
, PRIMARY KEY (`session_id`)
);
Bảng "Tóm tắt" chứa tổng số phiên trong đó có chứa ID giao dịch. Các tóm tắt này được phân tách, với một bản tóm tắt cho mỗi bộ lọc chiến dịch và (tùy chọn). Đây là một bảng phi giao dịch sử dụng MyISAM.
CREATE TABLE `Summary` (
`day` DATE NOT NULL
, `campaign_id` INTEGER NOT NULL
, `filter_id` INTEGER DEFAULT NULL
, `sessions` INTEGER UNSIGNED DEFAULT NULL
, `transactions` INTEGER UNSIGNED DEFAULT NULL
, UNIQUE KEY (`day`, `campaign_id`, `filter_id`)
) ENGINE=MyISAM;
Truy vấn tóm tắt thực tế giống như sau, đếm số lượng phiên và giao dịch, sau đó nhóm theo bộ lọc chiến dịch và (tùy chọn).
INSERT INTO `Summary`
(`day`, `campaign_id`, `filter_id`, `sessions`, `transactions`)
SELECT `day`, `campaign_id`, `filter_id
, COUNT(`session_id`) AS `sessions`
, COUNT(`transaction_id` IS NOT NULL) AS `transactions`
FROM Activity
GROUP BY `day`, `campaign_id`, `filter_id`
ON DUPLICATE KEY UPDATE
`sessions` = VALUES(`sessions`)
, `transactions` = VALUES(`transactions`)
;
Mọi thứ hoạt động tốt, ngoại trừ tóm tắt các trường hợp mà filter_id là NULL. Trong những trường hợp này, mệnh đề ON DUPLICATE KEY UPDATE không khớp với hàng hiện tại, và một hàng mới được viết mỗi lần. Điều này là do thực tế là "NULL! = NULL". Tuy nhiên, cái chúng ta cần là "NULL = NULL" khi so sánh các khóa duy nhất.
Tôi đang tìm ý tưởng về cách giải quyết hoặc phản hồi về những điều chúng tôi đã đưa ra cho đến thời điểm này. Cách giải quyết chúng tôi đã nghĩ đến cho đến nay theo.
Xóa tất cả các mục tóm tắt chứa giá trị khóa NULL trước khi chạy tóm tắt. (Đây là những gì chúng tôi đang làm bây giờ) Điều này có tác dụng phụ tiêu cực của việc trả về kết quả với dữ liệu bị thiếu nếu một truy vấn được thực hiện trong quá trình tổng kết.
Thay đổi cột DEFAULT NULL thành DEFAULT 0, cho phép khóa UNIQUE được kết hợp nhất quán. Điều này có tác dụng phụ tiêu cực của quá phức tạp sự phát triển của các truy vấn đối với bảng tóm tắt. Nó buộc chúng ta phải sử dụng rất nhiều "CASE filter_id = 0 THEN NULL ELSE filter_id END", và làm cho việc tham gia khó xử vì tất cả các bảng khác có NULLs thực tế cho filter_id.
Tạo chế độ xem trả về "Bộ lọc CASE_id = 0 THEN NULL ELSE filter_id END" và sử dụng chế độ xem này thay vì trực tiếp bảng. Bảng tóm tắt chứa hàng trăm nghìn hàng và tôi đã được cho biết hiệu suất xem khá kém.
Cho phép tạo các mục trùng lặp và xóa các mục nhập cũ sau khi tóm tắt hoàn tất. Có vấn đề tương tự khi xóa chúng trước thời hạn.
Thêm cột thay thế có chứa 0 cho NULL và sử dụng thay thế đó trong UNIQUE KEY (thực tế chúng tôi có thể sử dụng PRIMARY KEY nếu tất cả các cột KHÔNG NULL).
Giải pháp này có vẻ hợp lý, ngoại trừ ví dụ trên chỉ là một ví dụ; cơ sở dữ liệu thực tế chứa một nửa tá bảng tóm tắt, một trong số đó chứa bốn cột vô giá trị trong UNIQUE KEY. Có một số lo ngại rằng chi phí quá cao.
Bạn có cách giải quyết tốt hơn, cấu trúc bảng, quy trình cập nhật hoặc thực hành tốt nhất của MySQL có thể giúp ích?
EDIT: Để làm rõ "ý nghĩa của null"
Dữ liệu trong các hàng tóm tắt chứa các cột NULL được coi thuộc về nhau chỉ theo nghĩa là trở thành một single "catch-all" hàng trong báo cáo tóm tắt , tóm tắt những mục mà điểm dữ liệu đó không tồn tại hoặc không xác định. Vì vậy, trong bối cảnh của bảng tóm tắt chính nó, ý nghĩa là "tổng của những mục mà không có giá trị nào được biết". Trong các bảng quan hệ, mặt khác, đây thực sự là các kết quả NULL.
Lý do duy nhất để đưa chúng vào khóa duy nhất trên bảng tóm tắt là cho phép cập nhật tự động (theo ON DUPLICATE KEY UPDATE) khi tính lại các báo cáo tóm tắt.
Có thể một cách tốt hơn để mô tả nó là ví dụ cụ thể mà một trong các bảng tóm tắt nhóm kết quả theo địa lý bằng tiền tố mã zip của địa chỉ doanh nghiệp do người trả lời đưa ra. Không phải tất cả người trả lời đều cung cấp địa chỉ doanh nghiệp, do đó mối quan hệ giữa bảng giao dịch và bảng địa chỉ là khá chính xác NULL. Trong bảng tóm tắt cho dữ liệu này, một hàng được tạo cho mỗi tiền tố mã zip, chứa tóm tắt dữ liệu trong khu vực đó. Một hàng bổ sung được tạo để hiển thị tóm tắt dữ liệu mà không có tiền tố mã zip nào được biết.
Thay đổi phần còn lại của bảng dữ liệu để có giá trị 0 "THERE_IS_NO_ZIP_CODE" rõ ràng và đặt một bản ghi đặc biệt trong bảng ZipCodePrefix đại diện cho giá trị này là không đúng - mối quan hệ đó thực sự là NULL.
Vâng, bảng tóm tắt là khá rõ ràng không phải là một bảng quan hệ. Nó chỉ đơn giản là một container thuận tiện để giữ kết quả báo cáo. Tuyên bố của tôi rằng "Những NULL này có nghĩa là 'không có mặt, và tất cả các trường hợp như vậy là tương đương'", có lẽ là gây hiểu nhầm. Trong các bảng quan hệ có chứa dữ liệu chuẩn hóa, filter_id và các mối quan hệ rỗng khác mà tôi đề cập là một phần của khóa duy nhất trong bảng tóm tắt thực sự có nghĩa "không xác định" và không phải là một phần của bất kỳ khóa chính hoặc khóa duy nhất nào. Xem chỉnh sửa ở trên. – ryandenki
Chính xác. Chúng tôi sử dụng INSERT ... SELECT, sử dụng mệnh đề ON DUPLICATE KEY ở đó để cập nhật các mục trong suốt cả ngày. Thực ra, triển khai đầu tiên cách đây hai năm là như bạn đề xuất - trước tiên hãy chọn dữ liệu, thực hiện một số thao tác bổ sung, sau đó phát hành INSERTS riêng lẻ, với mệnh đề WHERE có tính đến trường hợp IS NULL. Cách tiếp cận đó có lợi thế là các khóa chèn các hàng riêng lẻ ngắn hơn cho phương thức CHỌN CHỌN ... SELECT. Nhưng những khóa này chỉ dựa trên bản gốc bằng cách sử dụng sao chép hàng, và chúng ta có thể thay thế tất cả mã phía ứng dụng bằng một câu lệnh SQL đơn. – ryandenki