Làm cách nào tôi có thể cải thiện câu lệnh SQL của mình với kết quả hàng tuần với tuần bắt đầu vào thứ năm hoặc bất kỳ ngày nào khác trong tuần?


8

Tôi là một người hoàn toàn mới và tôi không thể tìm thấy một cách tốt để làm điều này ở bất cứ đâu. Tôi có một bảng cơ sở dữ liệu chứa số liệu thống kê được ghi lại vào nhiều thời điểm khác nhau trong suốt cả tuần. Tuần báo cáo bắt đầu vào thứ năm. Bảng chứa cột dữ liệu (ngày) lưu trữ khi dữ liệu được ghi.

Tôi cần lấy dữ liệu trong một tuần nhất định (tuần báo cáo bắt đầu vào thứ năm). Tôi đã viết các truy vấn sau đây:

   SELECT * 
FROM `table` 
WHERE 1 = CASE 
  WHEN WEEKDAY(NOW()) = 0 THEN DATEDIFF(NOW(),`date`) BETWEEN -2 AND 4
  WHEN WEEKDAY(NOW()) = 1 THEN DATEDIFF(NOW(),`date`) BETWEEN -1 AND 5
  WHEN WEEKDAY(NOW()) = 2 THEN DATEDIFF(NOW(),`date`) BETWEEN -0 AND 6
  WHEN WEEKDAY(NOW()) = 3 THEN DATEDIFF(NOW(),`date`) BETWEEN -6 AND 0
  WHEN WEEKDAY(NOW()) = 4 THEN DATEDIFF(NOW(),`date`) BETWEEN -5 AND 1
  WHEN WEEKDAY(NOW()) = 5 THEN DATEDIFF(NOW(),`date`) BETWEEN -4 AND 2
  WHEN WEEKDAY(NOW()) = 6 THEN DATEDIFF(NOW(),`date`) BETWEEN -3 AND 3
END

Điều này dường như để làm việc trên thử nghiệm ban đầu. Nhưng tôi không chắc đó là cách tốt nhất để nói về nó. Tôi không biết nhiều về hiệu suất của MySQL, nhưng sẽ có hơn một trăm nghìn bản ghi để lọc. Truy vấn này sẽ thực sự chậm do số lượng điều kiện được kiểm tra?

Hàm NOW () được sử dụng khi lấy báo cáo mới nhất - tuy nhiên, một số trường hợp tôi sẽ cần làm báo cáo cho các tuần khác - vì vậy tôi sẽ thay thế một ngày khác vào vị trí.

Ngoài ra, thực hiện theo cách này yêu cầu viết lại truy vấn nếu tuần báo cáo thay đổi - giả sử ngày bắt đầu thay đổi thành Thứ Tư.

Tôi không thể sử dụng hàm TUẦN () vì bạn chỉ có thể bắt đầu một tuần trên Mặt trời hoặc Thứ hai với nó.

Bất kỳ ý tưởng để cải thiện truy vấn này được nhiều đánh giá cao!

Ghi chú khác: Hiện đang sử dụng MariaDB 5.3.

Câu trả lời:


5

Đây là một truy vấn tôi đã viết để cung cấp cho bạn thứ năm gần đây nhất và thứ tư kết thúc

SELECT thuwk_beg + INTERVAL 0 second thu_beg,
thuwk_beg + INTERVAL 604799 second wed_end
FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A;

Dưới đây là một ví dụ cho ngày hôm nay, 2011-09-21

mysql> SELECT
    -> thuwk_beg + INTERVAL 0 second thu_beg,
    -> thuwk_beg + INTERVAL 604799 second wed_end
    -> FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
    -> FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
    -> FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A;
+---------------------+---------------------+
| thu_beg             | wed_end             |
+---------------------+---------------------+
| 2011-09-15 00:00:00 | 2011-09-21 23:59:59 |
+---------------------+---------------------+
1 row in set (0.00 sec)

Chỉ cần thay thế các lệnh gọi hàm NOW () bằng bất kỳ datetime nào bạn thích và bạn sẽ có tuần bắt đầu từ thứ năm mọi lúc cho datetime bạn chọn.

Đây là một ví dụ khác sử dụng ngày cụ thể '2011-01-01'

mysql> SELECT
    -> thuwk_beg + INTERVAL 0 second thu_beg,
    -> thuwk_beg + INTERVAL 604799 second wed_end
    -> FROM (SELECT (DATE('2011-01-01') - INTERVAL daysbacktothursday DAY) thuwk_beg
    -> FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
    -> FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE('2011-01-01') dt) AAAA) AAA) AA) A;
+---------------------+---------------------+
| thu_beg             | wed_end             |
+---------------------+---------------------+
| 2010-12-30 00:00:00 | 2011-01-05 23:59:59 |
+---------------------+---------------------+
1 row in set (0.00 sec)

Truy vấn tabletham chiếu của bạn ngày hôm nay sẽ giống như thế này:

SELECT * from `table`,
(SELECT thuwk_beg + INTERVAL 0 second thu_beg,
thuwk_beg + INTERVAL 604799 second wed_end
FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A) M
WHERE `date` >= thu_beg
AND `date` <= wed_end;

Hãy thử một lần !!!

CẬP NHẬT 2011-09-22 16:27 EDT

Đây là đề xuất của tôi để đánh dấu Thu-Wed.

SELECT thuwk_beg + INTERVAL 0 second thu_beg,
thuwk_beg + INTERVAL 604799 second wed_end
FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A;

Còn những tuần khác thì sao ???

  • (SELECT SUBSTR('6012345',wkndx,1) tuần bắt đầu từ Mon kết thúc
  • (SELECT SUBSTR('5601234',wkndx,1) tuần bắt đầu thứ ba kết thúc thứ hai
  • (SELECT SUBSTR('4560123',wkndx,1) tuần bắt đầu thứ tư kết thúc thứ ba
  • (SELECT SUBSTR('3456012',wkndx,1) tuần bắt đầu từ Thu kết thúc
  • (SELECT SUBSTR('2345601',wkndx,1) tuần bắt đầu thứ sáu kết thúc Thu
  • (SELECT SUBSTR('1234560',wkndx,1) tuần bắt đầu thứ bảy kết thúc vào thứ sáu
  • (SELECT SUBSTR('0123456',wkndx,1) tuần bắt đầu từ mặt trời kết thúc Sat

1

Hãy để tôi nói lại những gì bạn đang yêu cầu để đảm bảo rằng tôi hiểu đúng:

Bạn muốn kéo tất cả các hồ sơ trong 7 ngày qua một ngày được chỉ định?

Nó có thể trông giống như:

select * from table where `date` between $date - interval 7 day and $date

Điều quan trọng cần lưu ý là $ date không phải là cú pháp mysql theo nghĩa đen mà chỉ là một trình giữ chỗ ví dụ cho ngày bắt đầu mà bạn muốn. Nếu đây là để báo cáo tôi tưởng tượng truy vấn cuối cùng sẽ được tạo ra từ một số tập lệnh? Nếu điều đó đúng, nó có thể đơn giản hơn trong ngôn ngữ đó và sau đó chuyển vào giá trị bằng chữ như một phần của truy vấn được xây dựng.

Tôi là một fan hâm mộ của việc giữ các truy vấn đơn giản nhất có thể vì vậy tôi sẽ để nó như thế. Tôi sẽ chừa chỗ cho những người khác đóng góp câu trả lời để thực hiện những gì bạn muốn trong một truy vấn SQL-fu duy nhất.

Chỉnh sửa: Sau khi đọc lại bài đăng của bạn, có vẻ như bạn có thể đang sử dụng loại ngày. Trong trường hợp đó, khối sau in nghiêng có thể là dư thừa. Tôi để nó trong trường hợp nó hữu ích cho người khác (và vì tôi đã dành thời gian để viết nó :-)

Bạn nói rằng bạn đang sử dụng một "datestamp"? Đó không phải là một kiểu dữ liệu Mysql. Đây có phải là một mốc thời gian, dấu thời gian hoặc ngày (thời gian và năm cũng tồn tại nhưng từ ngữ cảnh của bạn, tôi cảm thấy chúng không được áp dụng)? Tôi hỏi bởi vì bạn có thể muốn làm cho nó chỉ là một cột ngày thay vì những người khác. Nếu đây là lựa chọn phù hợp với bạn thực sự phụ thuộc vào chi tiết về cách sử dụng cột và bảng được truy vấn trên tất cả. Nếu mục đích duy nhất của nó chỉ là kéo các bản ghi trong phạm vi ngày, bất kể thời gian, thì ngày chắc chắn là cách để đi. Đối với một, nó chỉ yêu cầu 3 byte thay vì 4 hoặc 8. Tôi có thể giải thích các lý do khác mà bạn muốn sử dụng ngày nếu nó phù hợp với yêu cầu sử dụng của bạn (ví dụ: bạn không quan tâm đến phần thời gian). Bạn có thể tìm thấy chi tiết về các loại khác nhau tại http://dev.mysql.com/doc/refman/5.0/en/datetime.html

http://dev.mysql.com/doc/refman/5.0/en/st Storage-request.html

Nếu và chỉ khi bạn đang sử dụng ngày cụ thể, bạn có thể cân nhắc những điều sau:

Chạy truy vấn có tiền tố với "giải thích". Chi tiết về cách diễn giải đầu ra và những gì tốt nhất có thể được tìm thấy tại http://dev.mysql.com/doc/refman/5.0/en/explain-output.html

Khi bạn đã giải thích, thay đổi truy vấn thành

select * from table where date in ("N", "N+1"..."N+7")

nơi bạn liệt kê tất cả các ngày riêng lẻ mà bạn quan tâm. Tôi đã gặp các tình huống rõ ràng MySQL không đủ thông minh để sử dụng một truy vấn phạm vi (nghĩa là giữa x và y) liệt kê các giá trị cụ thể cho các tập hợp nhỏ.

Bất cứ trường hợp sử dụng nào bạn cũng muốn đảm bảo rằng cột đó được lập chỉ mục nếu bạn thực hiện các truy vấn báo cáo thường xuyên dựa trên các giá trị của nó.


1

Ông @Rolando rõ ràng trả lời câu hỏi nhưng tôi sẽ đề xuất một quyết định khác cho phép bạn thao tác và tùy chỉnh lịch giữa các múi giờ và nhóm quan trọng nhất theo tuần được xác định theo các múi giờ khác nhau

vì vậy, giả sử rằng myQuery của bạn đang chạy trên máy chủ được định cấu hình UTC và bạn muốn có một lịch tùy chỉnh trước 7 giờ và do đó tuần của bạn sẽ bắt đầu vào thứ bảy 7 giờ sáng

CREATE TABLE `wh_blur_calendar` (
  `date` timestamp NOT NULL ,
  `y` smallint(6) DEFAULT NULL,
  `q` tinyint(4) DEFAULT NULL,
  `m` tinyint(4) DEFAULT NULL,
  `d` tinyint(4) DEFAULT NULL,
  `w` tinyint(4) DEFAULT NULL,
  PRIMARY KEY (`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `wh_ints` (
  `i` tinyint(4) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into wh_ints values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);

bây giờ là một tham gia Cartesian phổ biến sẽ điền vào bảng của bạn:

INSERT INTO wh_blur_calendar (date)
SELECT DATE('2010-01-01 00:00:00 ') + INTERVAL a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i DAY
FROM wh_ints a JOIN wh_ints b JOIN wh_ints c JOIN wh_ints d JOIN wh_ints e
WHERE (a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i) < 10245
ORDER BY 1;

Cho phép cập nhật giờ:

update  db_wh_repo.wh_blur_calendar set date = date_add(date, interval 7 hour);

và cuối cùng sắp xếp tuần theo lịch của bạn theo cách tùy chỉnh

UPDATE wh_blur_calendar
SET 
    y = YEAR(date),
    q = quarter(date),
    m = MONTH(date),
    d = dayofmonth(date),
    w = week(date_add((date), interval 1 day));

Tin tôi đi, tôi dành vài giờ để đưa ra quyết định này nhưng nó mang lại cho bạn rất nhiều sự tự do trong trường hợp bạn muốn nhóm kết quả của mình dựa trên định nghĩa múi giờ và tuần tùy chỉnh.

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.