Jak wstawić wartości NULL za pomocą PDO?

105

Używam tego kodu i jestem bez frustracji:

try {
    $dbh = new PDO('mysql:dbname=' . DB . ';host=' . HOST, USER, PASS);
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $dbh->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET NAMES 'utf8'");
}
catch(PDOException $e)
{
    ...
}
$stmt = $dbh->prepare('INSERT INTO table(v1, v2, ...) VALUES(:v1, :v2, ...)');
$stmt->bindParam(':v1', PDO::PARAM_NULL); // --> Here's the problem

PDO::PARAM_NULL, null, '', wszystkie z nich zawodzą i zgłaszają ten błąd:

Błąd krytyczny : nie można przekazać parametru 2 przez odwołanie w / opt / ...

Nacho
źródło

Odpowiedzi:

138

Właśnie uczę PDO, ale myślę, że trzeba użyć bindValue, niebindParam

bindParampobiera zmienną jako odniesienie i nie pobiera wartości w momencie wywołania bindParam. Znalazłem to w komentarzu do dokumentów php:

bindValue(':param', null, PDO::PARAM_INT);

EDYCJA: PS Możesz pokusić się o zrobienie tego, bindValue(':param', null, PDO::PARAM_NULL);ale nie zadziałało to dla wszystkich (dziękuję Willowi Shaver za zgłoszenie).

JasonWoof
źródło
Nie jestem pewien, jaka jest różnica między tymi dwoma, ale trochę zbadam. Dzięki, twoja odpowiedź też była świetna.
Nacho,
3
Myślę, że to może być lepsza odpowiedź niż moja (jeśli rzeczywiście działa)
Joe Phillips
2
Miałem problem z PDO :: PARAM_NULL na MySql 5.1.53, ale PDO :: PARAM_INT z wartością zerową działało świetnie.
Will Shaver
2
Odin: Myślę, że najważniejszą częścią jest przekazanie wszystkich trzech parametrów :). w pytaniu igna przekazał PDO :: PARAM_NULL jako wartość, zamiast jako typ (i wcale nie przekazywał typu).
JasonWoof
1
FYI: w większości przypadków są całkowicie w porządku z użyciem bindValue()ponad bindParam(); za wszystko, nie tylko za NULLwartość. Nie przychodzi mi do głowy ani jeden przypadek, w którym musiałbyś powiązać parametr z odwołaniem do zmiennej PHP - ale tak właśnie bindParam()jest.
low_rents
48

Podczas używania bindParam()musisz przekazać zmienną, a nie stałą. Więc przed tą linią musisz utworzyć zmienną i ustawić ją nanull

$myNull = null;
$stmt->bindParam(':v1', $myNull, PDO::PARAM_NULL);

Otrzymasz ten sam komunikat o błędzie, jeśli spróbujesz:

$stmt->bindParam(':v1', 5, PDO::PARAM_NULL);
Joe Phillips
źródło
Masz rację, właśnie zdałem sobie sprawę, że zrobiłem to już wcześniej w moim kodzie, $ null = PDO :: PARAM_NULL; dzięki.
Nacho,
1
W tym przypadku lepiej jest użyć bindValue (), jeśli i tak zamierzasz utworzyć symbole zastępcze. Funkcja bindParam () jest pierwotnie przeznaczona do wykonywania zapytania, a następnie zmiany zmiennych i ponownego wykonania bez ponownego wiązania parametrów. bindValue () wiąże się natychmiast, bindParam () tylko podczas wykonywania.
Hugo Zink
1
Okazało się, że null musi być zapisane małymi literami w linii $ myNull = null. $ myNull = NULL NIE zadziałało.
raphael75
28

Podczas używania INTEGERkolumn (które mogą być NULL) w MySQL, PDO ma pewne (dla mnie) nieoczekiwane zachowanie.

Jeśli używasz $stmt->execute(Array), musisz określić literał NULLi nie możesz podać NULLprzez odwołanie do zmiennej. Więc to nie zadziała:

// $val is sometimes null, but sometimes an integer
$stmt->execute(array(
    ':param' => $val
));
// will cause the error 'incorrect integer value' when $val == null

Ale to zadziała:

// $val again is sometimes null, but sometimes an integer
$stmt->execute(array(
    ':param' => isset($val) ? $val : null
));
// no errors, inserts NULL when $val == null, inserts the integer otherwise

Wypróbowałem to na MySQL 5.5.15 z PHP 5.4.1

ChrisF
źródło
1
$ stmt-> execute (array (': param' =>! empty ($ val)? $ val: null)); Użyj! Pusty, ponieważ dla pustego ciągu chciałbyś również ustawić wartość null w bazie danych
Shehzad Nizamani
1
@ShehzadNizamani Tylko jeśli typ kolumny to liczba całkowita, jak w jego przykładzie, tak. Ale poza tym isset()lepiej koreluje z NULL. Wartość jest również pusta.
StanE
8

Dla tych, którzy nadal mają problemy (nie można przekazać parametru 2 przez odniesienie), zdefiniuj zmienną o wartości null, a nie tylko przekazuj null do PDO:

bindValue(':param', $n = null, PDO::PARAM_INT);

Mam nadzieję że to pomoże.

Pedro Guglielmo
źródło
6

Miałem ten sam problem i znalazłem to rozwiązanie działające z bindParam:

    bindParam(':param', $myvar = NULL, PDO::PARAM_INT);
user1719210
źródło
3

Jeśli chcesz wstawić NULLtylko wtedy, gdy valuejest emptylub '', ale wstaw, valuegdy jest dostępny.

A) Odbiera dane formularza metodą POST i wywołuje funkcję insert z tymi wartościami.

insert( $_POST['productId'], // Will be set to NULL if empty    
        $_POST['productName'] ); // Will be to NULL if empty                                

B) Ocenia, czy pole nie zostało wypełnione przez użytkownika i wstawia, NULLjeśli tak jest.

public function insert( $productId, $productName )
{ 
    $sql = "INSERT INTO products (  productId, productName ) 
                VALUES ( :productId, :productName )";

    //IMPORTANT: Repace $db with your PDO instance
    $query = $db->prepare($sql); 

    //Works with INT, FLOAT, ETC.
    $query->bindValue(':productId',  !empty($productId)   ? $productId   : NULL, PDO::PARAM_INT); 

    //Works with strings.
    $query->bindValue(':productName',!empty($productName) ? $productName : NULL, PDO::PARAM_STR);   

    $query->execute();      
}

Na przykład, jeśli użytkownik nie wprowadzi niczego w productNamepolu formularza, to $productNamebędzie SETale EMPTY. Musisz więc sprawdzić, czy tak jest empty(), a jeśli tak, to włóż NULL.

Przetestowano na PHP 5.5.17

Powodzenia,

Arian Acosta
źródło
3
Lub możesz użyć IFNULL()funkcji MySQL w ciągu zapytania. W ten sposób:$sql = "INSERT INTO products ( productId, productName ), VALUES ( IFNULL(:productId, NULL), IFNULL(:productName, NULL) )";
starleaf1
Podoba mi się czystość twojego rozwiązania. Właśnie to przetestowałem, ale niestety wstawia puste ciągi zamiast NULL, gdy użytkownik nie zapisuje niczego na wejściu. To tylko kwestia preferencji, ale wolę mieć wartości NULL zamiast pustych ciągów.
Arian Acosta
0

Spróbuj tego.

$stmt->bindValue(':v1', null, PDO::PARAM_NULL); // --> insert null
hector teran
źródło
1
Zanim spróbujesz odpowiedzieć na tak stare pytanie, powinieneś najpierw przeczytać inne odpowiedzi. Może już udzielono odpowiedzi.
Twój zdrowy rozsądek
0

Na podstawie innych odpowiedzi, ale z trochę większą jasnością, jak właściwie korzystać z tego rozwiązania.

Jeśli na przykład masz pusty ciąg dla wartości czasu, ale chcesz zapisać go jako wartość null:

  if($endtime == ""){
    $db->bind(":endtime",$endtime=NULL,PDO::PARAM_STR);
  }else{
    $db->bind("endtime",$endtime);
  }

Zauważ, że dla wartości czasu użyłbyś PARAM_STR, ponieważ czasy są przechowywane jako ciągi.

Vincent
źródło
-1

W moim przypadku używam:

  • SQLite,

  • przygotowane wyciągi z placeholderami do obsługi nieznanej liczby pól,

  • Żądanie AJAX wysłane przez użytkownika, w którym wszystko jest ciągiem znaków i nie ma czegoś takiego jak NULLwartość i

  • Desperacko muszę wstawić NULLs, ponieważ nie narusza to ograniczeń klucza obcego (dopuszczalna wartość).

Załóżmy, że teraz użytkownik wysyła pocztą: $_POST[field1]z wartością, value1która może być pustym ciągiem znaków ""lub "null"lub "NULL".

Najpierw oświadczam:

$stmt = $this->dbh->prepare("INSERT INTO $table ({$sColumns}) VALUES ({$sValues})");

gdzie {$sColumns}jest coś podobnego field1, field2, ...i {$sValues}są moje symbole zastępcze ?, ?, ....

Następnie zbieram $_POSTdane związane z nazwami kolumn w tablicy $values i zamieniam na NULLs:

  for($i = 0; $i < \count($values); $i++)
     if((\strtolower($values[$i]) == 'null') || ($values[$i] == ''))
        $values[$i] = null;

Teraz mogę wykonać:

$stmt->execute($values);

i między innymi ominąć ograniczenia klucza obcego.

Z drugiej strony, jeśli pusty łańcuch ma większy sens, to musisz sprawdzić, czy to pole jest częścią klucza obcego, czy nie (bardziej skomplikowane).

centurian
źródło