Как мне вставить значения NULL с помощью PDO?

105

Я использую этот код, и меня больше не волнует:

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, '', все они терпят неудачу и выдают эту ошибку:

Неустранимая ошибка : невозможно передать параметр 2 по ссылке в / opt / ...

Начо
источник

Ответы:

138

Я только изучаю PDO, но думаю, нужно использовать bindValue, а неbindParam

bindParamпринимает переменную для ссылки и не извлекает значение во время вызова bindParam. Я нашел это в комментарии к документации php:

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

РЕДАКТИРОВАТЬ: PS У вас может возникнуть соблазн сделать это, bindValue(':param', null, PDO::PARAM_NULL);но это не сработало для всех (спасибо, Уилл Шейвер, за сообщение.)

ДжейсонУуф
источник
Я не уверен, в чем разница между этими двумя, но я кое-что исследую. Спасибо, ваш ответ тоже был отличным.
Начо
3
Я думаю, что это может быть лучший ответ, чем мой (если он действительно работает)
Джо Филлипс,
2
У меня были проблемы с PDO :: PARAM_NULL на MySql 5.1.53, но PDO :: PARAM_INT с нулевым значением работал отлично.
Уилл Шейвер
2
Один: Я думаю, что самое главное - передать все три параметра :). в вопросе ign он передавал PDO :: PARAM_NULL как значение, а не как тип (и вообще не передавал тип)
JasonWoof
1
К вашему сведению: в большинстве случаев вас полностью устраивает использование bindValue()over bindParam(); для всего, а не только для NULLценности. Я не могу придумать ни одного случая, когда вам нужно было бы привязать параметр к ссылке на переменную PHP, но это то, что bindParam()нужно.
low_rents 03
48

При использовании bindParam()вы должны передать переменную, а не константу. Итак, перед этой строкой вам нужно создать переменную и установить для нее значениеnull

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

Вы получите такое же сообщение об ошибке, если попытаетесь:

$stmt->bindParam(':v1', 5, PDO::PARAM_NULL);
Джо Филлипс
источник
Вы правильно поняли, я только что понял, что делал это раньше в моем коде, $ null = PDO :: PARAM_NULL; Спасибо.
Начо
1
В этом случае лучше использовать bindValue (), если вы все равно собираетесь создавать заполнители. bindParam () изначально предназначена для выполнения запроса, а затем изменения переменных и повторного выполнения без повторной привязки параметров. bindValue () привязывается немедленно, bindParam () - только при выполнении.
Хьюго Зинк,
1
Я обнаружил, что значение null должно быть в нижнем регистре в строке $ myNull = null. $ myNull = NULL НЕ работал.
raphael75
28

При использовании INTEGERстолбцов (которые могут быть NULL) в MySQL PDO имеет (для меня) неожиданное поведение.

Если вы используете $stmt->execute(Array), вы должны указать литерал NULLи не можете указывать NULLссылку на переменную. Так что это не сработает:

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

Но это сработает:

// $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

Пробовал это в MySQL 5.5.15 с PHP 5.4.1

ChrisF
источник
1
$ stmt-> execute (array (': param' =>! empty ($ val)? $ val: null)); Используйте! Empty, потому что для пустой строки вы также хотели бы установить null в базе данных
Шехзад Низамани
1
@ShehzadNizamani Только если тип столбца целочисленный, как в его примере, да. Но в остальном isset()соотносится лучше NULL. Пустая строка также является значением.
StanE
8

Для тех, у кого все еще есть проблемы (невозможно передать параметр 2 по ссылке), определите переменную с нулевым значением, а не просто передавайте null в PDO:

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

Надеюсь это поможет.

Педро Гульельмо
источник
6

У меня была такая же проблема, и я нашел это решение, работающее с bindParam:

    bindParam(':param', $myvar = NULL, PDO::PARAM_INT);
user1719210
источник
3

Если вы хотите вставить, NULLтолько когда valueесть emptyили '', но вставляйте, valueкогда он доступен.

A) Получает данные формы с помощью метода POST и вызывает функцию insert с этими значениями.

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

Б) Оценивает, не было ли поле заполнено пользователем, и вставляет, NULLесли это так.

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();      
}

Например, если пользователь ничего не вводит в productNameполе формы, тогда $productNameбудет « SETно» EMPTY. Итак, вам нужно проверить, есть ли empty(), а если есть, то вставить NULL.

Протестировано на PHP 5.5.17

Удачи,

Ариан Акоста
источник
3
Или вы можете использовать IFNULL()функцию MySQL в строке запроса. Как это:$sql = "INSERT INTO products ( productId, productName ), VALUES ( IFNULL(:productId, NULL), IFNULL(:productName, NULL) )";
starleaf1
Мне нравится чистота вашего решения. Я только что протестировал его, но, к сожалению, он вставляет пустые строки вместо NULL, когда пользователь ничего не записывает на ввод. Это просто вопрос предпочтения, но я предпочитаю использовать NULL вместо пустых строк.
Ариан Акоста,
0

Попробуй это.

$stmt->bindValue(':v1', null, PDO::PARAM_NULL); // --> insert null
Гектор Теран
источник
1
Прежде чем пытаться ответить на такой старый вопрос, вам следует сначала прочитать другие ответы. Может быть, на него уже ответили.
Ваш здравый смысл
0

На основе других ответов, но с немного большей ясностью относительно того, как на самом деле использовать это решение.

Если, например, у вас есть пустая строка для значения времени, но вы хотите сохранить ее как нулевое значение:

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

Обратите внимание, что для значений времени вы должны использовать PARAM_STR, поскольку время хранится в виде строк.

Винсент
источник
-1

В моем случае я использую:

  • SQLite,

  • подготовленные операторы с заполнителями для обработки неизвестного количества полей,

  • Запрос AJAX, отправленный пользователем, где все является строкой, и нет таких вещей, как NULLзначение и

  • Мне отчаянно нужно вставить NULLs, так как это не нарушает ограничения внешнего ключа (приемлемое значение).

Предположим, теперь пользователь отправляет сообщение: $_POST[field1]со значением, value1которое может быть пустой строкой ""или "null"или "NULL".

Сначала я делаю заявление:

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

где {$sColumns}это что-то вроде field1, field2, ...и {$sValues}мои заполнители ?, ?, ....

Затем я собираю $_POSTданные, связанные с именами столбцов в массиве, $values и заменяю их на NULLs:

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

Теперь я могу выполнить:

$stmt->execute($values);

и среди прочего обходить ограничения внешнего ключа.

Если, с другой стороны, пустая строка имеет больше смысла, тогда вам нужно проверить, является ли это поле частью внешнего ключа или нет (более сложно).

центурианский
источник