Knex: Wie verwende ich knex.raw() mit `where in`

Erstellt am 28. Juni 2016  ·  7Kommentare  ·  Quelle: knex/knex

Dies mag eine dumme, einfache Frage sein, aber ich habe herumgestöbert und konnte keine Antwort finden.
Ich habe diese Abfrage mit einer Unterabfrage

    return knex.raw(`
                update gps_message gm
                set status = 'PROCESSING'
                from (
                    select id
                    from gps_message
                    where status = 'MATCHED'
                    order by id
                    limit ?
                    ) sub
                where gm.id = sub.id
                returning *
            `, [limit])

Aber wenn ich versuche, and id in (?) so hinzuzufügen,

    return knex.raw(`
                update gps_message gm
                set status = 'PROCESSING'
                from (
                    select id
                    from gps_message
                    where status = 'MATCHED'
                                        and id in (?)
                    order by id
                    limit ?
                    ) sub
                where gm.id = sub.id
                returning *
            `, [[1,2,3],limit])

Ich verstehe das
invalid input syntax for integer: "{"1","2","3"}"

Übersehe ich hier etwas?

Hilfreichster Kommentar

In der Zwischenzeit können Sie stattdessen ANY() verwenden:

knex.raw('select 1 where id = ANY(?)', [[1, 2, 3, 'strings too']])

in vielen Fällen sind sie gleichwertig:
http://stackoverflow.com/questions/30263671/postgresql-in-vs-any
http://stackoverflow.com/questions/34627026/in-vs-any-operator-in-postgresql

Alle 7 Kommentare

Laufen in ein ähnliches Problem. Hast du jemals eine Lösung gefunden @rogerschlachter ?

Das scheint zu funktionieren ... Ich nehme an, dies ist ein weiterer Fall von undefiniertem Verhalten, das sich in Zukunft ändern könnte:

> knex.raw(`select 1 where id in (??) order by id limit ? )`, [[1,2,3],1]).toSQL()

{ method: 'raw',
  sql: 'select 1 where id in (1, 2, 3) order by id limit ? )',
  bindings: [ '1' ],
  options: {},
  __knexQueryUid: '0634182d-0282-401d-9da0-83b0fb7547d8' }

Mir ist kein offizieller Weg bekannt, außer dass Sie den Abfragegenerator verwenden, um Ihre Abfrage zu schreiben, und .whereIn() in der Unterabfrage verwenden ...

BEARBEITEN:
Auch das funktioniert nicht mit Strings, weil sie als Bezeichner maskiert würden....

Ich bin mir nicht sicher, warum Array immer in '{"1","2","3"}' konvertiert wird. Ich nehme an, es hat etwas mit der Syntax von https://www.postgresql.org/docs/9.1/static/arrays.html zu tun, und Knex Raw hat keine Möglichkeit, den Kontext zu kennen, in dem das Array gerendert wird. Ich weiß auch nicht, ob sogar der pg-Treiber die Übergabe von Arrays an seine Parameterbindungen erlauben würde.

Die ursprüngliche Rohabfrage funktioniert wie gewünscht für die anderen Treiber: mysql , sqlite , oracle , mssql . Dies ist definitiv ein pg -spezifisches Problem. Es wandelt auch Zahlen in Strings um - was bei den anderen Treibern nicht vorkommt. Aus limit 10 wird also limit '10' (siehe #1001).

Wenn die innere Abfrage mit Knex erstellt wird, funktioniert sie meistens - wiederum mit der Einschränkung, dass Zahlen in Zeichenfolgen umgewandelt werden. Hier ist eine kurze Demo von jedem zusammen mit der Ausgabe:

const limit = 10;

const raw = knex.raw(`update gps_message gm
set status = 'PROCESSING'
from (
    select id
      from gps_message
     where status = 'MATCHED'
       and id in (?)
  order by id
     limit ?
  ) sub
where gm.id = sub.id
returning *
`, [[1,2,3],limit]);

console.log('--raw:\n', raw.toString());

const inner = knex.from('gps_message')
  .where('status', 'MATCHED')
  .whereIn('id', [1, 2, 3])
  .orderBy('id')
  .limit(limit);

console.log('--inner:\n', inner.toString());
console.log();

const rawWithInner = knex.raw(`update gps_message gm
set status = 'PROCESSING'
from (?) sub
where gm.id = sub.id
returning *
`, [knex.raw(inner.toString())]);

console.log('--rawWithInner:\n', rawWithInner.toString());

Und die Ausgabe:

--raw:
 update gps_message gm
set status = 'PROCESSING'
from (
    select id
      from gps_message
     where status = 'MATCHED'
       and id in ('{"1","2","3"}')
  order by id
     limit '10'
  ) sub
where gm.id = sub.id
returning *

--inner:
 select * from "gps_message" where "status" = 'MATCHED' and "id" in ('1', '2', '3') order by "id" asc limit '10'

--rawWithInner:
 update gps_message gm
set status = 'PROCESSING'
from (select * from "gps_message" where "status" = 'MATCHED' and "id" in ('1', '2', '3') order by "id" asc limit '10') sub
where gm.id = sub.id
returning *

In der Zwischenzeit können Sie stattdessen ANY() verwenden:

knex.raw('select 1 where id = ANY(?)', [[1, 2, 3, 'strings too']])

in vielen Fällen sind sie gleichwertig:
http://stackoverflow.com/questions/30263671/postgresql-in-vs-any
http://stackoverflow.com/questions/34627026/in-vs-any-operator-in-postgresql

Ich denke, dass die Dinge in erster Linie konsistent sein sollten - WHERE foo IN (?) sollte auf die gleiche Weise erweitert werden, egal welche Plattform verwendet wird. Arrays sind kein grundlegender Datentyp, der von allen Plattformen unterstützt wird – sie sind eine spezifische Funktion von Postgres – wenn Sie also den Benutzer knex über seine Absicht informieren müssen, wenn ein Array übergeben wird, sollte das Standardverhalten dasjenige sein, das funktioniert auf allen Plattformen, und der Benutzer sollte das Array mit etwas wie knex.array(foo) umschließen, wenn er das Array _type_ verwenden möchte.

Ich werde dies schließen, da der aktuelle Stand, wie dies unterstützt wird, bereits beantwortet ist. Das nächste wäre, eine entsprechende Funktionsanfrage zur Erweiterung der ? -Parametererweiterung zu öffnen, um Arrays besser zu unterstützen, mit einem API-Vorschlag und einer Untersuchung, welche Art von Unterstützung für verschiedene Datenbanken erforderlich ist.

@jahudka default könnte wahrscheinlich nur eine durch Kommas getrennte Liste ohne Klammern sein, wie eine, die mit where in (?) funktionieren könnte ... aber ich bin mir nicht sicher, ob selbst das für jeden Dialekt gültig ist, also könnte es sogar dialektspezifisch sein solange wie es einfach funktioniert. Wie auch immer, wie gesagt, eine ordnungsgemäße Feature-Anfrage wäre der nächste Schritt, um dies zu implementieren.

das ist meine lösung
const queryAll = _db .table('group_org_device') .leftJoin('group_org', function() { this.on( 'group_org.org_id_str', 'group_org_device.org_id_str') .on( 'group_org.ldap_id', 'group_org_device.ldap_id' ); }) .select('group_org_device.device_mark') if(!toAll) { queryAll.whereRaw('group_org.group_id in (' + allGroupIds.map(_ => '?').join(',') + ')', [...allGroupIds]) }

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen