cursor, propel
Bazen veri tabanında büyük veriler ile çalışmanız gerekebilir. (şu andaki bir projemizde haftada 500 bin satır veri girilen bir tablomuz var örneğin) ve PHP5′in pek bir güzel framework’ü olan symfony ile bu büyüklükteki bir datayı işlemek isteyebilirsiniz. Karşınıza çıkacak sorunlar kısaca şunlardır. PHP’nin çalışma zamanını aşması (evet bu miktar çözülebilinir) ve PHP için ayrılan bellek miktarının yetmemesi. Bu gün symfony e-posta listesine benzer bir soru geldi ve büyük datalarla çalışan bir arkadaş (test amaçlı 100.000 satır veri ile) PHP’nin bellek sorununa takıldığını 100MB belleğin yetmediğini ve paket ürün olduğunda bu veri miktarının 10 misli artacağını ve sorunu nasıl çözeceğini soran bir e-posta atmış. (ben biraz geç fark ettim yoksa hemen üstüne atlardım. ) MySQL’de bu işler nasıl bilmiyorum ama PostgreSQL denen enfes veri tabanında CURSOR denen pek bir güzel kavram vardır. BEGIN COMMIT bloğu içerisinde bir cursor tanımlarsınız ve yapmış olduğunuz işlemin sonucu PostgreSQL tarafından bellekte tutulur ve bu bellekteki veriye belirttiğiniz CURSOR ile erişebilirsiniz. Bunun güzel tarafı sonucun postgreSQL tarafında tutulması böylece büyük resultset’ler sonucunda PHP’nin kullandığı bellek miktarını daha uygun bir şekilde kullanabilirsiniz. Elbette bu sonuç seti içerisinde ileri geri hareket edebilirsiniz.
Hemen bir e-posta ile hızlı ve tu kaka cinsinden bir yanıt verdim ve ardından verdiğim yanıtın testini yaptım (baya ukela biriyim önce test yapsana be adam alla alla) bir iki hata dışında CURSOR kullanımını Propel içerisinden nasıl yapılacağına dair bir kod ortaya çıktı. Eminim birileri bir gün büyük datalarla Propel nasıl çalışır diye arayacaktır (yada ben unutacağım ve o zaman buraya bakacağım) diye buraya yazayım dedim.
public static function getCity()
{
$con = Propel::getConnection();
try
{
$con->begin();
$stmt = $con->PrepareStatement("DECLARE mycursor CURSOR FOR SELECT * FROM ".self::TABLE_NAME);
$stmt->executeQuery();
$loop=true;
while($loop)
{
$loop = false;
$res = $con->executeQuery("FETCH FORWARD 60 FROM mycursor");
while($res->next())
{
$loop = true;
//birşeyler yapın
}
}
$con->rollback();
}catch(Exception $e)
{
// birşeyler yapın
}
// sonucu döndürebilirsiniz ama tavsiye etmem
}
İşlem yapacağınız Tablonun Peer sınıfına yukarıdaki bir method yazıyoruz yaptığımız iş kısaca,
DECLARE deyimini kullanabilmek için BEGIN ile bir transaction açıp, ardından
$stmt = $con->PrepareStatement(”DECLARE mycursor CURSOR FOR SELECT * FROM “.self::TABLE_NAME); satırı ile mycursor isimli bir CURSOR’ü “SELECT * FROM “.self::TABLE_NAME” deyimi için tanımlamak.
ardından sorgumuzu çalıştırmak, burada bize bir result set dönecektir bu result set boş olduğundan ihtiyacımız yok.
Hemen ardından loop içerisinde mycursor adındaki sonuç kümesinden 60′ar adet veri alabilmek için ” $res = $con->executeQuery(’FETCH FORWARD 60 FROM mycursor’); ” deyimini çalıştırıyoruz buradan bize 60 satırlık bir result set dönecektir. bu resultset’ide diğer bir loop içerisinde kullanarak içerisindeki verilerimizi alıp işliyoruz (ne yapacağınız size kalmış.) Yukarıdaki kod bloğunda “FETCH FORWARD 60 FROM mycursor” deyimi bir adet fazla çalışıyor. Nedenini biliyorum, peki siz düzeltebilirmisiniz? (hızlı bir PHP bilgisi testidir efendim kazananlar uzay havası ile ödüllendirileceklerdir. )
Buda lazım tabii…
Bir alandaki tekrar eden verilerin hangileri olduğunu öğrenmek istiyorsunuz diyelim ne yaparsınız?
işte aşağıdaki gibi bir sorgu yazarsınız,
SELECT
dates
FROM reports
GROUP BY dates
HAVING ( COUNT(dates) > 1 )
çalıştırırsınız sonrada dadından yinmez....
Eşit değildir….
"select * from table where field = Null" != "select * from table where field isnull"