tiny ‘n’ smart
database layer

Odkazy: dibi | API reference

Oznámení

Omlouváme se, provoz fóra byl ukončen

jak na TOP v SQL? (pro MSSQL)

před 10 lety

wdolek
Člen | 331

pouzivam MSSQL 2008 server, k nemu se pripojuji pres ODBC. uz nejakou dobu si lamu hlavu, jak dynamicky sestavit SQL dotaz. nasel sem nejake priklady s modifikatory – lmt a ofs …

potreboval bych jednoduse poskladat SQL:

  • pokud zadam „limit“, aby se pridalo „TOP n“ na zacatek
  • pokud zadam „offset“, aby se do podminky pridal takovy ten zrudny subquery
  • pokud „limit“ nezadam, aby se nepridalo samozrejme zadne TOP natoz nejaky subquery

ale jak na to? jak na to pri pouzivani DibiFluent? vim si rady tak s pridanim te podminky (pridat k DibiFluent ->where(' …zrudne subquery… ', $l, $o) i kdyz je mi jasne, ze by to slo snad i cistejs – ze by samotna subquery byla vyrobena nejakym dalsim DibiFluent (?)…

zde jsou SQL, ktere bych chtel dynamicky skladat:
… ??? TeXy ma nejaky zvlastni problem s SQL ??? … nemuzu je sem zaboha vepsat :P

pri pouziti DibiFluent

$stmt = $db->select('*')->from('foo');

if ( ... $limit ... $offset ... ) {
  $stmt->top( $limit ); // ???
  $stmt->where( {nejak pridat subquery z jineho DibiFluent?} );
}

if ( ... xxx ... ) {
  $stmt->where('something = %s', 'xxx');
}

Editoval wdolek (4. 8. 2009 15:01)

před 10 lety

romansklenar
Člen | 657

Zkusil bych to nějak zkombinovat s modifikátory %ofs a %lmt, ty by si měl driver sám správně převést nezávisle na db.

před 10 lety

PetrP
Člen | 587

…a DibiFluent má tuto metodu:

$stmt->limit(10);

Nicméně DibiMssqlDriver nemá offset implementovanej viz api

Takže bud můžeš na davida tlačit aby to implementoval, nebo skusit DibiMssqlDriver rozšířit sám. Tedy jestli to implementovat vůbec nějak jde…

před 10 lety

wdolek
Člen | 331

romansklenar: jenze me jde o samotne poskladani toho query. nejak se mi nechce do skladani stringu … navic se mi jednou nejak podarilo, ze mi dibi samo do query nakladlo „TOP n“ … jenze uz nevim jak, a kdyz sem prochazel zdrojaky, nasel sem jen, ze se vklada „SELECT TOP *“ (ano, vcetne te hvezdicky)

PetrP: tam bude problem v tom, ze pouzivam to zlotrile odbc :( takze i kdyby to bylo v MSSQL driveru, tak je mi to naprd :(
a co se tyce implementace pro MSSQL – tak TOP … WHERE id NOT IN (subquery) … to je az od verze serveru 2008, u drivejsich se to musi resit zajimavejsi magii.
a navic – ackoliv bych rad pomohl s vyvojem – kdyz hledim do zdrojaku, tak proste vubec nemam poneti, co se kde deje, proc, jak, … :((

zkratka – stacilo by jen nejak skladat SQL – s tim, aby se to nejak samo escapovalo… to by mi mozna prozatim stacilo :P nejak bych si s tim poradil – porad by to bylo lepsi nez skladat proste nejaky string a pak se desit nejake injection…

před 10 lety

PetrP
Člen | 587

Přehlídl jsem, ale DibiOdbcDriver ma nemlich tu samou metodu, úplně stějně implementovanou/neimplementovanou.

Tedy platí to samé. Tobě ten limit nefunguje tak jak tady ja nebo roman píšem? Můžeš uvést konkrétní kus kédu který nefunguje.

Editoval PetrP (5. 8. 2009 9:21)

před 10 lety

wdolek
Člen | 331
public function applyLimit(&$sql, $limit, $offset)
{
    // offset support is missing
    if ($limit >= 0) {
        $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')';
    }

    if ($offset) throw new InvalidArgumentException('Offset is not implemented in driver odbc.');
}

nezda se mi, ze by tato funkce delala zrovna to, co ja potrebuji. nemluve o tom, ze stejne moc nerozumim, jak ji aplikovat. z toho jak na to koukam to udela vyber TOP zaznamu z nejakeho query – uz to neresi prave offset, ktery potrebuji.

-- obycejny select
SELECT [a], [b], [c] FROM [foo];

-- obycejny select s podminkou
SELECT [a], [b], [c] FROM [foo] WHERE [something] = 'xxx';

-- select N zaznamu od Mte polozky
SELECT TOP %lmt [a], [b], [c] FROM [foo] WHERE ([id] NOT IN (SELECT TOP %ofs [id] FROM [foo]));

-- select N zaznamu od Mte polozky s podminkou
SELECT TOP %lmt [a], [b], [c] FROM [foo] WHERE ([id] NOT IN (SELECT TOP %ofs [id] FROM [foo])) AND ([something] = 'xxx');

před 10 lety

wdolek
Člen | 331

stacilo by mi, aby:

  1. slo pridat k „SELECT“ jeste „TOP %lmt“
  2. pridat do podminek nejake query od jinud

narpiklad kdyby slo:

$sub_stmt = $db->select('id', 666)->from('foo');
$stmt = $db->select('a, b, c', 10)->from('foo')->where('id NOT IN (%sql)', $sub_stmt);
SELECT TOP 10 [a], [b], [c] FROM [foo] WHERE [id] NOT IN (SELECT TOP 666 [id] FROM [foo]);

nanestesti „->select(<string>, <int>)“ je ma fikce :( a „applyLimit“ by to cele query zabalil do zavorek, a uz by se tam spatne pridavala ta podminka pro offset… (nebo me alespon nenapada jak)

před 10 lety

PetrP
Člen | 587

Takže ještě jednou: Offset is not implemented in driver odbc.

Zkoušel jsem to nanečisto (tedy jen to co mi driver generuje za sql, stroj kde ověřit funkčnost nemám)
a zjistil jsem ze DibiFluent::limit() skutečně nefunguje, myslel jsem si že bude volat driver->applyLimit a nedělá to (Davide není to bug?), ale přes modifikátor to funguje

dibi::query('SELECT FROM xxx %lmt',10);
SELECT TOP 10 * FROM (SELECT FROM xxx )

To je správně ne?

A tohle je jedine řešení jak přidat ofset

dibi::query('SELECT FROM xxx WHERE id NOT IN (SELECT TOP 10 id FROM xxx) %lmt',10);
SELECT TOP 10 * FROM (SELECT FROM xxx WHERE id NOT IN (SELECT TOP 10 id FROM xxx) )

Nějaký obecný mechanizmus (pro začlenění do driveru) na to vymyslet nejde.

před 10 lety

wdolek
Člen | 331

Takže ještě jednou: Offset is not implemented in driver odbc.

ano, to vim :) a nikde se nepru, ze by to slo, nebo melo jit (protoze to ani pro ODBC takto zaridit nejde)

Nějaký obecný mechanizmus (pro začlenění do driveru) na to vymyslet nejde.

ono mi tak ani nejde o obecny mechanismus, ale jak to SQLko slozit nejak „dynamicky“ – nanestesti potrebuji k tomuto dotazu pridavat dalsi podminky (kde podminkou je samozrejme i ten dany limit).
potiz mam s tim, ze nyni mohu dotaz modifikovat pouze:

$stmt = $db->select('*')->from('table');
if ($whatever) {
  $stmt->where('something = %s', $blah);
}
$result = $stmt->execute();

… tedy pridat podminku do SQL je velice snadne. resim zde ale, jak mohu modifikovat nejak vyrazneji cele query pomoci DibiFluent. v mem pripade, pridani limitu spociva v: pridani TOP n hned za SELECT a pak pridani podminky za WHERE; resim zde, zda-li ma dibi nejakou funkci ala StringBuffer – jedine, co nyni svedu je proste slozit string vlastnimi silami na zaklade milionu podminek…

$sql = 'SELECT ';
$where = array();

if ($limit > 0 && $offset >= 0) {
  $sql .= 'TOP ' . $limit;
  array_push($where, 'id NOT IN (SELECT TOP ' . $offset . ' id FROM foo)');
}

if ( ... ) {
  ...
}

...

a to znamena, ze vlastne dibi v tomto pripade nevyuziji. jak sem psal vyse, ono by mi uplne stacilo, kdybych mohl ovlivnit pomoci dibi cast SELECT / SELECT TOP n (mit moznost pridat TOP) … nic vic, pak uz je to hracka (viz predesly post a ukazka kodu, jak by to asi mohlo vypadat). jenom kvuli tomu, ze toto neumim (neumi dibi / nevim o teto moznosti) musim string skladat sam „rucne“.