使用 DEFAULT 值准备 MySQL INSERT/UPDATE 语句

Posted

技术标签:

【中文标题】使用 DEFAULT 值准备 MySQL INSERT/UPDATE 语句【英文标题】:Preparing a MySQL INSERT/UPDATE statement with DEFAULT values 【发布时间】:2011-01-28 16:52:16 【问题描述】:

引用 mysql INSERT 手册 - 更新也是如此:

使用关键字 DEFAULT 将列显式设置为其默认值。这使得编写将值分配给除少数列之外的所有列的 INSERT 语句变得更容易,因为它使您能够避免编写不包含表中每一列的值的不完整 VALUES 列表。否则,您将不得不写出与 VALUES 列表中的每个值对应的列名列表。

所以简而言之,如果我写

INSERT INTO table1 (column1,column2) values ('value1',DEFAULT);

将插入一个将 column2 设置为其默认值的新行(无论它可能是什么)。

但是,如果我在 php 中准备并执行一条语句:

$statement = $pdoObject->
    prepare("INSERT INTO table1 (column1,column2) values (?,?)");
$statement->execute(array('value1','DEFAULT'));

如果该列能够存储文本值,则新行将包含“DEFAULT”作为其文本值。

现在我已经为 PDO 编写了一个抽象层(我需要它),为了解决这个问题,我正在考虑引入一个

const DEFAULT_VALUE = "randomstring";

所以我可以执行这样的语句:

$statement->execute(array('value1',mysql::DEFAULT_VALUE));

然后在进行绑定的方法中,我将通过发送要绑定的值,如果一些值等于self::DEFAULT_VALUE,则采取相应的行动。

我很确定有更好的方法来做到这一点。有没有其他人遇到过类似的情况?

【问题讨论】:

由于 PDO 将您的值作为要输入的字符串处理,如果可能的话,您可能需要稍微更改代码。您可能会更改它以便输入 ...execute(array('\'value1\'', 'DEFAULT')) ,但我猜 PDO 正在使用 mysql_real_escape_string() 等为您处理这些值。这会破坏该功能。您可能只需将其直接插入到 prepare() 参数中。 为什么不在INSERT 语句中包含您设置为默认值以外的值的列? 问题在于UPDATE 和一个空的提交输入。我需要将其设为默认值,例如 '' 设置为新值时的日期字段不是 0000-00-00 【参考方案1】:

尝试改变这个:

$statement = $pdoObject->
    prepare("INSERT INTO table1 (column1,column2) values (?,?)");
$statement->execute(array('value1','DEFAULT'));

到这里:

$statement = $pdoObject->
    prepare("INSERT INTO table1 (column1,column2) values (?,DEFAULT)");
$statement->execute(array('value1'));

在我看来,你的原始代码会给你这个:

INSERT INTO table1 (column1,column2) values ('value1','DEFAULT')

我的代码应该给你这个:

INSERT INTO table1 (column1,column2) values ('value1',DEFAULT)

【讨论】:

谁会想要其他值,但无论如何都是默认值。【参考方案2】:

我知道的唯一“解决方法”是使用 Coalesce() 和 Default(fieldname)

例如

$pdo = new PDO("mysql:host=localhost;dbname=test", 'localonly', 'localonly'); 
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$pdo->exec("
  CREATE TEMPORARY TABLE foo (
    id int auto_increment,
    x int NOT NULL DEFAULT 99,
    y DATETIME NOT NULL DEFAULT '2010-03-17 01:00:00',
    z varchar(64) NOT NULL DEFAULT 'abc',
    primary key(id)
  )
");


$stmt = $pdo->prepare('
  INSERT INTO
    foo
    (x,y,z)
  VALUES
    (
      Coalesce(:x, Default(x)),
      Coalesce(:y, Default(y)),
      Coalesce(:z, Default(z))
    )
');
$stmt->bindParam(':x', $x);
$stmt->bindParam(':y', $y);
$stmt->bindParam(':z', $z);


$testdata = array(
  array(null, null, null),
  array(1, null, 'lalala'),
  array(null, '2009-12-24 18:00:00', null)
);
foreach($testdata as $row) 
  list($x,$y,$z) = $row;
  $stmt->execute();

unset($stmt);
foreach( $pdo->query('SELECT id,x,y,z FROM foo', PDO::FETCH_NUM) as $row) 
  echo join(', ', $row), "\n";

打印

1, 99, 2010-03-17 01:00:00, abc
2, 1, 2010-03-17 01:00:00, lalala
3, 99, 2009-12-24 18:00:00, abc

【讨论】:

Default(x) 可以是DEFAULT,如果要在同一列中为该列设置默认值,则无需提及列名。它容易出错。更新 t SET x = 默认值; 好主意!不完美,但胜过我的解决方案! @Frank:当用作 Coalesce() 的参数时,它必须是 Default(fieldname)。 遗憾的是,如果未在每列上设置默认值,它会给出General error: 1364 Field 'column1' doesn't have a default value 您当然只能在具有默认值的字段上使用Coalesce(val, default(x))-thingy。你不能把它扔在那里... anywhere ;-) 但这只有在你的脚本在运行时构建/组装那些(任意)语句时才重要,然后你仍然可以使用 TABLE_INFORMATION_SCHEMA 来找出一个字段是否有默认值。【参考方案3】:

我尝试回复 VolkerK 的答案,但找不到方法。 :( 我对这一切有点陌生。

无论如何,我创建了一个 mysql 函数来结合他的 COALESCE 想法使用

CREATE FUNCTION NULLDEFAULT(colname VARCHAR(64), tablename VARCHAR(64), dbname VARCHAR(64)) RETURNS longtext DETERMINISTIC READS SQL DATA
BEGIN
    DECLARE retval longtext;
    SELECT
        COLUMN_DEFAULT INTO retval 
    FROM
        information_schema.COLUMNS
    WHERE
        TABLE_NAME = tablename
    AND
        COLUMN_NAME = colname
    AND
        TABLE_SCHEMA = dbname;

    RETURN retval;
END

你会这样使用它:

$stmt = $pdo->prepare("
  INSERT INTO
    foo
    (x,y,z)
  VALUES
    (
      Coalesce(:x, NULLDEFAULT('x', 'foo', 'database')),
      Coalesce(:y, NULLDEFAULT('y', 'foo', 'database')),
      Coalesce(:z, NULLDEFAULT('z', 'foo', 'database'))
    )
");

如果该列没有默认值,则返回null,并且不会触发“列没有默认值”错误。

当然你可以修改为不需要数据库参数

【讨论】:

我认为它不会在性能方面保持优势。 AFAIK mysql 不会缓存函数的结果,因此您将为插入查询中的每一行和每一列调用查询。事先通过 PHP 获取默认值要好得多,但正如我已经得出的结论,我在这里学到的教训是根本不要指望默认的 mysql 值。我仍然会给你一个 +1 的努力。 根据文档,DETERMINISTIC 关键字告诉 mysql 该函数始终返回具有相同参数的相同值,因此我相信它会缓存结果。但是我没有广泛品尝它,我在一个小型的内网中使用它,速度没有任何明显的下降。【参考方案4】:

我认为它正在编写字符串'DEFAULT',因为它被 pdo 转义了,所以有用于 bindvalue 的参数,您可以在其中指定给定值的类型,这样您就可以发送不带引号的空值,它将是 PDO ::PARAM_NULL;然后会放默认值,但是我不确定与execute绑定时是否有类似的参数

  if(is_int($param))$pdoParam = PDO::PARAM_INT;
      elseif(is_bool($param))$pdoParam = PDO::PARAM_BOOL;
      elseif(is_null($param)) $pdoParam = PDO::PARAM_NULL;
      elseif(is_string($param))$pdoParam = PDO::PARAM_STR;
                                    else$pdoParam = FALSE;
              $this->_query->bindValue($k,$param,$pdoParam);

【讨论】:

$pdoParam = FALSE; 应该实现什么?默认为PARAM_STR

以上是关于使用 DEFAULT 值准备 MySQL INSERT/UPDATE 语句的主要内容,如果未能解决你的问题,请参考以下文章

Mysql 数据库之高级操作

我可以在 MySQL 中使用具有多个值的 DEFAULT 约束吗?我可以在下面的示例中添加更多默认值吗? [关闭]

_mysql_exceptions 错误(1064,“检查与您的 MySQL 服务器版本相对应的手册,以获取在 'default 附近使用的正确语法)值

mysql default设置了默认值但插入依然为null

mysql default默认值 空白怎么弄?

数据的增删改