Preparing a MySQL INSERT/UPDATE statement with DEFAULT values
The only "workaround" I know for this is to use Coalesce() and Default(fieldname)
E.g.
$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";
}
prints
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
mysql prepared statement set default value for insert
No, it's not possible to "alias" a value multiple times. Each placeholder is unique and must be bound to exactly one value. (It is not even possible to do this reliably with named parameters in PDO.)
If automatically building SQL dynamically (with placeholders) and associated data array, then there is no issue with such "duplication" as it's already handled by the generator.
If transactions are correctly used - which they should be anyway! - then simply calling INSERT in a loop, once for each pair, will likely have similar performance. This avoids needing to build the dynamic SQL (with placeholders) itself, but can increase latency as each command needs to round-trip to the SQL server.
If finding a bunch of such repetition, it might be time to use a Data Access Layer to hind such details; write it, test it, move onto something interesting.
In response to the comment about using SQL variables:
While the following will not improve performance, excepting possibly for ridiculously large values of @v, and makes the code more difficult to understand - it ought to be possible to use user-defined variables in a single insert statement. For instance, consider:
INSERT INTO t VALUES (@v := ?, ?), (@v, ?), (@v, ?)
This is "valid" in MySQL (and is also MySQL-specific), where the placeholders are valid expressions; if it works will depend on how/if such a binding is allowed in a prepared statement.
PHP MySQL - How to insert default value in a prepared statement
Your question addresses an interesting subject, in my opinion.
By a rapid search, I don't have found any predefined solution, but I will continue search in the next days.
I have elaborate a solution, but it is a bit twisted and not totally clean.
First of all, I redefine the query with placeholders:
$query = "
INSERT INTO table
(
tag,
month_interval,
month_interval_lastupdated,
date_due,
date_due_lastupdated,
date_completion,
date_completion_lastupdated,
activeNotification,
date_lastmodified
)
VALUES (<tag>,<month_interval>,NOW(),<date_due>,NOW(),<date_completion>,NOW(),<activeNotification>,NOW())
";
In this example, I use <>
to surround placeholder, but you can choose your preferred placeholder, making sure that they do not confuse with others elements of the query.
Then, I init an associative array with keys as placeholders names and values to be bind, and a variable ($types
) with the complete set of bind types:
$values = array( 'tag'=>$tag, 'month_interval'=>$month_interval, 'date_due'=>$date_due, 'date_completion'=>$date_completion, 'activeNotification'=>$activeNotification );
$types = 'sisss';
Then, the most important line. I init an array in this strange way:
$bind = array( Null, '' );
The index 0 is set to Null
, because is reserved for the stmt
object (created below); the index 1 is an empty string that will be filled with necessary bind types.
Now I perform a foreach
loop through all elements of array $values
:
$i = 0;
foreach( $values as $key => $val )
{
if( is_null( $val ) )
{
$query = str_replace( '<'.$key.'>', 'DEFAULT', $query );
}
else
{
$query = str_replace( '<'.$key.'>', '?', $query );
$bind[1] .= $types[$i];
$bind[] = &$val;
}
$i++;
}
In that loop, if the value is null I replace the corresponding query's placeholder with mySQL keyword DEFAULT
. Otherwise, I replace placeholder with a ?
, I add the corresponding $types
substring to $bind[1]
, and I append the value (by reference) to $bind
array.
At this point, the real query is ready to go, and I can prepare it:
$stmt = $db->prepare( $query ) or die( $stmt->error );
and, if there are not default values (count($bind)>2
), I can bind it:
if( count($bind)>2 )
{
$bind[0] = $stmt;
call_user_func_array( 'mysqli_stmt_bind_param', $bind );
}
As said before, I set $bind[0]
to stmt object returned by ->prepare
, then I use call_user_func_array
to perform mysqli_stmt_bind_param
. I can't directly call ->bind_param
because I have a variable arguments number.
At the end of this eccentric process, finally I can execute the query:
$stmt->execute() or die( $stmt->error );
I have tested this code, and it works.
The main problem reside in the is_null( $val )
: using an array, I can't use isset
as test, because every element of an array is even set. The code works if all the field with placeholder that allows Null value has Null ad default value.
MySQL insert row with only default values
This will do it :
INSERT INTO `myTable` (`id`)
VALUES
(null),
(null);
-- or
INSERT INTO `myTable` ()
VALUES();
SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE Table1
(`id` int AUTO_INCREMENT PRIMARY KEY, `title` varchar(5) DEFAULT '***')
;
INSERT INTO Table1
(`id`, `title`)
VALUES
(1, 'hi'),
(2, 'hello')
;
INSERT INTO Table1
(`id`)
VALUES
(null),
(null)
;
INSERT INTO Table1 () VALUES();
Query 1:
SELECT * from Table1
Results:
| id | title |
|----|-------|
| 1 | hi |
| 2 | hello |
| 3 | *** |
| 4 | *** |
| 5 | *** |
Using DEFAULT in an INSERT/UPDATE statement vs CASE
You cannot do that. You can use an expression there or the DEFAULT
keyword.
SET { column = { expression | DEFAULT } | ... }
However, you can split your query into 2 updates:
UPDATE some_table
SET some_column = DEFAULT
WHERE id = 1 AND some_condition;
UPDATE some_table
SET some_column = 'foo'
WHERE id = 1 AND NOT some_condition;
This will do the desired changes. If you really want to do it in one query, you can use CTEs:
WITH upd1 AS (
UPDATE some_table
SET some_column = DEFAULT
WHERE id = 1 AND some_condition
RETURNING *
),
upd2 AS (
UPDATE some_table
SET some_column = 'foo'
WHERE id = 1
RETURNING *
)
SELECT * FROM upd1
UNION ALL
SELECT * FROM upd2
Note: the AND NOT some_condition
skipped intentionally. The last CTE can work exactly as the ELSE
branch in the CASE
expression, because if you use multiple data modifying CTEs, maximum one of them can affect every row within a table.
Related Topics
Insert Data into Database with Codeigniter
Protect the Excel File Using PHPexcel
How to Grab All Variables in a Post (Php)
Ftp Upload File to Distant Server with Curl and PHP Uploads a Blank File
Pass Base64 Jpeg Image to Og:Image
How to Set Base Url for All Pages of My Website
Printing PHP Script Output to File
Customize Tax Amount in "Woocommerce_Package_Rates" Hook
How to Destroy Session with Browser Closing in Codeigniter
Google Map: Is a Lat/Lng Within a Polygon
Should I Use Both Striptags() and HTMLspecialchars() to Prevent Xss
Follow Redirects with Curl in PHP
Getting Elements of a Div from Another Page (Php)