Trong hai phần trước, chúng tôi đã trình bày mô hình cơ sở dữ liệu trực tiếp cho doanh nghiệp dựa trên đăng ký và kho dữ liệu (DWH) mà chúng tôi có thể sử dụng để báo cáo. Mặc dù rõ ràng là chúng nên hoạt động cùng nhau, nhưng không có mối liên hệ nào giữa hai mô hình này. Hôm nay, chúng tôi sẽ thực hiện bước tiếp theo đó và viết mã để chuyển dữ liệu từ cơ sở dữ liệu trực tiếp sang DWH của chúng tôi.
Mô hình dữ liệu
Trước khi đi sâu vào mã, hãy tự nhắc mình về hai mô hình mà chúng tôi sẽ làm việc. Đầu tiên là mô hình dữ liệu giao dịch mà chúng tôi sẽ sử dụng để lưu trữ dữ liệu thời gian thực của mình. Có tính đến việc chúng tôi điều hành hoạt động kinh doanh dựa trên đăng ký, chúng tôi sẽ cần lưu trữ thông tin chi tiết về khách hàng và đăng ký, đơn đặt hàng của khách hàng và trạng thái đơn đặt hàng.
Thực sự có rất nhiều thứ mà chúng tôi có thể thêm vào mô hình này, chẳng hạn như theo dõi các khoản thanh toán và lưu trữ dữ liệu lịch sử (đặc biệt là các thay đổi về dữ liệu khách hàng và đăng ký). Tuy nhiên, để nhấn mạnh quy trình ETL (trích xuất, biến đổi và tải), tôi muốn giữ cho mô hình này càng đơn giản càng tốt.
Sử dụng mô hình dữ liệu giao dịch làm cơ sở dữ liệu báo cáo có thể hoạt động trong một số trường hợp, nhưng nó sẽ không hoạt động trong mọi trường hợp. Chúng tôi đã đề cập đến điều đó, nhưng rất đáng để nhắc lại. Nếu chúng ta muốn tách các nhiệm vụ báo cáo của mình khỏi các quy trình thời gian thực, chúng ta nên tạo một số loại cơ sở dữ liệu báo cáo. Kho dữ liệu là một trong những giải pháp.
DWH của chúng tôi tập trung xung quanh bốn bảng dữ kiện. Hai đầu tiên theo dõi số lượng khách hàng và đăng ký ở cấp độ hàng ngày. Hai phần còn lại theo dõi số lần giao hàng và các sản phẩm có trong những lần giao hàng này.
Giả định của tôi là chúng tôi sẽ chạy quy trình ETL của mình mỗi ngày một lần. Đầu tiên, chúng tôi sẽ điền vào bảng thứ nguyên các giá trị mới (nếu cần). Sau đó, chúng tôi sẽ điền các bảng dữ kiện.
Để tránh lặp lại không cần thiết, tôi sẽ chỉ trình bày mã sẽ điền vào hai bảng thứ nguyên đầu tiên và hai bảng dữ kiện đầu tiên. Các bảng còn lại có thể được điền bằng cách sử dụng mã rất giống nhau. Tôi khuyến khích bạn tự viết ra mã. Không có cách nào tốt hơn để học một điều gì đó mới hơn là thử nó.
Ý tưởng:Bảng thứ nguyên
Ý tưởng chung là tạo ra các thủ tục được lưu trữ mà chúng ta có thể thường xuyên sử dụng để điền vào bảng DWH - kích thước cũng như bảng dữ kiện. Các thủ tục này sẽ chuyển dữ liệu giữa hai cơ sở dữ liệu trên cùng một máy chủ. Điều này có nghĩa là một số truy vấn bên trong các thủ tục này sẽ sử dụng các bảng từ cả hai cơ sở dữ liệu. Điều này được mong đợi; chúng ta cần so sánh trạng thái của DWH với DB trực tiếp và thực hiện các thay đổi đối với DWH theo những gì đang diễn ra trong DB trực tiếp.
Chúng tôi có bốn bảng thứ nguyên trong DWH của chúng tôi:dim_time
, dim_city
, dim_product
và dim_delivery_status
.
Thứ nguyên thời gian được điền bằng cách thêm ngày trước đó. Giả định chính là chúng tôi sẽ chạy quy trình này hàng ngày, sau khi kết thúc hoạt động kinh doanh.
Kích thước thành phố và sản phẩm sẽ phụ thuộc vào các giá trị hiện tại được lưu trữ trong city
và product
từ điển trong cơ sở dữ liệu trực tiếp. Nếu chúng tôi thêm nội dung nào đó vào các từ điển này, thì các giá trị mới sẽ được thêm vào bảng thứ nguyên trong bản cập nhật DWH tiếp theo.
Bảng thứ nguyên cuối cùng là dim_delivery_status
bàn. Nó sẽ không được cập nhật vì nó chỉ chứa ba giá trị mặc định. Giao hàng đang trong quá trình vận chuyển, bị hủy hoặc đã giao.
Ý tưởng:Bảng dữ kiện
Việc điền các bảng thực tế là công việc thực sự. Mặc dù các từ điển trong cơ sở dữ liệu trực tiếp không chứa thuộc tính dấu thời gian, nhưng các bảng có dữ liệu được chèn vào kết quả của các hoạt động của chúng tôi thì có. Bạn sẽ nhận thấy hai thuộc tính dấu thời gian, time_inserted
và time_updated
, trong mô hình dữ liệu.
Một lần nữa, tôi giả định rằng chúng tôi sẽ chạy thành công quá trình nhập DWH mỗi ngày một lần. Điều này cho phép chúng tôi tổng hợp dữ liệu ở cấp độ hàng ngày. Chúng tôi sẽ đếm số lượng đăng ký và khách hàng đang hoạt động và đã hủy, cũng như số lượng giao hàng và sản phẩm đã giao cho ngày đó.
Mô hình trực tiếp của chúng tôi hoạt động tốt nếu chúng tôi chạy quy trình chèn sau COB (đóng cửa kinh doanh). Tuy nhiên, nếu chúng ta muốn linh hoạt hơn, chúng ta nên thực hiện một số thay đổi đối với mô hình. Một thay đổi như vậy có thể là có một bảng lịch sử riêng để theo dõi thời điểm chính xác khi bất kỳ dữ liệu nào liên quan đến khách hàng hoặc đăng ký thay đổi. Với tổ chức hiện tại của chúng tôi, chúng tôi sẽ biết rằng thay đổi đã xảy ra, nhưng chúng tôi sẽ không biết liệu có bất kỳ thay đổi nào trước khi thay đổi này hay không (ví dụ:một khách hàng đã hủy ngày hôm qua, kích hoạt lại tài khoản của mình sau nửa đêm và sau đó lại hủy vào hôm nay) .
Đang điền bảng thứ nguyên
Như đã đề cập trước đây, tôi sẽ giả định rằng chúng tôi sẽ chạy quá trình nhập DWH chính xác mỗi ngày một lần. Nếu không đúng như vậy, chúng tôi cần mã bổ sung để xóa dữ liệu mới được chèn khỏi bảng thứ nguyên và bảng dữ kiện. Đối với bảng thứ nguyên, điều này sẽ được giới hạn trong việc xóa ngày đã cho.
Trước tiên, chúng tôi sẽ kiểm tra xem ngày đã cho có tồn tại trong dim_time
bàn. Nếu không, chúng tôi sẽ thêm một hàng mới vào bảng; nếu có, chúng tôi không cần phải làm gì cả. Trong hầu hết các trường hợp, tất cả các ngày đều được chèn trong quá trình triển khai sản xuất ban đầu. Nhưng tôi sẽ sử dụng ví dụ này cho mục đích giáo dục.
Đối với dim_city
và dim_product
thứ nguyên, tôi sẽ chỉ thêm bất kỳ giá trị mới nào mà tôi phát hiện được trong city
và product
những cái bàn. Tôi sẽ không thực hiện bất kỳ thao tác xóa nào vì bất kỳ giá trị nào đã chèn trước đó đều có thể được tham chiếu trong một số bảng thông tin. Chúng ta có thể thực hiện với một thao tác xóa mềm, ví dụ:có cờ "đang hoạt động" mà chúng tôi có thể bật và tắt.
Đối với bảng cuối cùng, dim_delivery_status
, Tôi sẽ không làm gì cả vì nó sẽ luôn chứa ba giá trị giống nhau.
Đoạn mã dưới đây tạo một quy trình sẽ điền vào các bảng thứ nguyên dim_time
và dim_city
.
Đối với thứ nguyên thời gian, tôi sẽ thêm ngày hôm qua. Tôi sẽ giả định rằng quy trình ETL bắt đầu ngay sau nửa đêm. Tôi sẽ kiểm tra xem thứ nguyên đó đã tồn tại chưa và nếu chưa, thì tôi sẽ thêm ngày mới vào bảng.
Đối với thứ nguyên thành phố, tôi sẽ sử dụng THAM GIA TRÁI để kết hợp dữ liệu từ cơ sở dữ liệu trực tiếp và cơ sở dữ liệu DWH để xác định hàng nào bị thiếu. Sau đó, tôi sẽ chỉ thêm bất kỳ dữ liệu bị thiếu nào vào bảng thứ nguyên. Điều đáng nói là có một số cách để kiểm tra xem dữ liệu có bị thay đổi hay không. Quá trình này được gọi là thu thập dữ liệu thay đổi, hoặc CDC. Một phương pháp phổ biến là kiểm tra dấu thời gian hoặc phiên bản cập nhật. Có một số cách bổ sung, nhưng chúng nằm ngoài phạm vi của bài viết này.
Bây giờ chúng ta hãy xem mã, được viết bằng cú pháp MySQL .
DROP PROCEDURE IF EXISTS p_update_dimensions// CREATE PROCEDURE p_update_dimensions () BEGIN SET @time_exists = 0; SET @time_date = DATE_ADD(DATE(NOW()), INTERVAL -1 DAY); -- procedure populates dimension tables with new values -- dim_time SET @time_exists = (SELECT COUNT(*) FROM subscription_dwh.dim_time dim_time WHERE dim_time.time_date = @time_date); IF (@time_exists = 0) THEN INSERT INTO subscription_dwh.`dim_time`(`time_date`, `time_year`, `time_month`, `time_week`, `time_weekday`, `ts`) SELECT @time_date AS time_date, YEAR(@time_date) AS time_year, MONTH(@time_date) AS time_month, WEEK(@time_date) AS time_week, WEEKDAY(@time_date) AS time_weekday, NOW() AS ts; END IF; -- dim_city INSERT INTO subscription_dwh.`dim_city`(`city_name`, `postal_code`, `country_name`, `ts`) SELECT city_live.city_name, city_live.postal_code, country_live.country_name, Now() FROM subscription_live.city city_live INNER JOIN subscription_live.country country_live ON city_live.country_id = country_live.id LEFT JOIN subscription_dwh.dim_city city_dwh ON city_live.city_name = city_dwh.city_name AND city_live.postal_code = city_dwh.postal_code AND country_live.country_name = city_dwh.country_name WHERE city_dwh.id IS NULL; END// -- CALL p_update_dimensions ()
Đang chạy quy trình này - mà chúng tôi thực hiện bằng quy trình đã nhận xét CALL - chèn một ngày mới và tất cả các thành phố bị thiếu vào bảng thứ nguyên. Hãy thử thêm mã của riêng bạn để điền các giá trị mới vào hai bảng thứ nguyên còn lại.
Quy trình ETL trong Kho dữ liệu
Ý tưởng chính đằng sau kho dữ liệu là chứa dữ liệu tổng hợp ở định dạng mong muốn. Tất nhiên, chúng ta nên biết định dạng đó trước khi bắt đầu xây dựng nhà kho. Nếu chúng tôi đã hoàn thành mọi thứ theo kế hoạch, chúng tôi có thể nhận được tất cả các lợi ích mà DWH mang lại cho chúng tôi. Lợi ích chính là cải thiện hiệu suất khi chạy các truy vấn. Các truy vấn của chúng tôi hoạt động với ít bản ghi hơn (vì chúng được tổng hợp) và chạy trên cơ sở dữ liệu báo cáo (thay vì trực tiếp).
Nhưng trước khi có thể truy vấn, chúng ta cần lưu trữ dữ kiện trong cơ sở dữ liệu của mình. Cách chúng tôi sẽ làm điều đó phụ thuộc vào những gì chúng tôi cần làm với dữ liệu của mình sau này. Nếu chúng ta không có một bức tranh tổng thể tốt trước khi bắt đầu xây dựng DWH của mình, chúng ta có thể sớm gặp rắc rối! sớm thôi.
Tên của quá trình này là ETL:E =Extract, T =Transform, L =Load. Nó lấy dữ liệu, biến đổi nó cho phù hợp với cấu trúc DWH và tải nó trong DWH. Nói một cách chính xác, quy trình thực tế mà chúng tôi sẽ sử dụng là ELT:Trích xuất, Tải, Chuyển đổi. Vì chúng tôi đang sử dụng các quy trình được lưu trữ, chúng tôi sẽ trích xuất dữ liệu, tải dữ liệu và sau đó chuyển đổi dữ liệu để đáp ứng nhu cầu của chúng tôi. Thật tốt khi biết rằng mặc dù ETL và ELT hơi khác nhau, nhưng các thuật ngữ này đôi khi được sử dụng thay thế cho nhau.
Điền các Bảng Dữ kiện
Việc phổ biến các bảng thông tin là lý do tại sao chúng tôi thực sự ở đây. Hôm nay, tôi sẽ điền vào hai bảng dữ kiện, fact_customer_subscribed
bảng và fact_subscription_status
bàn. Hai bảng dữ kiện còn lại là của bạn để làm bài tập về nhà.
Trước khi chuyển sang điền bảng dữ kiện, chúng ta phải giả định rằng bảng thứ nguyên được điền các giá trị mới. Việc điền các bảng dữ kiện theo cùng một mẫu. Vì chúng có cấu trúc giống nhau nên tôi sẽ giải thích cả hai cùng một lúc.
Chúng tôi đang nhóm dữ liệu theo hai thứ nguyên:thời gian và thành phố. Thứ nguyên thời gian sẽ được đặt thành ngày hôm qua và chúng tôi sẽ tìm thấy ID của bản ghi có liên quan trong dim_time
bảng bằng cách so sánh ngày (THAM GIA INNER cuối cùng trong cả hai truy vấn).
ID của dim_city
được trích xuất bằng cách kết hợp tất cả các thuộc tính tạo thành một kết hợp DUY NHẤT trong bảng thứ nguyên (tên thành phố, mã bưu điện và tên quốc gia).
Trong truy vấn này, chúng tôi sẽ kiểm tra các giá trị với CASE và sau đó TÓM TẮT chúng. Đối với khách hàng đang hoạt động và không hoạt động, tôi chưa kiểm tra ngày. Tuy nhiên, tôi đã chọn các giá trị nguyên trạng cho các trường này. Đối với tài khoản mới và tài khoản bị hủy, tôi đã kiểm tra thời gian cập nhật.
DROP PROCEDURE IF EXISTS p_update_facts// CREATE PROCEDURE p_update_facts () BEGIN SET @time_date = DATE_ADD(DATE(NOW()), INTERVAL -1 DAY); -- procedure populates fact tables with new values -- fact_customer_subscribed INSERT INTO `fact_customer_subscribed`(`dim_city_id`, `dim_time_id`, `total_active`, `total_inactive`, `daily_new`, `daily_canceled`, `ts`) SELECT city_dwh.id AS dim_ctiy_id, time_dwh.id AS dim_time_id, SUM(CASE WHEN customer_live.active = 1 THEN 1 ELSE 0 END) AS total_active, SUM(CASE WHEN customer_live.active = 0 THEN 1 ELSE 0 END) AS total_inactive, SUM(CASE WHEN customer_live.active = 1 AND DATE(customer_live.time_updated) = @time_date THEN 1 ELSE 0 END) AS daily_new, SUM(CASE WHEN customer_live.active = 0 AND DATE(customer_live.time_updated) = @time_date THEN 1 ELSE 0 END) AS daily_canceled, MIN(NOW()) AS ts FROM subscription_live.`customer` customer_live INNER JOIN subscription_live.`city` city_live ON customer_live.city_id = city_live.id INNER JOIN subscription_live.`country` country_live ON city_live.country_id = country_live.id INNER JOIN subscription_dwh.dim_city city_dwh ON city_live.city_name = city_dwh.city_name AND city_live.postal_code = city_dwh.postal_code AND country_live.country_name = city_dwh.country_name INNER JOIN subscription_dwh.dim_time time_dwh ON time_dwh.time_date = @time_date GROUP BY city_dwh.id, time_dwh.id; -- fact_subscription_status INSERT INTO `fact_subscription_status`(`dim_city_id`, `dim_time_id`, `total_active`, `total_inactive`, `daily_new`, `daily_canceled`, `ts`) SELECT city_dwh.id AS dim_ctiy_id, time_dwh.id AS dim_time_id, SUM(CASE WHEN subscription_live.active = 1 THEN 1 ELSE 0 END) AS total_active, SUM(CASE WHEN subscription_live.active = 0 THEN 1 ELSE 0 END) AS total_inactive, SUM(CASE WHEN subscription_live.active = 1 AND DATE(subscription_live.time_updated) = @time_date THEN 1 ELSE 0 END) AS daily_new, SUM(CASE WHEN subscription_live.active = 0 AND DATE(subscription_live.time_updated) = @time_date THEN 1 ELSE 0 END) AS daily_canceled, MIN(NOW()) AS ts FROM subscription_live.`customer` customer_live INNER JOIN subscription_live.`subscription` subscription_live ON subscription_live.customer_id = customer_live.id INNER JOIN subscription_live.`city` city_live ON customer_live.city_id = city_live.id INNER JOIN subscription_live.`country` country_live ON city_live.country_id = country_live.id INNER JOIN subscription_dwh.dim_city city_dwh ON city_live.city_name = city_dwh.city_name AND city_live.postal_code = city_dwh.postal_code AND country_live.country_name = city_dwh.country_name INNER JOIN subscription_dwh.dim_time time_dwh ON time_dwh.time_date = @time_date GROUP BY city_dwh.id, time_dwh.id; END// -- CALL p_update_facts ()
Một lần nữa, tôi đã nhận xét đến dòng cuối cùng. Xóa nhận xét và bạn có thể sử dụng dòng này để gọi thủ tục và chèn các giá trị mới. Xin lưu ý rằng tôi chưa xóa bất kỳ giá trị cũ hiện có nào, vì vậy quy trình này sẽ không hoạt động nếu chúng tôi đã có các giá trị cho ngày và thành phố đó. Điều này có thể được giải quyết bằng cách thực hiện xóa trước khi chèn.
Hãy nhớ rằng, chúng ta cần điền các bảng dữ kiện còn lại vào DWH của chúng ta. Tôi khuyến khích bạn tự mình thử điều đó!
Một điều khác mà tôi chắc chắn sẽ khuyên bạn là đặt toàn bộ quy trình bên trong một giao dịch. Điều đó sẽ đảm bảo rằng tất cả các lần chèn đều thành công hoặc không có lần chèn nào được thực hiện. Điều này rất quan trọng khi chúng tôi muốn tránh việc dữ liệu bị chèn một phần, ví dụ:nếu chúng ta có nhiều thủ tục để chèn thứ nguyên và dữ kiện và một số trong số đó thực hiện được công việc của mình trong khi những thủ tục khác không thành công.
Bạn nghĩ gì?
Hôm nay, chúng tôi đã thấy cách chúng tôi có thể thực hiện quy trình ELT / ETL và tải dữ liệu từ cơ sở dữ liệu trực tiếp vào kho dữ liệu. Mặc dù quy trình chúng tôi đã trình bày khá đơn giản, nhưng nó chứa tất cả các yếu tố cần thiết để E (xtract) dữ liệu, T (ransform) nó thành một định dạng phù hợp, và cuối cùng là L (đưa) nó vào DWH. Bạn nghĩ sao? Hãy cho chúng tôi biết trải nghiệm của bạn trong phần bình luận bên dưới.