Czy mogę powiązać tablicę z warunkiem IN ()?

562

Jestem ciekawy, czy możliwe jest powiązanie tablicy wartości z symbolem zastępczym za pomocą PDO. Przypadek użycia tutaj próbuje przekazać tablicę wartości do użycia z IN()warunkiem.

Chciałbym móc zrobić coś takiego:

<?php
$ids=array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(:an_array)'
);
$stmt->bindParam('an_array',$ids);
$stmt->execute();
?>

I poproś o powiązanie PDO i zacytowanie wszystkich wartości w tablicy.

W tej chwili robię:

<?php
$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
foreach($ids as &$val)
    $val=$db->quote($val); //iterate through array and quote
$in = implode(',',$ids); //create comma separated list
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN('.$in.')'
);
$stmt->execute();
?>

Co na pewno działa, ale zastanawiam się, czy brakuje mi wbudowanego rozwiązania?

Andru
źródło
3
Kompletny przewodnik na temat wiązania tablicy z warunkiem IN () , w tym przypadku, gdy w zapytaniu występują inne symbole zastępcze
Twoje wspólne
Pytanie zostało zamknięte jako duplikat tego pytania . Odwróciłem powieloną flagę, ponieważ to pytanie jest 4 lata starsze, ma 4 razy więcej wyświetleń, 3 razy więcej odpowiedzi i 12 razy więcej niż wynik. Jest to wyraźnie cel nadrzędny.
miken32
Każdy, kto patrzy na to w 2020 roku: możesz wypróbować github.com/morris/dop .
morris4

Odpowiedzi:

262

myślę, że soulmerge ma rację. będziesz musiał zbudować ciąg zapytania.

<?php
$ids     = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids), '?'));

$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(' . $inQuery . ')'
);

// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();
?>

fix: Dan, miałeś rację. naprawiono kod (chociaż go nie testowałem)

edytuj: zarówno Chris (komentarze), jak i ktoś niesłusznie sugerowali, że pętla foreach ...

(...)
// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();

... może być zbędny, więc foreachpętla i pętla $stmt->executemogą zostać zastąpione przez ...

<?php 
  (...)
  $stmt->execute($ids);
?>

(ponownie nie testowałem tego)

stefs
źródło
7
To ciekawe rozwiązanie i choć wolę iterację od identyfikatorów i wywoływanie PDO :: quote (), myślę, że indeks „?” symbole zastępcze zostaną pomieszane, jeśli jakieś inne symbole zastępcze pojawią się w innym miejscu zapytania, prawda?
Andru
5
tak, to byłby problem. ale w tym przypadku można utworzyć nazwane parametry zamiast?
stefs
9
Stare pytanie, ale warte odnotowania, uważam, jest to, że $foreachi bindValue()nie jest wymagane - wystarczy wykonać z tablicą. Np .:$stmt->execute($ids);
Chris
2
Generowanie str_repeat('?,', count($array) - 1). '?';
symboli
8
Wskazówka dla tych, którzy nie są świadomi, nie można mieszać parametrów nazwanych i nienazwanych. Dlatego jeśli użyjesz nazwanych parametrów w swoim zapytaniu, przełącz je na?, A następnie zwiększ przesunięcie indeksu bindValue, aby dopasować pozycję IN? Z, gdziekolwiek są względem siebie? parametry.
justinl
169

Na coś szybkiego:

//$db = new PDO(...);
//$ids = array(...);

$qMarks = str_repeat('?,', count($ids) - 1) . '?';
$sth = $db->prepare("SELECT * FROM myTable WHERE id IN ($qMarks)");
$sth->execute($ids);
DonVaughn
źródło
7
Doskonale, nie pomyślałem, aby użyć argumentu input_parameters w ten sposób. Dla tych, których zapytania mają więcej parametrów niż lista IN, możesz użyć array_unshift i array_push, aby dodać niezbędne argumenty na początku i na końcu tablicy. Również wolę $input_list = substr(str_repeat(',?', count($ids)), 1);
Orca
7
Możesz także spróbować str_repeat('?,', count($ids) - 1) . '?'. Jedno połączenie funkcji mniej.
orca
5
@erfling, to jest przygotowane oświadczenie, skąd będzie zastrzyk? Z przyjemnością wprowadzę wszelkie poprawki, jeśli możesz to potwierdzić, przedstawiając jakiś faktyczny dowód.
DonVaughn
5
@erfling, tak, to jest poprawne, a wiązanie parametrów jest dokładnie tym, co robimy w tym przykładzie, wysyłając executetablicę identyfikatorów
DonVaughn
5
Och, rzeczywiście. Jakoś przeoczył fakt, że minąłeś tablicę. To wydaje się być bezpieczne i dobrą odpowiedzią. Przepraszam.
erfling
46

Czy użycie INinstrukcji jest tak ważne ? Spróbuj użyć FIND_IN_SETop.

Na przykład w PDO istnieje takie zapytanie

SELECT * FROM table WHERE FIND_IN_SET(id, :array)

Następnie musisz tylko powiązać tablicę wartości, zaimplantowaną przecinkiem, taką jak ta

$ids_string = implode(',', $array_of_smth); // WITHOUT WHITESPACES BEFORE AND AFTER THE COMMA
$stmt->bindParam('array', $ids_string);

i gotowe.

UPD: Jak niektórzy zauważyli w komentarzach do tej odpowiedzi, jest kilka kwestii, które należy wyjaśnić w sposób wyraźny.

  1. FIND_IN_SETnie używa indeksu w tabeli i wciąż nie jest jeszcze zaimplementowany - zobacz ten rekord w narzędziu do śledzenia błędów MYSQL . Dzięki @BillKarwin za powiadomienie.
  2. Nie można użyć ciągu z przecinkiem w środku jako wartości tablicy do wyszukiwania. Niemożliwe jest prawidłowe parsowanie takiego łańcucha po implodeużyciu symbolu przecinka jako separatora. Dzięki @VaL za notatkę.

W porządku, jeśli nie jesteś silnie zależny od indeksów i nie używasz ciągów z przecinkiem do wyszukiwania, moje rozwiązanie będzie znacznie łatwiejsze, prostsze i szybsze niż rozwiązania wymienione powyżej.

Tim Tonkonogov
źródło
25
IN () może używać indeksu i liczy się jako skanowanie zakresu. FIND_IN_SET () nie może używać indeksu.
Bill Karwin
1
To jest punkt. Nie wiedziałem tego Ale w żaden sposób nie ma żadnych wymagań dotyczących wydajności w pytaniu. W przypadku niezbyt dużych tabel jest on znacznie lepszy i czystszy niż osobna klasa do generowania zapytań o różnej liczbie symboli zastępczych.
Tim Tonkonogov
11
Tak, ale kto dziś ma nie tak duży stół? ;-)
Bill Karwin
3
Kolejny problem z tym podejściem, co zrobić, jeśli w środku będzie ciąg znaków z przecinkiem? Na przykład ... FIND_IN_SET(description,'simple,search')będzie działać, ale FIND_IN_SET(description,'first value,text, with coma inside')zawiedzie. Więc funkcja będzie szukać "first value", "text", "with coma inside" zamiast pożądanego"first value", "text, with coma inside"
VaL
33

Ponieważ wykonuję wiele zapytań dynamicznych, stworzyłem bardzo prostą funkcję pomocnika.

public static function bindParamArray($prefix, $values, &$bindArray)
{
    $str = "";
    foreach($values as $index => $value){
        $str .= ":".$prefix.$index.",";
        $bindArray[$prefix.$index] = $value;
    }
    return rtrim($str,",");     
}

Użyj tego w ten sposób:

$bindString = helper::bindParamArray("id", $_GET['ids'], $bindArray);
$userConditions .= " AND users.id IN($bindString)";

Zwraca ciąg, :id1,:id2,:id3a także aktualizuje $bindArraywiązania, które będą potrzebne, gdy nadejdzie czas uruchomienia zapytania. Łatwy!

prograhammer
źródło
6
Jest to znacznie lepsze rozwiązanie, ponieważ nie łamie reguły wiązania parametrów. Jest to o wiele bezpieczniejsze niż wbudowane sql, jak sugerują tu inni.
Dimitar Darazhanski,
1
Awesomeness. Elegancki. Doskonały.
Ricalsin,
17

Rozwiązanie EvilRygy nie działało dla mnie. W Postgres możesz wykonać inne obejście:


$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id = ANY (string_to_array(:an_array, ','))'
);
$stmt->bindParam(':an_array', implode(',', $ids));
$stmt->execute();
Siergiej Galkin
źródło
To nie działa: ERROR: operator does not exist: integer = text. Musisz przynajmniej dodać jawne casting.
collimarco
17

bardzo czystym sposobem dla postgres jest użycie tablicy postgres („{}”):

$ids = array(1,4,7,9,45);
$param = "{".implode(', ',$ids)."}";
$cmd = $db->prepare("SELECT * FROM table WHERE id = ANY (?)");
$result = $cmd->execute(array($param));
ESCOBAR
źródło
blokuje zastrzyk sql?
Fábio Zangirolami
1
@ FábioZangirolami to ChNP, więc tak.
ESCOBAR
TAK, ChNP! po 4 latach. Twoja odpowiedź była dla mnie w porządku, bardzo prosta i skuteczna. Dziękuję Ci!!!
Fábio Zangirolami
14

Oto moje rozwiązanie:

$total_items = count($array_of_items);
$question_marks = array_fill(0, $total_items, '?');
$sql = 'SELECT * FROM foo WHERE bar IN (' . implode(',', $question_marks ). ')';

$stmt = $dbh->prepare($sql);
$stmt->execute(array_values($array_of_items));

Zwróć uwagę na użycie wartości array_values. To może rozwiązać problemy z kluczowymi zamówieniami.

Łączę tablice identyfikatorów, a następnie usuwam zduplikowane elementy. Miałem coś takiego:

$ids = array(0 => 23, 1 => 47, 3 => 17);

I to się nie udawało.

Progrock
źródło
To było doskonałe rozwiązanie. W przeciwieństwie do budowania ciągu zapytania, to poprawnie używa wiązania. Zdecydowanie polecam ten.
Lindylead
Uwaga: Korzystając z rozwiązania, możesz dodawać elementy z przodu lub z tyłu tablicy, dzięki czemu możesz dołączać inne powiązania. $original_array Dodaj elementy przed oryginalną tablicą: array_unshift($original_array, new_unrelated_item); Dodaj elementy za oryginalną tablicą: array_push($original_array, new_unrelated_item); Gdy wartości zostaną powiązane, nowe_ niezwiązane elementy zostaną umieszczone w odpowiednich lokalizacjach. Umożliwia to mieszanie elementów Array i non-array. `
Lindylead
12

Rozszerzyłem PDO, aby zrobić coś podobnego do tego, co sugeruje stefs, a na dłuższą metę było mi łatwiej:

class Array_Capable_PDO extends PDO {
    /**
     * Both prepare a statement and bind array values to it
     * @param string $statement mysql query with colon-prefixed tokens
     * @param array $arrays associatve array with string tokens as keys and integer-indexed data arrays as values 
     * @param array $driver_options see php documention
     * @return PDOStatement with given array values already bound 
     */
    public function prepare_with_arrays($statement, array $arrays, $driver_options = array()) {

        $replace_strings = array();
        $x = 0;
        foreach($arrays as $token => $data) {
            // just for testing...
            //// tokens should be legit
            //assert('is_string($token)');
            //assert('$token !== ""');
            //// a given token shouldn't appear more than once in the query
            //assert('substr_count($statement, $token) === 1');
            //// there should be an array of values for each token
            //assert('is_array($data)');
            //// empty data arrays aren't okay, they're a SQL syntax error
            //assert('count($data) > 0');

            // replace array tokens with a list of value tokens
            $replace_string_pieces = array();
            foreach($data as $y => $value) {
                //// the data arrays have to be integer-indexed
                //assert('is_int($y)');
                $replace_string_pieces[] = ":{$x}_{$y}";
            }
            $replace_strings[] = '('.implode(', ', $replace_string_pieces).')';
            $x++;
        }
        $statement = str_replace(array_keys($arrays), $replace_strings, $statement);
        $prepared_statement = $this->prepare($statement, $driver_options);

        // bind values to the value tokens
        $x = 0;
        foreach($arrays as $token => $data) {
            foreach($data as $y => $value) {
                $prepared_statement->bindValue(":{$x}_{$y}", $value);
            }
            $x++;
        }

        return $prepared_statement;
    }
}

Możesz użyć tego w następujący sposób:

$db_link = new Array_Capable_PDO($dsn, $username, $password);

$query = '
    SELECT     *
    FROM       test
    WHERE      field1 IN :array1
     OR        field2 IN :array2
     OR        field3 = :value
';

$pdo_query = $db_link->prepare_with_arrays(
    $query,
    array(
        ':array1' => array(1,2,3),
        ':array2' => array(7,8,9)
    )
);

$pdo_query->bindValue(':value', '10');

$pdo_query->execute();
Chris
źródło
1
Odniosłem się do pierwszej części komentarza Marka, ale, jak zauważył, nadal nie jest bezpieczne, jeśli taki znak :arrayjest w ciągu zapytania.
Chris
4
Uwaga dla wszystkich przyszłych czytelników: Nigdy nie należy używać tego rozwiązania. Aserty nie są przeznaczone dla kodu produkcyjnego
Your Common Sense
1
YCS: dziękuję za opinie, zainteresowani twoją opinią o podejściu wykraczającym poza przydatność stwierdzeń.
Chris
1
Pomysł jest prawie taki sam, ale bez stwierdzeń i prostszy i wyraźniejszy sposób - nie jako wyjątek dla pojedynczej sprawy, ale jako ogólny sposób budowania każdego zapytania. Każdy symbol zastępczy jest oznaczony rodzajem. To sprawia, że ​​zgadywanie (jak if (is_array($data))jeden) jest niepotrzebne, a jednocześnie znacznie dokładniejsze przetwarzanie danych.
Twój zdrowy rozsądek
1
Wszystkim osobom czytającym komentarze: problem wymieniony przez @Twoj rozsądek został naprawiony w wersji 4 .
user2428118,
11

Patrząc na PDO: Stałe predefiniowane, nie ma PDO :: PARAM_ARRAY, którego byś potrzebował, jak podano na PDOStatement-> bindParam

bool PDOStatement :: bindParam (mieszany parametr $, mieszany i zmienna $ [, int $ typ_danych [, int $ długość [, mieszane $ opcje_końcowe]]])

Więc nie sądzę, że można to osiągnąć.


źródło
1
Nie wiem czy to zadziała. Sądzę, że zacytowany ciąg zostaje zacytowany.
soulmerge
2
Masz rację, cytaty są usuwane, więc nie działają; t działa. Usunąłem ten kod.
11

Jeśli masz inny parametr, możesz zrobić tak:

$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$query = 'SELECT *
            FROM table
           WHERE X = :x
             AND id IN(';
$comma = '';
for($i=0; $i<count($ids); $i++){
  $query .= $comma.':p'.$i;       // :p0, :p1, ...
  $comma = ',';
}
$query .= ')';

$stmt = $db->prepare($query);
$stmt->bindValue(':x', 123);  // some value
for($i=0; $i<count($ids); $i++){
  $stmt->bindValue(':p'.$i, $ids[$i]);
}
$stmt->execute();
Daniel Miloca - Brazylia
źródło
Dzięki za świetną odpowiedź. To był jedyny ze wszystkich, który faktycznie działał dla mnie. Widziałem jednak 1 błąd. Zmienna $ rs powinna wynosić $ stmt
Piet
11

Dla mnie bardziej seksownym rozwiązaniem jest zbudowanie dynamicznej tablicy asocjacyjnej i użycie jej

// A dirty array sent by user
$dirtyArray = ['Cecile', 'Gilles', 'Andre', 'Claude'];

// we construct an associative array like this
// [ ':name_0' => 'Cecile', ... , ':name_3' => 'Claude' ]
$params = array_combine(
    array_map(
        // construct param name according to array index
        function ($v) {return ":name_{$v}";},
        // get values of users
        array_keys($dirtyArray)
    ),
    $dirtyArray
);

// construct the query like `.. WHERE name IN ( :name_1, .. , :name_3 )`
$query = "SELECT * FROM user WHERE name IN( " . implode(",", array_keys($params)) . " )";
// here we go
$stmt  = $db->prepare($query);
$stmt->execute($params);
RousseauAlexandre
źródło
Trudno być pewnym bez wypróbowania go w prawdziwym scenariuszu, ale wydaje się być w porządku. + 1
Anant Singh --- Alive to Die
9

Zdaję sobie również sprawę, że ten wątek jest stary, ale miałem unikalny problem polegający na tym, że podczas konwersji niedługo przestarzałego sterownika mysql na sterownik PDO musiałem stworzyć funkcję, która dynamicznie mogłaby budować zarówno normalne parametry, jak i IN z tego samego tablica parametrów. Więc szybko to zbudowałem:

/**
 * mysql::pdo_query('SELECT * FROM TBL_WHOOP WHERE type_of_whoop IN :param AND siz_of_whoop = :size', array(':param' => array(1,2,3), ':size' => 3))
 *
 * @param $query
 * @param $params
 */
function pdo_query($query, $params = array()){

    if(!$query)
        trigger_error('Could not query nothing');

    // Lets get our IN fields first
    $in_fields = array();
    foreach($params as $field => $value){
        if(is_array($value)){
            for($i=0,$size=sizeof($value);$i<$size;$i++)
                $in_array[] = $field.$i;

            $query = str_replace($field, "(".implode(',', $in_array).")", $query); // Lets replace the position in the query string with the full version
            $in_fields[$field] = $value; // Lets add this field to an array for use later
            unset($params[$field]); // Lets unset so we don't bind the param later down the line
        }
    }

    $query_obj = $this->pdo_link->prepare($query);
    $query_obj->setFetchMode(PDO::FETCH_ASSOC);

    // Now lets bind normal params.
    foreach($params as $field => $value) $query_obj->bindValue($field, $value);

    // Now lets bind the IN params
    foreach($in_fields as $field => $value){
        for($i=0,$size=sizeof($value);$i<$size;$i++)
            $query_obj->bindValue($field.$i, $value[$i]); // Both the named param index and this index are based off the array index which has not changed...hopefully
    }

    $query_obj->execute();

    if($query_obj->rowCount() <= 0)
        return null;

    return $query_obj;
}

Nadal nie został przetestowany, jednak wydaje się, że logika istnieje.

Mam nadzieję, że pomoże to komuś na tej samej pozycji,

Edycja: Po kilku testach dowiedziałem się:

  • ChNP nie lubi „.” w ich nazwach (co jest trochę głupie, jeśli mnie pytasz)
  • bindParam to niewłaściwa funkcja, bindValue to właściwa funkcja.

Kod edytowany do działającej wersji.

Sammaye
źródło
8

Trochę edycji o kodzie Schnalle

<?php
$ids     = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids)-1, '?'));

$db   = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(' . $inQuery . ')'
);

foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();
?>

//implode(',', array_fill(0, count($ids)-1), '?')); 
//'?' this should be inside the array_fill
//$stmt->bindValue(($k+1), $in); 
// instead of $in, it should be $id
Aaron Angelo Vicuna
źródło
1
Musiałem usunąć -1 po zliczeniu ($ ids), aby działało to dla mnie, inaczej zawsze będzie brakowało jednego symbolu zastępczego.
Marcel Burkhard
7

Z jakiej bazy danych korzystasz? W PostgreSQL lubię używać DOWOLNEJ (tablicy). Aby ponownie użyć swojego przykładu:

<?php
$ids=array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id = ANY (:an_array)'
);
$stmt->bindParam('an_array',$ids);
$stmt->execute();
?>

Niestety jest to dość nieprzenośne.

W innych bazach danych musisz wymyślić własną magię, o czym wspominali inni. Będziesz chciał umieścić tę logikę w klasie / funkcji, aby oczywiście można ją było ponownie wykorzystać w całym programie. Spójrz na komentarze na mysql_querystronie PHP.NET, aby uzyskać więcej przemyśleń na ten temat i przykłady tego scenariusza.

Ryan Bair
źródło
4

Po przejściu przez ten sam problem poszedłem do prostszego rozwiązania (choć nadal nie jest tak eleganckie, jak PDO::PARAM_ARRAYmogłoby być):

biorąc pod uwagę tablicę $ids = array(2, 4, 32):

$newparams = array();
foreach ($ids as $n => $val){ $newparams[] = ":id_$n"; }

try {
    $stmt = $conn->prepare("DELETE FROM $table WHERE ($table.id IN (" . implode(", ",$newparams). "))");
    foreach ($ids as $n => $val){
        $stmt->bindParam(":id_$n", intval($val), PDO::PARAM_INT);
    }
    $stmt->execute();

... i tak dalej

Więc jeśli używasz tablicy mieszanych wartości, będziesz potrzebować więcej kodu, aby przetestować swoje wartości przed przypisaniem parametru param:

// inside second foreach..

$valuevar = (is_float($val) ? floatval($val) : is_int($val) ? intval($val) :  is_string($val) ? strval($val) : $val );
$stmt->bindParam(":id_$n", $valuevar, (is_int($val) ? PDO::PARAM_INT :  is_string($val) ? PDO::PARAM_STR : NULL ));

Ale nie przetestowałem tego.

alan_mm
źródło
4

Jak wiem, nie ma możliwości powiązania tablicy z instrukcją PDO.

Ale istnieją 2 wspólne rozwiązania:

  1. Użyj Pozycyjnych symboli zastępczych (?,?,?,?) Lub Nazwanych symboli zastępczych (: id1,: id2,: id3)

    $ whereIn = implode (',', array_fill (0, count ($ ids), '?'));

  2. Cytuj tablicę wcześniej

    $ whereIn = tablica_mapy (tablica ($ db, „quote”), $ ids);

Obie opcje są dobre i bezpieczne. Wolę drugi, ponieważ jest krótszy i mogę var_dump parametry, jeśli go potrzebuję. Używając symboli zastępczych, musisz powiązać wartości, a na końcu kod SQL będzie taki sam.

$sql = "SELECT * FROM table WHERE id IN ($whereIn)";

A ostatnim i ważnym dla mnie jest uniknięcie błędu „liczba powiązanych zmiennych nie pasuje do liczby tokenów”

Doctrine to świetny przykład użycia symboli zastępczych, tylko dlatego, że ma wewnętrzną kontrolę nad przychodzącymi parametrami.

Oleg Matei
źródło
4

Jeśli kolumna może zawierać tylko liczby całkowite, prawdopodobnie można to zrobić bez symboli zastępczych i po prostu wstawić identyfikatory bezpośrednio do zapytania. Musisz tylko rzutować wszystkie wartości tablicy na liczby całkowite. Lubię to:

$listOfIds = implode(',',array_map('intval', $ids));
$stmt = $db->prepare(
    "SELECT *
     FROM table
     WHERE id IN($listOfIds)"
);
$stmt->execute();

Nie powinno to być podatne na iniekcję SQL.

Kodos Johnson
źródło
3

oto moje rozwiązanie. Rozszerzyłem również klasę PDO:

class Db extends PDO
{

    /**
     * SELECT ... WHERE fieldName IN (:paramName) workaround
     *
     * @param array  $array
     * @param string $prefix
     *
     * @return string
     */
    public function CreateArrayBindParamNames(array $array, $prefix = 'id_')
    {
        $newparams = [];
        foreach ($array as $n => $val)
        {
            $newparams[] = ":".$prefix.$n;
        }
        return implode(", ", $newparams);
    }

    /**
     * Bind every array element to the proper named parameter
     *
     * @param PDOStatement $stmt
     * @param array        $array
     * @param string       $prefix
     */
    public function BindArrayParam(PDOStatement &$stmt, array $array, $prefix = 'id_')
    {
        foreach($array as $n => $val)
        {
            $val = intval($val);
            $stmt -> bindParam(":".$prefix.$n, $val, PDO::PARAM_INT);
        }
    }
}

Oto przykładowe użycie powyższego kodu:

$idList = [1, 2, 3, 4];
$stmt = $this -> db -> prepare("
  SELECT
    `Name`
  FROM
    `User`
  WHERE
    (`ID` IN (".$this -> db -> CreateArrayBindParamNames($idList)."))");
$this -> db -> BindArrayParam($stmt, $idList);
$stmt -> execute();
foreach($stmt as $row)
{
    echo $row['Name'];
}

Powiedz mi co myślisz

Lippai Zoltan
źródło
Zapomniałem wspomnieć, że jest to oparte na odpowiedzi użytkownika2188977 poniżej.
Lippai Zoltan
Nie jestem pewien, co to jest getOne (), nie wydaje się być częścią PDO. Widziałem to tylko w PEAR. Co to dokładnie robi?
Lippai Zoltan
@YourCommonSense czy możesz opublikować funkcję zdefiniowaną przez użytkownika jako odpowiedź?
nullability
Sugerowałbym przekazanie typu danych do BindArrayParamtablicy asocjacyjnej, ponieważ wydaje się, że ograniczasz to do liczb całkowitych.
Ian Brindley
2

W PDO nie można użyć takiej tablicy.

Musisz zbudować ciąg z parametrem (lub użyć?) Dla każdej wartości, na przykład:

:an_array_0, :an_array_1, :an_array_2, :an_array_3, :an_array_4, :an_array_5

Oto przykład:

<?php
$ids = array(1,2,3,7,8,9);
$sqlAnArray = join(
    ', ',
    array_map(
        function($index) {
            return ":an_array_$index";
        },
        array_keys($ids)
    )
);
$db = new PDO(
    'mysql:dbname=mydb;host=localhost',
    'user',
    'passwd'
);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN('.$sqlAnArray.')'
);
foreach ($ids as $index => $id) {
    $stmt->bindValue("an_array_$index", $id);
}

Jeśli chcesz nadal używać bindParam, możesz to zrobić w zamian:

foreach ($ids as $index => $id) {
    $stmt->bindParam("an_array_$index", $ids[$id]);
}

Jeśli chcesz użyć ?symboli zastępczych, możesz to zrobić w następujący sposób:

<?php
$ids = array(1,2,3,7,8,9);
$sqlAnArray = '?' . str_repeat(', ?', count($ids)-1);
$db = new PDO(
    'mysql:dbname=dbname;host=localhost',
    'user',
    'passwd'
);
$stmt = $db->prepare(
    'SELECT *
     FROM phone_number_lookup
     WHERE country_code IN('.$sqlAnArray.')'
);
$stmt->execute($ids);

Jeśli nie wiesz, czy $idsjest pusty, powinieneś go przetestować i odpowiednio obsłużyć tę sprawę (zwróć pustą tablicę lub zwróć Obiekt Null lub wyrzuć wyjątek, ...).

Pedro Amaral Couto
źródło
0

Posunąłem się nieco dalej, aby przybliżyć odpowiedź do pierwotnego pytania o użycie symboli zastępczych do powiązania parametrów.

Ta odpowiedź będzie musiała utworzyć dwie pętle przez tablicę, która będzie używana w zapytaniu. Ale rozwiązuje to problem posiadania innych symboli zastępczych dla bardziej selektywnych zapytań.

//builds placeholders to insert in IN()
foreach($array as $key=>$value) {
    $in_query = $in_query . ' :val_' . $key . ', ';
}

//gets rid of trailing comma and space
$in_query = substr($in_query, 0, -2);

$stmt = $db->prepare(
    "SELECT *
     WHERE id IN($in_query)";

//pind params for your placeholders.
foreach ($array as $key=>$value) {
    $stmt->bindParam(":val_" . $key, $array[$key])
}

$stmt->execute();
Joseph_J
źródło
0

najpierw ustawiłeś liczbę „?” w zapytaniu, a następnie za pomocą „for” wyślij parametry takie jak to:

require 'dbConnect.php';
$db=new dbConnect();
$array=[];
array_push($array,'value1');
array_push($array,'value2');
$query="SELECT * FROM sites WHERE kind IN (";

foreach ($array as $field){
    $query.="?,";
}
$query=substr($query,0,strlen($query)-1);
$query.=")";
$tbl=$db->connection->prepare($query);
for($i=1;$i<=count($array);$i++)
    $tbl->bindParam($i,$array[$i-1],PDO::PARAM_STR);
$tbl->execute();
$row=$tbl->fetchAll(PDO::FETCH_OBJ);
var_dump($row);
Ali Chegini
źródło