My MySQL query won't work because of quotes, or missing quotes, but I don't understand how to use it properly. I need some explanations about this:
Perl script writing to .csv file:
open(ECRIRE,">$ARGV[1]") || die ("Impossible de creer le fichier de sortie");
foreach my $key (sort keys %caisse)
{
print ECRIRE "insert into etablissement(code_etablissement,nom, contact_ce_nom, contact_ce_tel, contact_ce_mail) values ($key,$caisse{$key}[0];$caisse{$key}[1];$caisse{$key}[2];$caisse{$key}[3]) on duplicate key update contact_ce_nom=$caisse{$key}[1],contact_ce_tel=$caisse{$key}[2],contact_ce_mail=$caisse{$key}[3];\n";
}
close(ECRIRE);
Bash script executing the SQL request:
$mysql -f -h $db_address -P $db_port -u $db_user -p$db_passwd $db_name < $vtiger_temporary_file_etablissement_clean
Mysql is crying over almost every informations like this one
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'migrationrh2cepal#mail.com) on duplicate key update contact_ce_no' at line 1
I even tried quoting every variable with single quotes, with the same results...
EDIT : Using DBI for perl
The code now looks like :
foreach my $key (sort keys %caisse)
{
my $insert = $sql_connection->prepare('insert into etablissement values(?, ?, ?, ?, ?, ?) on duplicate key update');
$insert->execute($key, $caisse{$key}[0], $caisse{$key}[1], $caisse{$key}[2], $caisse{$key}[3],'');
}
I now have the "on duplicate key" issue. How can I add the "on duplicate key" statement in here ?
I tried adding it at the end, just like this :
my $insert = $sql_connection->prepare('insert into etablissement values(?, ?, ?, ?, ?, ?) on duplicate key update');
But it's not working
Instead of quoting the variables yourself, use the DBI module and placeholders:
$db = 'DBI'->connect(...);
my $insert = $db->prepare('insert into etablissement values(?, ?, ?)');
$insert->execute($key, $caisse{$key}[0], $caisse{$key}[1]);
For repeated values, numbered placeholders are usually used:
my $insert = $db->prepare(<<'__SQL__');
INSERT INTO etablissement
(code_etablissement, nom, contact_ce_nom, contact_ce_tel, contact_ce_mail)
VALUES (:1, :2, :3, :4, :5)
ON DUPLICATE KEY UPDATE contact_ce_nom = :3,
contact_ce_tel = :4,
contact_ce_mail = :5'
__SQL__
$insert->execute($key, #{ $caisse{$key} }[0 .. 3]);
If your driver doesn't support them (I don't see them mentioned in DBD::mysql), you can workaround it e.g.
my $insert = $db->prepare(<<'__SQL__');
INSERT INTO etablissement
(code_etablissement, nom, contact_ce_nom, contact_ce_tel, contact_ce_mail)
VALUES (?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE contact_ce_nom = ?,
contact_ce_tel = ?,
contact_ce_mail = ?
__SQL__
$insert->execute($key, #{ $caisse{$key} }[0 .. 3], #{ $caisse{$key} }[1 .. 3]);
or you can play with quote and omit placeholders totally.
Using partial answer from choroba :
The final question is "how to use on duplicate key syntax with perl DBI ?"
Like this
my $insert = $sql_connection->prepare('insert into etablissement values(?, ?, ?, ?, ?, ?) on duplicate key update contact_ce_nom=?,contact_ce_tel=?,contact_ce_mail=?');
$insert->execute($key, $caisse{$key}[0], $caisse{$key}[1], $caisse{$key}[2], $caisse{$key}[3],'',$caisse{$key}[1],$caisse{$key}[2],$caisse{$key}[3]);
The last 3 ARGS are used by the "on udplicate key" syntax...
Related
I have a MariaDB database in conjuction with an Express.js backend and therefore use the MariaDB provided Node.js Connector. I Initialized a Database with a Table that looks like this:
CREATE TABLE IF NOT EXISTS `Threads` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`title` TINYTEXT NOT NULL,
`post` TEXT NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (`id`)
);
Using the Node.js-Connector in my Backend, I want to insert some data to this table and use the default values for "created_at" and "updated_at".
I was thinking of something like this:
const insertThreadQuery = 'INSERT INTO Threads VALUES (?, ?, ?, ?, ?)';
con = await pool.getConnection();
const result = await con.query(insertThreadQuery, [null, "title", "post", null, null]);
Which obivously throws an error that tells me, that I cannot insert null for these Values ((conn=4, no: 1048, SQLState: 23000) Column 'updated_at' cannot be null sql: INSERT INTO Threads VALUES (?, ?, ?, ?, ?) - parameters:[null,'title','post',null,null])
My question is: How can I insert an entry like I was showing before, but instead of inserting "null" insert something else so my columns created_at and updated_at use the default value?
If you want to use DEFAULT values, then pass DEFAULT instead of NULL.
const insertThreadQuery = 'INSERT INTO Threads VALUES (NULL, ?, ?, DEFAULT, DEFAULT)';
con = await pool.getConnection();
const result = await con.query(insertThreadQuery, ["title", "post"]);
I am not very experienced with SQL outside of the basics. I'm identifying places in our code that need refactoring and I am trying to optimize some code to limit the number of calls to the DB. I've found some SQL updates that are all updating the same table and am curious how I could go about this...
Here are the update statements:
Host Variables:
[u.contentTitle, u.contentDescription, "title and dscr update", u.urlId, u.statusCode, u.urlId, u.statusCode]
[u.contentTitle, u.contentDescription, "title and dscr update", u.urlId, u.statusCode, u.urlId, u.statusCode]
[null, null, 'title and dscr update', u.urlId, u.statusCode]
UPDATE URL
SET CONTENT_TITLE = ?,
CONTENT_DSCR = ?,
SYSTEM_UPDATE_REASON = ?,
UPDATED_ON = SYSDATE
WHERE ID = ?
AND CONTENT_TITLE IS NULL
AND ? NOT BETWEEN 400 AND 599 OR ID = ?
AND CONTENT_TITLE = ''
AND ? NOT BETWEEN 400 AND 599
UPDATE URL
SET CONTENT_TITLE = ?,
CONTENT_DSCR = ?,
SYSTEM_UPDATE_REASON = ?,
UPDATED_ON = SYSDATE
WHERE ID = ?
AND CONTENT_DSCR IS NULL
AND ? NOT BETWEEN 400 AND 599 OR ID = ?
AND CONTENT_DSCR = ''
AND ? NOT BETWEEN 400 AND 599
UPDATE URL
SET CONTENT_TITLE = ?,
CONTENT_DSCR = ?,
SYSTEM_UPDATE_REASON = ?,
UPDATED_ON = SYSDATE
WHERE ID = ?
AND CONTENT_TITLE IS NOT NULL
AND ? BETWEEN 400 AND 599
All 3 statements are called during our scraping process... so you can imagine it's triple the calls which is not something we want. Can this be done in one single update?
String pName = getStrFromUser("Product name: ");
int price = getIntFromUser("Price: ", false);
String category = getStrFromUser("Category: ");
String description = getStrFromUser("Description: ");
PreparedStatement statement = connection.prepareStatement("INSERT INTO ws.products (name, price, cid, description) VALUES (?, ?, (SELECT ws.categories.cid FROM ws.categories WHERE ws.categories.name LIKE ?), ?)");
statement.setString(1, pName);
statement.setInt(2, price);
statement.setString(3, category);
statement.setString(4, description);
statement.executeUpdate();
I get:
Error encountered: ERROR: syntax error at or near "INSERT INTO ws
What might be the problem?
The subquery inside the VALUES clause looks suspicious. Try rephrasing as an INSERT INTO ... SELECT:
String sql = "INSERT INTO ws.products (name, price, cid, description) ";
sql += "SELECT ?, ?, cid, ? FROM ws.categories WHERE name LIKE ?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1, pName);
statement.setInt(2, price);
statement.setString(3, description);
statement.setString(4, category);
statement.executeUpdate();
I would recommend insert . . . select:
INSERT INTO ws.products (name, price, cid, description)
SELECT ?, ?, ws.categories.cid, ?
FROM ws.categories
WHERE ws.categories.name LIKE ?;
This will not fix the problem with INSERT, but it will prevent the next problem of a subquery returning more than one row.
My best guess for that problem is that the library you are using only supports SELECT statements. That would be atypical; INSERT is usually allowed.
I have a table that imports 221 rows from a database table. Whenever I add a row dynamically inside the HTML Table page, I want it to be able to pull the MAX ID, so in this case 221, and add 1 to it, therefore bringing the MR_ID to 222. I need this because each ID must be unique. Whenever I add a row into the table and look at it in the database, it displays as NULL. I have a little something for that, but it doesn't seem to be working. If you need any more code than what I posted, let me know and I will provide it. Thank you!
<?php
$MR_ID = $_POST['MR_ID'];
$MR_Name = $_POST['MR_Name'];
$Buyer_ID = $_POST['Buyer_ID'];
$MR_POC_N = $_POST['MR_POC_N'];
$MR_POC_E = $_POST['MR_POC_E'];
$MR_POC_P = $_POST['MR_POC_P'];
$host="xxxxxxx";
$dbName="xxxx";
$dbUser="xxxxxxxxxxx";
$dbPass="xxxxxxxxx";
$pdo = new PDO("sqlsrv:server=".$host.";Database=".$dbName, $dbUser, $dbPass);
$MR_ID = "Select MAX(MR_ID) + 1 FROM Stage_Rebate_Master";
$sql = "INSERT INTO Stage_Rebate_Master (MR_ID, MR_Name, Buyer_ID, MR_POC_N, MR_POC_E, MR_POC_P) VALUES (?, ?, ?, ?, ?, ?)";
$stmt = $pdo->prepare($sql);
$result = $stmt->execute(array($MR_ID, $MR_Name, $Buyer_ID, $MR_POC_N, $MR_POC_E, $MR_POC_P));
echo json_encode($result);
?>
you can try like this
$sql = "INSERT INTO Stage_Rebate_Master (MR_ID, MR_Name, Buyer_ID, MR_POC_N, MR_POC_E, MR_POC_P) SELECT ISNULL(MAX(MR_ID)+1,1), ?, ?, ?, ?, ? FROM Stage_Rebate_Master";
$stmt = $pdo->prepare($sql);
$result = $stmt->execute(array( $MR_Name, $Buyer_ID, $MR_POC_N, $MR_POC_E, $MR_POC_P));
I don't know if "variadic" is actually the right word, but I'm talking about things that can take a list of values, like IN(). If you've been working with DBI for long, you've probably tried to do this:
(Note: All examples extremely simplified for brevity)
my $vals = join ', ', #numbers;
my $sth = $dbh->prepare( "SELECT * FROM mytbl WHERE foo IN( ? )" );
$sth->execute( $vals ); # doesn't work
DBI placeholders simply don't support these kinds of shenanigans, it's a single value for each ? or nothing, as far as I know.
This leads me to end up doing something like:
my $sth = $dbh->prepare( "SELECT * FROM mytbl WHERE foo IN ( $vals )" );
which isn't so horrible, but consider a function, like one I wrote today, that has to accept some arbitrary SQL with an IN clause and a list of values
sub example {
my $self = shift;
my ( $sql, #args ) = #_;
my $vals = join ', ', #args;
$sql =~ s/XXX/$vals/; <---- # AARRRGHGH
my $sth = $self->dbh->prepare( $sql );
...
}
This ends up getting called by stuff that looks like
my $sql = "SELECT * FROM mytbl WHERE foo IN( XXX ) AND bar = 42 ORDER BY baz";
my $result = $self->example( $sql, #quux );
This really offends my sense of aesthetics. Building custom SQL programmaticly is a big enough pain as it is; I don't want to go down the road of regexing my SQL strings if I don't have to.
Is there a better way?
Food for thought.
DBIx::Simple offers a syntax for this type of thing using a double-question mark placeholder:
$db->query( 'SELECT * FROM mytbl WHERE foo IN ( ?? )', #args );
Also, SQL::Abstract is powerful, but I find sometimes the abstractions don't result in optimal SQL.
Why not:
my $sql = "SELECT * FROM mytbl WHERE foo IN(" . join(',', ('?')x#quux) . ") AND bar = 42 ORDER BY baz";
my $sth = $dbh->prepare($sql);
$sth->execute(#quux);
If you don't mind breaking from pure DBI and using some modules, I'd take a look at SQL::Abstract for your example. SQL::Abstract can take a Perl hash and turn it into a where clause.
my $sql = SQL::Abstract->new;
my #numbers = (1 .. 10);
my ($stmt, #bind) = $sql->where({foo => {'in', \#numbers}});
# $stmt is " WHERE ( foo IN ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) )"
# #bind contains the values 1 through 10.
sprintf is handy in such situations:
my $sth = $dbh->prepare(
sprintf(
'SELECT * FROM mytbl WHERE foo IN( %s )',
join(',', ('?') x #numbers) )
);
If using placeholders and bind values gets clumsy, there's always DBI::quote().
my $sql = sprintf 'SELECT * FROM mytabl WHERE foo IN ( %s )',
join( ',', map { $dbh->quote( $_ ) } #args );