Can someone help me figure out the correct syntax of
for (my $i = 0; $i <=3; $i++)
{
$store = qq(INSERT INTO main (creator_name,relationship)
VALUES("$data{creatorname}",$data{"relationship$i"}) );
The problem lies with $data{"relationship$1"}. I'm looping because I have 'relationship1', 'relationship2', and 'relationship3' in my data hash. I didn't want to go through 3 separate mysql queries to get the job done so I'm trying to loop over it.
Any pointers?
EDIT:
Thanks for your help with pointing me towards placeholders. It's not working as placeholders and it looks like it's because of
$sth->execute($data{creatorname},$data{relationship},"DATE_ADD(NOW(), INTERVAL $interval)"
I have a DATE_ADD now that I'm using, it doesn't look like it likes to be used as a placeholder.
As pointed out by mob and Bill, if possible it is best to use place holders, but that's not the reason your code is not working.
It is not working because you are trying to do two levels of variable interpolation in one string: first interpolate $i into "relationship$i", then interpolate $data{"relationship$i"} into the larger string quoted with qq. They will not nest like that.
This would work:
for (my $i = 0; $i <=3; $i++)
{
my $relationship = $data{"relationship$i"}
$store = qq(INSERT INTO main (creator_name,relationship)
VALUES("$data{creatorname}",$relationship ) );
As #mob says, you should use query parameters instead of fighting with how to interpolate variables directly into strings.
$store = qq(INSERT INTO main (creator_name, relationship) VALUES (?, ?));
$st = $dbi->prepare($store);
for (my $i = 0; $i < 3; $i++)
{
$st->execute($data{creatorname}, $data{"relationship$i"});
}
Advantages of using parameters:
Easier to code, without worrying about awkward string interpolation.
Slightly better for performance, because the SQL statement is parsed once, instead of repeatedly during each loop iteration.
Safer with respect to application security; good defense against SQL injection.
Re your comment:
An SQL parameter can be used only in place of a single scalar value. Not an expression, or a table name or column name, or a list of values, or SQL keywords. Basically, any value you pass for the parameter value will be treated as though you had put quotes around it (there are some nuances to that, but it gives you the approximate idea).
Given the expression you described, I'd write the code like this:
$store = qq(INSERT INTO main (creator_name, relationship, complicated_column)
VALUES (?, ?, DATE_ADD(NOW(), INTERVAL ? HOUR)));
$st = $dbi->prepare($store);
for (my $i = 0; $i < 3; $i++)
{
$st->execute($data{creatorname}, $data{"relationship$i"}, $interval);
}
Re answer from #harmic:
This is awkward to reply to another answer by adding to my own answer, but I wanted to share a code test that demonstrates the "double-interpolation" does in fact work.
$ cat test.pl
$i = 1;
$data{"key$i"} = "word";
$s = qq(string with parentheses ($data{"key$i"}));
print $s, "\n";
$ perl test.pl
string with parentheses (word)
The output of running this Perl script shows that the interpolation worked.
It's a bit hard on the eyes, but if you always have three rows to enter you could do it all in one execute():
my $sth = $dbh->prepare(<<'__eosql');
INSERT INTO main (time_column, creator_name, relationship)
SELECT NOW() + INTERVAL ? HOUR, -- placeholder for $interval
?, -- $data{creatorname}
relation
FROM (SELECT ? AS relation -- $data{relationship1}
UNION ALL
SELECT ? -- $data{relationship2}
UNION ALL
SELECT ?) d -- $data{relationship3}
__eosql
$sth->execute($interval, #data{qw(creatorname relationship1 relationship2 relationship3)});
That uses a hash slice to pull the values out of %data.
Related
I am wanting to count all occurrences of the # symbol in a field and originally i thought LIKE '%#%' would be the way to go, but if the character appears in the field more than once it only counts it as one.
What other method are there that i could use that would count every occurrence?
Thanks.
EDIT
For anyone needing it, this is what i ended up using that works.
$count = 0;
$sql = mysql_query("SELECT LENGTH(field_name) - LENGTH(REPLACE(field_name,'#','')) AS 'occurs' FROM table_name WHERE field_name LIKE '%#%'");
while ($data = mysql_fetch_assoc($sql)) {
$count += $data['occurs'];
}
echo $count;
select length('aa:bb:cc:dd')-length(replace('aa:bb:cc:dd',':',''));
source: http://lists.mysql.com/mysql/215049
You could make this even simpler by using the ``substr_count function in php. see below.
$message = $row['themessage'];
echo substr_count($message, '#');
what this will return is the number of times # has occurred in your "themessage" field in your database.
for($count = 0; $count < count($_POST["item_sub_category"]); $count++)
{
$data = array(
':item_sub_category_id'
=> SELECT r_name FROM Repair where r_id = $_POST["item_sub_category"][$count]
);
$query = "INSERT INTO Repairlog (description,visitID) VALUES (:item_sub_category_id,'1')";
$statement = $connect->prepare($query);
$statement->execute($data);
}
As far as concerns, your code won't work. The SQL query that you are passing as a parameter will simply be interpreted as a string.
You could avoid the need for a loop by taking advantage of the INSERT INTO ... SELECT ... syntax. The idea is to generate an IN clause that contains all values that are in the array, and then run a single query to insert all records at once.
Consider:
$in = str_repeat('?,', count($_POST["item_sub_category"]) - 1) . '?';
$query = "INSERT INTO Repairlog (description,visitID) SELECT r_name, 1 FROM Repair WHERE r_id IN ($in)";
$statement = $connect->prepare($query);
$statement->execute($_POST["item_sub_category"]);
Note: it is likely that visitID is an integer and not a string; if so, then it is better not to surround the value with single quotes (I removed them in the above code).
TLDR; No.
Your question can be re-framed as: Can I write SQL code in php. The answer is NO. You can write the SQL code within a String type variable (or parameter) in php.
This is a general rule for any programming language, you cannot have multiple languages within the same file, as the language parser will not be able understand which syntax is that.
In order to embed a different language in another language, you need some kind of separator that will define when the new language or special type will start and when it will end.
I have this code to get a value count.
Short way:
my $count = $dbh->selectrow_array("SELECT COUNT(name) AS RESCOUNT FROM users");
Long way
my $sth = $dbh->prepare("SELECT COUNT(name) AS RESCOUNT FROM users");
$sth->execute() or die "$DBI::errstr";
my $count = $sth->fetchrow_array();
$sth->finish;
selectrow_array, fetchrow_array --> but I don't need an array. I checked the docs, but found nothing for scalars. Just methods for arrays and hashes.
The method I use is fast enough, but I was just curious if there is a better, fastest way to get a single value from the call. Or this is the fastest possible way?
The fastest way is to use fetchrow_arrayref or selectrow_arrayref, depending on how many executes you have. This only really makes a difference if executed in a loop and you have thousands (or rather hundreds of thousands) of rows.
When using fetchrow_array, it will make a copy every time, which slows you down. Also keep in mind that the behaviour for scalar context is only partly defined.
If called in a scalar context for a statement handle that has more than one column, it is undefined whether the driver will return the value of the first column or the last. So don't do that.
You can also do bind_col, which works with references.
There used to be a good presentation on DBI speeds from about 10 or more years ago that I can't find right now. Also take a look at this very old Perlmonks post, that explains quite a bit about performance.
Keep in mind that you should only do optimisation when you really know you need it. Most of the time you won't.
If "modern" means "I only heard of it recently", I'm feeling all modern with DBI's bind_col and bind_columns. Cribbing from a post by DBI hero Tim Bunce...
For your case:
my $sth = $dbh->prepare("SELECT COUNT(name) AS RESCOUNT FROM users");
my $count = 0;
$sth->bind_col(1,\$count); # bind to a reference to the variable
$sth->execute() or die "$DBI::errstr";
$sth->fetch;
print $count;
In a loop for a SELECT statement returning multiple records:
my $sth = $dbh->prepare(qq{SELECT name FROM users WHERE zip_code == '20500'});
my $name = '';
$sth->bind_col(1,\$name); # bind to a reference to the variable
$sth->execute() or die "$DBI::errstr";
while ($sth->fetch) {
print $name, "\n";
}
And with bind_columns this works:
my $sth = $dbh->prepare(qq{SELECT name,phone,address FROM users WHERE zip_code == '20500'});
my #fields = qw/name phone address/;
# With a 'SELECT All * ...', get all columns with #{$sth->{NAME_lc}}
my %data;
$sth->bind_columns( \( #data{#fields} ) ); # \(...) gives references to its elements
$sth->execute() or die "$DBI::errstr";
while ($sth->fetch) {
print "$data{name} lives at $data{address}, with phone $data{phone}.", "\n";
}
Once the setup is handled, the looping is simple to write and fast to run. (But, benchmark).
HTH, apologize if this diverges too much from the OP's problem statement. But it's the simplest and most direct way to get your returned data into the form of variable(s) you want, so you can move on to doing something with it...
Got a question for you all...
What would be the best way to search my table by array, that has an array in the table.
EG:
$var = (1,4,7,9,14)
$Query = "SELECT * FROM business_listings WHERE category IN ($var)";
'category' would have 4,27,89,101
How can I get this to match if one of the numbers in the $var matches one of the numbers in the table.
If your database column is a list of comma separated values, and you're searching for one value in that list, then you're in a different situation.
If your category column contains the text value 410,406,149,152, like you commented below, and you're searching for fields whose category contains 152, then you'll need to use MySQL's FIND_IN_SET() function.
If you have to check multiple values, then you need to use more than one FIND_IN_SET. If you read the documentation, you'll see that the first argument for FIND_IN_SET must be a single string, not a string list (it can't contain a comma). Use the following instead:
$var = "401,320,152";
$items = explode(",", $var);
foreach ($items as &$i) {
$i = "FIND_IN_SET('" . $i . "', `category`)";
}
$search = implode(" OR ", $items);
unset($i);
$query = "SELECT * FROM business_listings WHERE " . $items;
This will output:
SELECT * FROM business_listings WHERE
FIND_IN_SET('401', `category`) OR
FIND_IN_SET('320', `category`) OR
FIND_IN_SET('152', `category`)
The above script will work even if $var contains only one value.
Finally, as tadman mentioned, since we're getting into queries that can be tricky to build with prepared statements, you need to make sure you're escaping and sanitizing your input properly. For an example, if $var is being retrieved from the user somehow, then before you modify it in any way, you need to escape it with mysqli_real_escape_string():
$var = $mysqli->real_escape_string($var);
Assuming that $mysqli is your open MySQLi connection.
Hope this helps!
This is probably a somewhat simple question but I am trying to make sure that a query statement (specifically a select statement) contains a specific number of parameters only:
$result = mysql_query("select type,some_other_column from my_table");
$row = mysql_fetch_array($result);
$number = count($row);
print $number;
This returns twice the number I think it should return (4) - as I believe it must also be returning the key and the value as separate parameters.
The select statement above is just an example and it could be any number of statements. They could be a lot more complicated and the tests I have run do not seem to have any problems. I want to make sure that there are only ever two parameters (it can be any two) and they could be from different tables too.
I just want to make sure that it that what I am doing above is both the fastest way to check that the number of parameters is correct and that it won't get upset if there is a much more complicated statement given to it.
I am sure there is a really easy answer to this. Thanks in advance for any help.
Try mysql_fetch_assoc or mysql_fetch_row. Both functions available on php.net
mysql_fetch_array -- Fetch a result row as an associative array, a numeric array, or both. You end up having
$row["type"] = "somevalue"; // AND
$row[0] = "somevalue";
hence double the number
Whatever you SELECT would be in the $row variable, so in your code:
$result = mysql_query("select type,some_other_column from my_table");
$row = mysql_fetch_array($result);
/*
$row = array(
'type' => 'type_value',
'0' => 'type_value',
'some_other_column' => 'col_value',
'1' => 'col_value'
)
*/
$number = count($row);
print $number; // prints 4
I am not sure i understood your question right.
Do you just want to limit your number of returned values to one row?
If this is your point, you can add LIMIT 1 to your SQL-Query. This would, as it says, limit the number of results to one row.