2009-08-09 31 views
7

Tôi có một bảng gọi là prices bao gồm giá đóng cửa của các cổ phiếu mà tôi đang theo dõi hàng ngày.Làm cách nào để tôi có thể tính toán% thay đổi giá hàng ngày bằng cách sử dụng MySQL?

Dưới đây là lược đồ:

CREATE TABLE `prices` (
    `id` int(21) NOT NULL auto_increment, 
    `ticker` varchar(21) NOT NULL, 
    `price` decimal(7,2) NOT NULL, 
    `date` timestamp NOT NULL default CURRENT_TIMESTAMP, 
    PRIMARY KEY (`id`), 
    KEY `ticker` (`ticker`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=2200 ; 

Tôi cố gắng để tính toán mức giảm% giá cho bất cứ thứ gì có giá trị giá lớn hơn 0 cho ngày hôm nay và ngày hôm qua. Theo thời gian, bảng này sẽ rất lớn và tôi lo lắng về hiệu suất. Tôi cho rằng điều này sẽ phải được thực hiện ở phía MySQL thay vì PHP vì LIMIT sẽ là cần thiết ở đây.

Làm cách nào để có 2 ngày cuối cùng và thực hiện tính toán% thả trong MySQL?

Mọi lời khuyên sẽ được đánh giá cao.

Trả lời

4

Một vấn đề tôi nhìn thấy ngay lập tức được sử dụng một kiểu dữ liệu timestamp cho ngày, điều này sẽ làm phức tạp truy vấn sql của bạn vì hai lý do - bạn sẽ phải sử dụng một phạm vi hoặc chuyển đổi sang một ngày thực tế trong bạn điều khoản, nhưng, quan trọng hơn, vì bạn nói rằng bạn quan tâm đến giá đóng cửa ngày hôm nay và giá đóng cửa ngày hôm qua, bạn sẽ phải theo dõi những ngày khi thị trường mở cửa - vì vậy truy vấn của Thứ Hai khác với tue - fri và bất kỳ ngày nào thị trường đóng cửa cho một kỳ nghỉ cũng sẽ phải được tính toán.

Tôi sẽ thêm một cột như mktDay và tăng nó mỗi ngày thị trường mở cửa cho doanh nghiệp. Một cách tiếp cận khác có thể bao gồm cột 'previousClose' khiến cho việc tính toán của bạn tầm thường. Tôi nhận ra điều này vi phạm hình thức bình thường, nhưng nó giúp tiết kiệm tiền tự tham gia đắt tiền trong truy vấn của bạn.

Nếu bạn không thể thay đổi cấu trúc, bạn sẽ tự tham gia để đóng cửa ngày hôm qua và bạn có thể tính% thay đổi và thứ tự bằng% thay đổi đó nếu bạn muốn.

Dưới đây là mã của Eric, dọn dẹp một chút nó thực hiện trên máy chủ chạy mysql tôi 5.0.27

select 
    p_today.`ticker`, 
    p_today.`date`, 
    p_yest.price as `open`, 
    p_today.price as `close`, 
    ((p_today.price - p_yest.price)/p_yest.price) as `change` 
from 
    prices p_today 
    inner join prices p_yest on 
     p_today.ticker = p_yest.ticker 
     and date(p_today.`date`) = date(p_yest.`date`) + INTERVAL 1 DAY 
     and p_today.price > 0 
     and p_yest.price > 0 
     and date(p_today.`date`) = CURRENT_DATE 
order by `change` desc 
limit 10 

Note back-ve như một số các tên cột của bạn và bí danh của Eric được dành lời.

Cũng lưu ý rằng việc sử dụng một mệnh đề where cho bảng đầu tiên sẽ là một truy vấn ít tốn kém - nơi được của thực hiện đầu tiên và chỉ có cố gắng tự tham gia vào các hàng là lớn hơn không và có ngày hôm nay

select 
    p_today.`ticker`, 
    p_today.`date`, 
    p_yest.price as `open`, 
    p_today.price as `close`, 
    ((p_today.price - p_yest.price)/p_yest.price) as `change` 
from 
    prices p_today 
    inner join prices p_yest on 
     p_today.ticker = p_yest.ticker 
     and date(p_today.`date`) = date(p_yest.`date`) + INTERVAL 1 DAY 

     and p_yest.price > 0 
where p_today.price > 0 
    and date(p_today.`date`) = CURRENT_DATE 
order by `change` desc 
limit 10 
+0

@Scott: Cảm ơn bạn đã bình luận. Tôi có thể thay đổi dấu thời gian chỉ là ngày để làm cho mọi thứ dễ dàng hơn là phải đối phó với các phạm vi. –

+0

@Knix Chức năng ngày khá sạch sẽ, không chắc chắn nó đắt tiền như thế nào, nhưng chắc chắn là cuộc gọi của bạn. Vẫn còn một vấn đề về thị trường đóng cửa vào cuối tuần và ngày lễ. trướcĐóng cột loại bỏ sự tự tham gia, sự lộn xộn của những ngày thị trường đóng cửa với chi phí sao chép dữ liệu và phải biết trước khi đóng ngày hôm nay. – Scott

+0

Cảm ơn ... Tôi sẽ đưa ra lời khuyên của bạn! –

2

Về cơ bản, bạn chỉ có thể tham gia bảng để tìm% thay đổi đã cho. Sau đó, đặt hàng theo số change giảm dần để có được những thay đổi lớn nhất ở trên cùng. Bạn thậm chí có thể đặt hàng theo số abs(change) nếu bạn muốn thay đổi lớn nhất.

select 
    p_today.ticker, 
    p_today.date, 
    p_yest.price as open, 
    p_today.price as close, 
    --Don't have to worry about 0 division here 
    (p_today.price - p_yest.price)/p_yest.price as change 
from 
    prices p_today 
    inner join prices p_yest on 
     p_today.ticker = p_yest.ticker 
     and date(p_today.date) = date(date_add(p_yest.date interval 1 day)) 
     and p_today.price > 0 
     and p_yest.price > 0 
     and date(p_today.date) = CURRENT_DATE 
order by change desc 
limit 10 
+0

Xin chào Eric. Cảm ơn các giải pháp. Tôi gặp lỗi khi thực hiện truy vấn mặc dù: # 1064 - Bạn có lỗi trong cú pháp SQL của mình; kiểm tra hướng dẫn tương ứng với phiên bản máy chủ MySQL của bạn để có cú pháp đúng để sử dụng gần 'thay đổi từ giá p_today giá tham gia nội bộ p_yest trên p_today'. tại dòng 6 –

+0

@Knix: Huh. Tôi không có một cá thể MySQL để thử điều này, nhưng điều gì sẽ xảy ra nếu bạn lấy chú thích ra, và sau đó là cột 'thay đổi' ra? – Eric

+0

@Eric: Tôi đã đưa ra nhận xét và cũng là 2 dòng có thay đổi trong đó nhưng vẫn gặp lỗi. Tôi nghĩ rằng nó không giống như dòng 'và ngày (p_today.date) = CURRENT_DATE' vì 'ngày' có màu đỏ trong thông báo lỗi. –

3

Scott mang đến một điểm tuyệt vời về những ngày thị trường liên tiếp. Tôi khuyên bạn nên xử lý này với một bảng kết nối như:

CREATE TABLE `market_days` ( 
    `market_day` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT, 
    `date` DATE NOT NULL DEFAULT '0000-00-00', 
    PRIMARY KEY USING BTREE (`market_day`), 
    UNIQUE KEY USING BTREE (`date`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=0 
; 

Khi ngày càng nhiều ngày trôi qua trên thị trường, chỉ INSERTdate giá trị mới trong bảng. market_day sẽ tăng tương ứng.

Khi chèn prices dữ liệu, tra cứu LAST_INSERT_ID() hoặc giá trị tương ứng với một giá trị đã cho trước đây cho date.

Đối với prices bảng riêng của mình, bạn có thể thực hiện lưu trữ, SELECTINSERT hoạt động hiệu quả hơn với một PRIMARY KEY hữu ích và không AUTO_INCREMENT cột. Trong lược đồ bên dưới, PRIMARY KEY của bạn chứa thông tin hữu ích thực chất và không chỉ là một quy ước để xác định các hàng duy nhất. Sử dụng MEDIUMINT (3 byte) thay vì INT (4 byte) tiết kiệm thêm một byte cho mỗi hàng và quan trọng hơn 2 byte cho mỗi hàng trong PRIMARY KEY - tất cả trong khi vẫn affording hơn 16 triệu ngày có thể và biểu tượng ticker (mỗi).

CREATE TABLE `prices` ( 
    `market_day` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', 
    `ticker_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', 
    `price` decimal (7,2) NOT NULL DEFAULT '00000.00', 
    PRIMARY KEY USING BTREE (`market_day`,`ticker_id`), 
    KEY `ticker_id` USING BTREE (`ticker_id`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 
; 

Trong sơ đồ này mỗi hàng là duy nhất trên mỗi cặp market_dayticker_id. Dưới đây ticker_id tương ứng với một danh sách các biểu tượng ticker trong một bảng tickers với một sơ đồ tương tự như market_days bảng:

CREATE TABLE `tickers` ( 
    `ticker_id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT, 
    `ticker_symbol` VARCHAR(5), 
    `company_name` VARCHAR(50), 
    /* etc */ 
    PRIMARY KEY USING BTREE (`ticker_id`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=0 
; 

Điều này mang lại một truy vấn tương tự như những người khác đề xuất, nhưng với hai sự khác biệt quan trọng: 1) Không có sự biến đổi chức năng vào cột ngày, phá hủy khả năng của MySQL để sử dụng các khóa trên tham gia; trong truy vấn bên dưới MySQL sẽ sử dụng một phần của số PRIMARY KEY để tham gia trên market_day. 2) MySQL chỉ có thể sử dụng một khóa cho mỗi mệnh đề JOIN hoặc WHERE. Trong truy vấn này, MySQL sẽ sử dụng chiều rộng đầy đủ của PRIMARY KEY (market_dayticker_id) trong khi trong truy vấn trước nó chỉ có thể sử dụng một (MySQL thường sẽ chọn lọc nhiều hơn hai).

SELECT 
    `market_days`.`date`, 
    `tickers`.`ticker_symbol`, 
    `yesterday`.`price` AS `close_yesterday`, 
    `today`.`price` AS `close_today`, 
    (`today`.`price` - `yesterday`.`price`)/(`yesterday`.`price`) AS `pct_change` 
FROM 
    `prices` AS `today` 
LEFT JOIN 
    `prices` AS `yesterday` 
    ON /* uses PRIMARY KEY */ 
    `yesterday`.`market_day` = `today`.`market_day` - 1 /* this will join NULL for `today`.`market_day` = 0 */ 
    AND 
    `yesterday`.`ticker_id` = `today`.`ticker_id` 
INNER JOIN 
    `market_days` /* uses first 3 bytes of PRIMARY KEY */ 
    ON 
    `market_days`.`market_day` = `today`.`market_day` 
INNER JOIN 
    `tickers` /* uses KEY (`ticker_id`) */ 
    ON 
    `tickers`.`ticker_id` = `today`.`ticker_id` 
WHERE 
    `today`.`price` > 0 
    AND 
    `yesterday`.`price` > 0 
; 

Một điểm tốt hơn là sự cần thiết cũng tham gia chống lại tickersmarket_days để hiển thị thực tế ticker_symboldate, nhưng các hoạt động này là rất nhanh kể từ khi họ sử dụng các phím.

+0

đây là một lược đồ và tập hợp các truy vấn nghiêm túc hơn nhiều – philfreo

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