2009-08-09 18 views
7

Her gün izlediğim stokların kapanış fiyatını içeren prices adlı bir tablo var. Ben bugün ve dün için bir fiyat değeri 0'dan büyük sahiptir şey için% fiyat düşüşü hesaplamak çalışıyorumMySQL kullanarak en üst günlük fiyat değişikliklerini nasıl hesaplayabilirim?

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 ; 

: Burada

şema olduğunu. Zamanla, bu tablo çok büyük olacak ve performans konusunda endişeliyim. Bunun, PHP yerine MySQL tarafında yapılması gerekeceğini düşünüyorum, çünkü burada LIMIT gerekli olacaktır.

Son 2 tarihi nasıl yaparım ve MySQL'deki% drop hesaplamasını nasıl yaparım?

Her türlü öneri çok takdir edilecektir.

cevap

4

bir problem, bu iki nedenden dolayı sql sorgusu karmaşık hale getirecektir - Eğer gerçek bir tarihe aralığı kullanır veya dönüştürmek zorunda kalacak senin Ancak, daha önemlisi, bugünkü kapanış fiyatı ve dünkü kapanış fiyatı ile ilgilendiğinizi belirttiğinizden beri, piyasanın açık olduğu günleri takip etmeniz gerekir - bu yüzden, Pazartesi günkü sorgusu tue - fri'den farklıdır ve herhangi bir gün pazarın bir tatil için kapatılması da hesaba katılması gerekecek.

MktDay gibi bir sütun eklerim ve pazarın her gün iş için açık olduğunu artırırdım. Başka bir yaklaşım, hesabınızı önemsiz kılan bir 'previousClose' sütunu eklemek olabilir. Bunun normal formu ihlal ettiğini anlıyorum, ancak sorgunuza pahalı bir öz katılma kaydeder.

Yapısını değiştiremiyorsanız, o zaman dün kapanışını elde etmek için kendi kendine birleştirme yapacaksınız ve isterseniz% değişikliği ve siparişi% olarak değiştirebilirsiniz.

Aşağıda benim sunucu çalışan mysql sütun isimleri ve Eric'in diğer adları bazı geri-kene kelimeleri saklıdır edildi 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 

Not çalıştırılan Biraz temizlik Eric'in koddur.

Ayrıca, ilk tablo için nereye maddesini kullanarak daha az pahalı sorgu olacağını unutmayın - ilk idam nerede ve yalnızca bugünün tarihi öz girişimi sıfırdan büyük olan satırlarda katılıp olması gerekiyor

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: Yorumunuz için teşekkürler. Zaman aralığını, aralıklarla uğraşmaktan ziyade işleri kolaylaştırmak için sadece tarihe kadar değiştirebilirim. –

+0

@Knix Tarih işlevi oldukça temiz, ne kadar pahalı olduğundan emin değilsiniz, fakat kesinlikle aramanız. Hafta sonları ve tatil günlerinde kapalı olan pazar hakkında hala bir sorun var. previousClose sütunu, kendiliğinden birleşmeyi, kapalı piyasa günlerinin dağınıklığını ortadan kaldırarak veriyi çoğaltmaktan ve günümüzün kapanışını kapattığını bilmek zorundadır. – Scott

+0

Teşekkürler ... Tavsiyenizi alacağım! –

2

Özellikle, belirtilen% değişikliği bulmak için tabloya kendiniz katılabilirsiniz. Ardından, en büyük değiştiriciyi almak için change'a göre sıralayın. En büyük salınımları istiyorsanız, ile sipariş verebilirsiniz. Ben tarih için bir zaman damgası veri türünü kullanıyor derhal kapalı bkz

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

Merhaba Eric. Çözüm için teşekkürler. Sorguyu çalıştırırken bir hata alıyorum: # 1064 - SQL sözdiziminizde bir hata var; ' fiyatlarından fiyatından fiyatına arası fiyatlara yakın kullanım için MySQL sunucu sürümünüze karşılık gelen kılavuzu gözden geçirin. p_you p_yest ürününün p_today. satır 6'da –

+0

@ Knix: Huh. Bunu denemek için bir MySQL örneğim yok, ancak yorumu çıkarırsanız ve sonra 'change' sütunu çıktığında ne olur? – Eric

+0

@Eric: Bu yorumu ve aynı zamanda değiştiren 2 satırı aldım ama yine de hatayı aldım. 'Tarih' (p_today.date) = CURRENT_DATE 'satırını beğenmediğini düşünüyorum çünkü hata mesajında' date 'kırmızı renktedir. –

3

Scott, ardışık pazar günleri hakkında harika bir noktaya işaret ediyor. Ben böyle bir konektör tabloyla bu işleme öneririz: daha fazla pazar günleri geçmesi

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 
; 

olarak, tabloda sadece INSERT yeni date değerleri. market_day buna göre artırılacaktır.

prices verilerini eklerken, geçmiş değerler için verilen date'a karşılık gelen değeri LAST_INSERT_ID() arayınız.

prices masaya kendisine gelince, sen yapabilirsiniz depolama, SELECT ve INSERT işlemleri çok daha verimli bir kullanışlı PRIMARY KEY ve hiçbir AUTO_INCREMENT sütun ile. Aşağıdaki şemada, PRIMARY KEY, kendinize özgü yararlı bilgiler içerir ve yalnızca benzersiz satırları tanımlamak için bir kural değildir. INT (4 bayt) yerine MEDIUMINT (3 bayt) kullanıldığında, PRIMARY KEY numaralı satırda her satır için ek bir bayt ve daha da önemlisi 2 baytlık bir satır kaydedilir; bunların tümü, 16 milyondan fazla olası tarih ve sıralı sembolün (her biri) hala devam etmesini sağlar.

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 
; 

Bu şemada her satır market_day ve ticker_id her bir çifti arasında benzersizdir. İşte ticker_idmarket_days tabloya benzer bir şema ile tickers tabloda Kısaltma simgeler listesine karşılık gelir: Önerilen bu diğerlerine benzer bir sorgu verir

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 
; 

, ancak iki önemli farkla: 1) Hiçbir fonksiyonel dönüşüm yok MySQL'in birleştirmede anahtarları kullanma yeteneğini ortadan kaldıran tarih sütunundaki; Aşağıdaki sorguda MySQL, market_day'a katılmak için PRIMARY KEY'un bir bölümünü kullanacaktır. 2) MySQL, sadece JOIN veya WHERE tümcesinde bir anahtar kullanabilir. Bu sorguda MySQL, PRIMARY KEY (market_day ve ticker_id) 'un tüm genişliğini kullanacaktır, oysa önceki sorguda sadece bir tane kullanabiliyordu (MySQL genellikle iki tanesini daha seçecektir).

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 
; 

bir ince noktası da onlar tuşları yararlanabilirler beri fiili ticker_symbol ve date fakat bu işlemler çok hızlı görüntülemek için tickers ve market_days karşı katılmak ihtiyacıdır.

+0

Bu çok daha iyi bir şema ve sorgu kümesidir – philfreo

İlgili konular