2010-02-08 52 views
5

Tôi đang cố sắp xếp một tập hợp kết quả cung cấp cho 5 người dùng gần nhất được sắp xếp theo ngày sinh sắp tới. Điều này hoạt động hoàn hảo cho đến khi năm nhuận nhảy vào chơi. Ví dụ:lời nhắc sinh nhật mysql, năm nhuận

  • 15 tháng năm - 96 ngày còn lại
  • 15 tháng năm - 97 ngày còn lại

Kết quả đầu là một sinh tại năm 1987 và thấp hơn là từ năm 1988. u_birth được lưu giữ như yyyy-mm-dd Có cách nào đơn giản để sắp xếp vấn đề này mà không phải viết lại toàn bộ truy vấn?

SELECT u_birth, IF(DAYOFYEAR(u_birth) >= DAYOFYEAR(NOW()), 
      DAYOFYEAR(u_birth) - DAYOFYEAR(NOW()), 
      DAYOFYEAR(u_birth) - DAYOFYEAR(NOW()) + 
     DAYOFYEAR(CONCAT(YEAR(NOW()), '-12-31')) 
) 
AS distance 
FROM (blog_users) 
WHERE `s_agehide` = 0 
ORDER BY distance ASC 
LIMIT 5 

truy vấn này được thực hiện và sửa đổi từ hướng dẫn mysql: http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#c7489

Trả lời

7

Rõ ràng là có vấn đề nếu thuật toán của bạn tùy thuộc vào năm sinh của người đó. Để giải quyết vấn đề này, trước tiên hãy tìm sinh nhật tiếp theo của mỗi người sau ngày hiện tại, sau đó tính toán sự khác biệt giữa ngày đó và bây giờ.

SELECT u_birth, DATEDIFF(next_birthday, NOW()) AS distance FROM (
    SELECT *, ADDDATE(birthday, INTERVAL birthday < DATE(NOW()) YEAR) AS next_birthday 
    FROM (
     SELECT *, ADDDATE(u_birth, INTERVAL YEAR(NOW()) - YEAR(u_birth) YEAR) AS birthday 
     FROM blog_users 
     WHERE s_agehide = 0 
    ) AS T1 
) AS T2 
ORDER BY distance ASC 
LIMIT 5 

Kết quả:

'1992-02-29', 20 
'1993-03-01', 21 
'1987-05-15', 96 
'1988-05-15', 96 
'1988-09-18', 222 

Test data:

CREATE TABLE blog_users (u_birth NVARCHAR(100) NOT NULL, s_agehide INT NOT NULL); 
INSERT INTO blog_users (u_birth, s_agehide) VALUES 
('1987-05-15', 0), 
('1988-05-15', 0), 
('1988-09-20', 0), 
('2000-01-02', 0), 
('2000-01-03', 1), 
('1988-09-19', 0), 
('1988-09-18', 0), 
('1992-02-29', 0), 
('1993-03-01', 0); 

Lưu ý rằng ai đó sinh ra vào một ngày bước nhảy vọt được giả định có một ngày sinh nhật của 28 tháng hai trên một năm không nhuận.

Ngoài ra, truy vấn của bạn không bao gồm id người dùng của người dùng. Bạn có thể muốn thêm điều này quá, tôi tưởng tượng.

+0

Cảm ơn! Tôi sẽ có một cái nhìn, gimme một vài phút để thử nó ra =) – moodh

+0

@tired: Lưu ý rằng tôi đã thực hiện một thay đổi nhỏ để tất cả những người có ngày sinh nhật ngày nay có khoảng cách 0, không phải khoảng cách 365. Tôi đã thay đổi 'NOW()' thành 'DATE (NOW())'. –

+0

Nó hoạt động như nó được cho là, tuy nhiên tôi có một câu hỏi tiếp theo: Làm cách nào để truy xuất các hàng bổ sung từ bảng? Tôi đã cố gắng trong cả ba lựa chọn nhưng không may mắn, cho phép nói rằng tôi muốn lấy s_agehide quá. Cảm ơn tất cả sự giúp đỡ! – moodh

1

Không sử dụng dayofyear như bất cứ điều gì sau ngày 29 tháng hai trong một năm nhuận là một ngày nào đó thêm vào hơn bình thường. Thay vào đó, trích xuất các tháng và ngày và concat chúng cùng với năm nay. Sau đó, làm so sánh.

Giống như vậy:

SELECT u_birth, 
    IF(DATE(CONCAT(YEAR(NOW()),'-',MONTH(u_birth),'-',DAY(u-birth))) >= DATE(NOW()), 
... 

ETA:

Chỉ vì vậy tôi không cần phải viết lại biểu thức để chuyển đổi ngày tháng năm sinh để sinh nhật, tôi sẽ dính vào nó trong một biến. Tôi khuyên bạn nên viết một hàm thực hiện chuyển đổi để bạn có thể sử dụng nó trong truy vấn trực tiếp.

SET @birthday= SELECT DATE(CONCAT(YEAR(NOW()),'-',MONTH(u_birth),'-',DAY(u-birth))) FROM users WHERE user_id=x; //obviously only good for one value, but use the example for the calculation 

SELECT u_birth, 
     IF(DATE(@birthday)>=DATE(NOW()), 
      DATEDIFF(DATE_ADD(@birthday, INTERVAL 1 YEAR),NOW()), 
      DATEDIFF(NOW(),@birthday) 
     ) as days 
FROM users WHERE user_id=x; 
+0

Tôi đã thử thay thế tất cả các ngày trong năm bằng các ví dụ bạn đã cho thấy, có, vấn đề năm nhuận sẽ biến mất nhưng giá trị và sắp xếp hoàn toàn tắt. Bạn có thể vui lòng chỉ cho tôi toàn bộ truy vấn được viết lại không? – moodh

1

Có thể ai đó lấy cảm hứng từ sự yên bình mã này.

Nó làm việc một cách hoàn hảo trong trường hợp của tôi, nhưng tôi nghĩ rằng có đến nhiều ngày-tính ...

SELECT CONCAT(YEAR(CURRENT_DATE()),'-',DATE_FORMAT(FROM_UNIXTIME(date_of_birth), '%m-%d')), fe_users.* 
FROM `fe_users` 
WHERE `date_of_birth` != 0 AND (
    CONCAT(YEAR(CURRENT_DATE())+1,'-',DATE_FORMAT(FROM_UNIXTIME(date_of_birth), '%m-%d')) BETWEEN CURRENT_DATE() AND DATE_ADD(CURRENT_DATE(), INTERVAL 7 DAY) 
    OR 
    CONCAT(YEAR(CURRENT_DATE()),'-',DATE_FORMAT(FROM_UNIXTIME(date_of_birth), '%m-%d')) BETWEEN CURRENT_DATE() AND DATE_ADD(CURRENT_DATE(), INTERVAL 7 DAY) 
) 

Bằng cách hòa bình này của SQL-Code bạn sẽ nhận được tất cả người dùng của fe_users bảng mà date_of_birth (lưu là unix-timestamp) sẽ trong vòng 7 ngày tới. Bạn có thể dễ dàng mở rộng này đến 21 ngày bằng cách thay đổi khoảng thời gian được sử dụng bởi hàm date_add().

+0

Nhân tiện - tập lệnh này là tốt nhất trong trường hợp hoạt động - như tôi đã thử nghiệm ... – SimonSimCity

2
SELECT 
    CONCAT(
     YEAR(CURRENT_DATE()), 
     '-', 
     DATE_FORMAT((birthDate), 
      '%m-%d' 
     ) 
    ), 
    fullName 
FROM 
    employees 
WHERE 
    birthDate != 0 
AND(
    CONCAT(
     YEAR(CURRENT_DATE())+ 1, 
     '-', 
     DATE_FORMAT((birthDate), 
      '%m-%d' 
     ) 
    )BETWEEN CURRENT_DATE() 
    AND DATE_ADD(
     CURRENT_DATE(), 
     INTERVAL 21 DAY 
    ) 
    OR CONCAT(
     YEAR(CURRENT_DATE()), 
     '-', 
     DATE_FORMAT((birthDate), 
      '%m-%d' 
     ) 
    )BETWEEN CURRENT_DATE() 
    AND DATE_ADD(
     CURRENT_DATE(), 
     INTERVAL 21 DAY 
    ) 
) 

ps: sinh là cột nơi tôi lưu trữ ngày tháng năm sinh của người lao động. nhân viên là tên bảng.

Các vấn đề liên quan