Complex Taşıma NEREDE PHP Query Builder ile cümlecikleri

4 Cevap php

Birkaç ActiveRecord tarz sorgu oluşturucu kitaplıkları orada vardır. Bazıları stand alone ve bazı built into frameworks gelir. Ancak, onlar gerçekten NEREDE ve karmaşık SQL gelince hükümler HAVING ile sorun var. Kenara diğer veritabanları ayarlama - Ben bu güncel yöntem düşüşlerine düzeltmek olabilir ki () yöntemi, MySQL ve PostgreSQL uyumlu ile gelip çalışıyorum.

Aşağıda şimdiye kadar ile gelebilir en iyi gösteren örnekler ve fikir uzun bir liste. Ancak, all of the use cases çözmek için görünmüyor olabilir ve benim kısmi bir çözüm özensiz olduğunu hissediyorum. Tüm bu sorunların çözer şey ile cevap verebilecek herkes, sadece bu soruya cevap olmayacak - ama şimdi birkaç yıl için PHP uygulamaları avlanan bir sorunu gidermekle sorumlu olacaktır.

Common Operators

    =	Equal
    <>	Not Equal
    >	Greater Than
    <	Less Than
    >=	Greater Than Or Equal
    <=	Less Than Or Equal
    BETWEEN between values on right 
    NOT logical NOT 
    AND logical AND 
    OR	logical OR

Example Where clauses

SELECT ... FROM table...
    WHERE column = 5
    WHERE column > 5
    WHERE column IS NULL
    WHERE column IN (1, 2, 3)
    WHERE column NOT IN (1, 2, 3)
    WHERE column IN (SELECT column FROM t2)
    WHERE column IN (SELECT c3 FROM t2 WHERE c2 = table.column + 10)
    WHERE column BETWEEN 32 AND 34
    WHERE column BETWEEN (SELECT c3 FROM t2 WHERE c2 = table.column + 10) AND 100
    WHERE EXISTS (SELECT column FROM t2 WHERE c2 > table.column)

Burada () yan farklı current kütüphanelerde kullandığı birçok ortak ActiveRecord formatları vardır.

$this->db->where(array('session_id' => '?', 'username' => '?'));
$this->db->fetch(array($id, $username));

// vs with is_int($key)
$this->db->where(array('session_id', 'username'));
$this->db->fetch(array($id, $username));

// vs with is_string($where)
$this->db->where('session_id', '?');
$this->db->where('username');
$this->db->fetch(array($id, $username));

// vs with is_array($value)
$this->db->where('session_id', '?');
$this->db->where('username', array('Sam', 'Bob'));
$this->db->fetch(array($id));

İşte ben bugüne kadar nihai biçimidir. Bu (...) AND (...) yanı sıra hazır deyim bağlı params ("?" ": Isim" &) gruplandırma işlemek gerekir.

function where($column, $op = '=', $value = '?', $group = FALSE){}


// Single line

$this->db->where('column > 5');
$this->db->where('column IS NULL');

// Column + condition

$this->db->where('column', '=');
// WHERE column = ? 	(prepared statement)
$this->db->where('column', '<>');
// WHERE column <> ?    (prepared statement)

// Column + condition + values

$this->db->where('column', '=', 5);
// // WHERE column = 5
$this->db->where('column', 'IN', '(SELECT column FROM t2)');
// WHERE column IN (SELECT column FROM t2)
$this->db->where('column', 'IN', array(1,2,3));
// WHERE column IN (1, 2, 3)
$this->db->where('column', 'NOT IN', array(1,2,3));
// WHERE column NOT IN (1, 2, 3)

// column + condition + values + group
$this->db->where(
    array(
    	array('column', '<', 20), 
    	array('column', '>', 10)
    ),
    NULL,
    NULL,
    $group = TRUE
);
// WHERE (column < 20 AND column > 10)

:UPDATE:

Benim soru boyunca ben NEREDE ve sadece derin gitmek daha karmaşık koşullara sahip olduğunu fark geldi. Özellikleri soyut hatta% 80 çalışıyorum sadece WHERE ve HAVING için büyük bir kütüphanede neden olacaktır. Bill de işaret ettiği gibi, bu sadece PHP gibi bir betik dili için makul değildir.

Çözüm sadece sorgunun WHERE kısmını zanaat el etmektir. Sürece sütunları etrafında " kullanmak gibi onlar hemen hemen aynı SQL sözdizimini kullanın Postgre'nin, SQLite, MySQL sorgu beri NEREDE aynı kullanabilirsiniz. (MySQL için yapmanız gerekir str_replace() Onları bir kene ile `).

Soyutlama koşulları böyle bir yer vardır NEREDE yardımcı olur, daha fazla acıyor bir nokta vardır geliyor.

4 Cevap

Ben bir PHP class for constructing SQL queries içeren Zend_Db kütüphanesine biraz çalıştım. Ben çeşitli nedenlerle WHERE ve HAVING cümlelerinde akla gelebilecek her SQL sözdizimi, işlemek için çalışırken çelmele karar verdi:

  • PHP ayrıştırır ve (bir bayt kodu önbelleği kullanmak sürece) her istek kodu derler bir betik dilidir. Yani PHP çevre hantal kod kütüphaneleri duyarlı olduğunu - daha çok Java fazla veya C # veya Python ya da ne var. Biz olabildiğince yalın kütüphaneleri tutmak için bu nedenle yüksek öncelikli.

    Ben çalıştı Zend_Db kütüphanenin tüm PHP kod yaklaşık 2,000 satır oldu. Buna karşılık, Java hazırda kod 118K hatlarının mertebesindedir. Ama bu bir Java kütüphanesi precompiled beri bir sorun çok fazla değil ve her istek üzerine yüklenmek zorunda değildir.

  • SQL ifadeleri okumak ve PHP tabanlı inşaat herhangi birini gösterdi korumak için daha kompakt ve daha kolay bir üretken dilbilgisi izleyin. SQL ifadesi dilbilgisi öğrenme simüle bir API öğrenme çok daha kolaydır. Sen bir destek sona "basitleştirilmiş dilbilgisi." Yoksa bu şekilde başlar ve API unusably karmaşık olana kadar kendinizi Feature Creep içine kullanıcı topluluğu tarafından zorlanmıştır bulabilirsiniz.

  • Böyle bir API kullanılan bir uygulama hata ayıklamak için, kaçınılmaz son SQL ifadesi erişim gerekiyordu, bu yüzden leakiest abstraction sahip olabilir ilgili.

  • SQL ifadeleri için bir PHP tabanlı arayüzü kullanılarak tek avantajı akıllı editörler ve IDE yıllarda kod tamamlama yardımcı olacaktır. Ama operatörler ve işlenen pek çok '>=', herhangi bir kod tamamlama istihbarat yağma gibi dize sabitleri kullandığınızda.


update: Ben sadece iyi bir blog yazı okudum "A Farewell to ORMs." Yazar, Aldo Cotesi, Python'un sqlalchemy yılında SQL Expression Language kullanarak önerir. Python, standart (ama PHP desteklenmez) olduğu sözdizimsel şeker ve operatör overloading bu çok etkili bir sorgu üreten çözüm olun.

Ayrıca Perl DBIx :: Sınıf bakmak olabilir, ama oldukça çirkin olmak biter.

Bu benim ActiveRecord sınıfının bir parçasıdır, ben alt sorguları (Ben bile rahatsız etmeyin) kolu yok:

public function Having($data, $operator = 'LIKE', $merge = 'AND')
{
    if (array_key_exists('query', $this->sql) === true)
    {
    	foreach ($data as $key => $value)
    	{
    		$this->sql['having'][] = ((empty($this->sql['having']) === true) ? 'HAVING' : $merge) . ' ' . $this->Tick($key) . ' ' . $operator . ' ' . $this->Quote($value);
    	}
    }

    return $this;
}

public function Where($data, $operator = 'LIKE', $merge = 'AND')
{
    if (array_key_exists('query', $this->sql) === true)
    {
    	foreach ($data as $key => $value)
    	{
    		$this->sql['where'][] = ((empty($this->sql['where']) === true) ? 'WHERE' : $merge) . ' ' . $this->Tick($key) . ' ' . $operator . ' ' . $this->Quote($value);
    	}
    }

    return $this;
}

Eğer düşünebilirsiniz Bir diğer şey bir customHaving () ve customWhere () yöntemlerini yaşıyor.

Ben bu son derece eski bir gönderme olduğunu biliyorum, ama ben soru sorar ne benzer ihtiyaçlarını karşılamak için kendi sınıfları geliştirme sürecinde, çünkü ben zaten ona cevap gidiyorum.

İçine baktıktan sonra, ben Zend-Db ve benzeri motorları ile sorun tüm insanlar için her şey olmaya çalışın olduğunu tespit ettik. Büyük bir kitleye hitap etmek, onlar kadarıyla (ve aynı ustalıkla Bill Karwin açıklanabilir) görebileceğiniz gibi kendi felâket olur en genel işlevi, sunmak gerekir.

Birçok motorları yapmak en bariz fazla komplikasyonlarından biri, yürütme (kolay kirli SQL yazmak için yapım) ile SQL kodu nesil karıştırmaktır. Birçok uygulamada, vb enjeksiyon saldırıları düşünmeye geliştirici teşvik, oldukça açıkça bu iki ayırmak için iyi bir fikirdir

SQL motoru bina yapmak için denemek için ilk şey, motor üretebilir ne SQL kapsamını sınırlamak için. Bunu bir select * from table, örneğin üretmek için izin vermemelidir; Motor her select, where ve having sütun açıkça tanımlamak için geliştirici gerektirmelidir. Başka bir örnek olarak, (normalde veritabanı tarafından gerekli değildir) bir takma ad olması için her sütunda gerektirmeyen genellikle yararlıdır.

Bu şekilde SQL sınırlama aslında veritabanı çıkmak ne sınırı olmadığına dikkat edin. Evet, bu vesileyle kadar ön kodlama daha ayrıntılı hale getirir, ama aynı zamanda daha yapılandırılmış yapar ve (karmaşık istisnalar ile başa çıkmak ve sağlamak için ilk etapta sadece hiç vardı kütüphane-kod satırları yüzlerce dökümü sağlar ahem) "esneklik".

Şimdiye kadar yazdığım kütüphaneleri kod yaklaşık 600 hatları (~ hata işleme 170 hatları olan) vardır. ISO katılır ilgilenir, alt ifadeler, herhangi bir 2 taraflı karşılaştırma maddesinin (SELECT, FROM ve WHERE hükümler içinde), {[(3) ]}, EXISTS ve BETWEEN (WHERE yan tümcesinde alt deyimleri ile). Ayrıca dolaylı yerine, doğrudan SQL içine değerleri enjekte, bağlamaları oluşturur.

Sınırlamalar (zaten belirtilenler dışındaki): SQL Oracle için açıkça yazılmıştır. Başka herhangi bir veritabanı platformu üzerinde denenmemiş.

Ben herhangi bir iyileştirmeler geri gönderilir varsayarak, kodu paylaşmaya hazırım.

Kütüphaneler bana üretelim ne bir örnek olarak, ben de genişleme potansiyelini göstermek için yeterince karmaşık olurken şu, sezgisel olacak kadar basit olduğunu umuyoruz:

<?php
$substmt = new OraSqlStatement;
$substmt->AddVarcharCol ('value','VALUE')
        ->AddVarcharCol ('identity','UID',false)
        ->AddVarcharCol ('type','info_type',false)
        ->AddFrom ('schemaa.user_propertues','up')
        ->AddWhere ('AND')
        ->AddComparison ('UID', '=', 'e.identity', 'column')
        ->AddComparison ('info_type', '=', 'MAIL_ADDRESS');

$stmt = new OraSqlStatement;
$stmt->AddVarcharCol ('company_id', 'Company')
     ->AddVarcharCol ('emp_no',     'Emp Id')
     ->AddVarcharCol ('person_id',  'Pers Id')
     ->AddVarcharCol ('name',       'Pers Name')
     ->AddDateCol ('employed_date', 'Entry Date')
     ->AddDateCol ('leave_date', 'Leave Date')
     ->AddVarcharCol ('identity',   'User Id')
     ->AddVarcharCol ('active', 'Active')
     ->AddVarcharCol ($substmt, 'mail_addy')
     ->AddFrom ('schemab.employee_tab', 'e')
     ->AddFrom ('schemaa.users_vw','u','INNER JOIN','u.emp_no=e.emp_number')
     ->AddWhere ('AND')
     ->AddComparison ('User Id', '=', 'my_user_id')
     ->AddSubCondition ('OR')
     ->AddComparisonNull ('Leave Date', false)
     ->AddComparisonBetween ('Entry Date', '2011/01/01', '2011/01/31');

echo $stmt->WriteSql();
var_dump($stmt->GetBindArray());
?>

Üretir:

SELECT 
  company_id "Company", emp_no "Emp Id", person_id "Pers Id", name "Pers Name", 
  employed_date "Entry Date", leave_date "Leave Date", identity "User Id", active "Active", 
  ( SELECT value "VALUE" FROM schemaa.user_propertues up 
    WHERE  upper(identity) = upper(e.identity)
      AND  upper(TYPE) = upper (:var0) 
  ) "mail_addy" 
FROM 
  schemab.employee_tab e 
      INNER JOIN schemaa.users_vw u ON u.emp_no = e.emp_number 
WHERE 
        upper (identity) = upper (:var1)
  AND ( leave_date IS NOT NULL OR
        employed_date BETWEEN to_date (:var2,'YYYY/MM/DD') AND to_date (:var3,'YYYY/MM/DD') 
      )

Bağlama dizisi ile birlikte:

array
  0 => string 'MAIL_ADDRESS' (length=12)
  1 => string 'my_user_id' (length=10)
  2 => string '2011/01/01' (length=10)
  3 => string '2011/01/31' (length=10)

SQLAlchemy 's API şimdiye kadar birlikte çalıştığım en iyi biridir. Bu bir Python kütüphanesi var, ama yine de onun tarafından ilham olabilir. Bu NEREDE bentlerinde için değil sadece var --- tüm SQL sorgusu kolayca değiştirilebilir bir veri yapısı ile ifade edilir (bir seçme veya DML olabilir).

(Ben onun SQL-araç değil, ORM-parçaları bahsediyorum. :-)