Subquerying in mysql with full-text querying - mysql

I am trying to query a database. I already have a file that includes some primary keys out of this whole database. Now I want to filter these primary keys and get only those primary keys which also agree to "other condition". My primary keys are associated to abstracts in the database. The abstracts are full-text indexed. Now I want to consider the abstracts using the given primary keys, look for my "other condition(terms)" in those abstracts and if its present I want to pull their primary keys out(which is going to be same from the file). My "other condition" is another file with a list of terms. I want to get the abstracts that contain those terms within the given primary keys.
My full-text search is something like this:
while(<FILE1>){
$PK = $_;
foreach $foo(#foo){
my $sth = $dbh->prepare(qq{
SELECT value
FROM value_table
WHERE MATCH (column_text_indexed) AGAINST (? IN BOOLEAN MODE)
}) AND primary_key=$PK;
$sth->execute(qq{+"$foo"});
}
}
where $PK is coming from the list of primary keys I already have.
$foo will be the list of the terms (condition 2) I am looking for.
Normally, I can run this query number of $PK times number of $foo. But I learned something about optimization by sub querying where I won't be running my query # $PK times # $foo. That will get rid of inner loop but will still form combination of every $PK with every term in file 2 that is #foo. Something like as follows:
while(<FILE1>){
$PK = $_;
my $sth = $dbh->prepare(qq{
SELECT value
FROM value_table
WHERE MATCH (column_text_indexed) AGAINST (**SUB QUERYING HERE**)
}) AND primary_key=$PK;
$sth->execute(qq{+"$foo"});
}
Just I don't know how to do it. I may be wrong with the syntax. I want to know how to write code for full-text search as well as a subquery. I hope this will be efficient than querying directly the combinations. Any help is greatly appreciated.

I don't think you need to use a subquery. But you can still get rid of the inner loop by combining the match strings.
my $against = join ' ', map {qq/"$_"/} #foo;
while (my $PK = <FILE1>) {
chomp $PK;
my $sth = $dbh->prepare(qq{
SELECT value
FROM value_table
WHERE primary_key = ?
# no '+' so it can match against at least one of the words in the list
AND MATCH (column_text_indexed) AGAINST (? IN BOOLEAN MODE)
});
$sth->execute($PK, $against);
Update
I revised it and have completely removed the query from the loops.
my #primary_keys;
while (my $PK = <FILE1>) {
chomp $PK;
push #primary_keys, $PK;
}
my $PK_list = join ',', map {qq/'$_'/} #primary_keys;
my $against = join ' ', map {qq/"$_"/} #foo;
my $sth = $dbh->prepare(qq{
SELECT value
FROM value_table
# placeholders can only represent single scalar values so $PK_list can't be bound
WHERE primary_key IN ($PK_list)
# no '+' so it can match against at least one of the words in the list
AND MATCH (column_text_indexed) AGAINST (? IN BOOLEAN MODE)
});
$sth->execute($against);
# continue with fetching the rows
...;

Your syntax looks dodgy. I think you meant:
while(<FILE1>){
$PK = $_;
foreach $foo (#foo){
my $sth = $dbh->prepare(qq{
SELECT value
FROM value_table
WHERE MATCH (column_text_indexed)
AGAINST (**SUB QUERYING HERE**)
AND primary_key=$PK }); # '})' after AND clause
$sth->execute(qq{ $foo });
}
}
But why not make $PK an additional argument in this case (and use best practices)? :
while ( my $PK = <FILE1> ) {
chomp $PK; # Remove trailing newline
foreach my $foo ( #foo ) { # Lexical $foo
my $sth = $dbh->prepare( qq{
SELECT value
FROM value_table
WHERE MATCH (column_text_indexed)
AGAINST (**SUB QUERYING HERE**)
AND primary_key=? }); # Extra placeholder
$sth->execute( $foo, $PK );
}
}

If you want efficiency I would recommend to use least database transactions and operations. So in this case I think the best option is to just get the abstract from the database based the primary key and then search for the terms in that abstract by doing simple string search in your pearl or any other standard language code. I am not very sure of the length of your list of terms. But if its possible you can save it in a standard data-structure like array or list. The same operation in database would definitely take lot more time. I am not that good with pearl syntax so I am writing the algorithm.
for all the terms in PK get the abstract as a string variable :
for each term in array/list: find the term in string variable containing abstract. If found add the PK to a new file.
continue with next pk.
if not found then continue next term in array/list.

Related

Comparing List of regex to a string

i have question i don't know better approach to do it in mysql. I have a table in mysql with list of regex's each regex represent a company order number
i want to be able to compare a number to that list to get which company this number belongs to. the lazy way is to list all the regex in php and then using loop to get the company , but i want to do this using the power of mysql .
Like #Mech mention this might be vague.
i will try to explain it more :
I have two tables table with actual regex pattern in plain text in a column like "^[8]{1}[0-9]{10}$"
and this pattern belong to a company , there is more than 500 regex patterns .
Thank you.
Here you go #BM2ilabs. A function as requested :)
carrierID(88888141234);
function carrierID($ordernum) {
// create a connection to your db here
// fetch data needed for loop
$sql = "SELECT regex, carrier_id FROM `company_tbl_from_image`";
// fetch results
$results = $conn->query($sql);
// loop through $results
foreach ($results as $result) {
// individually check against each regex in the table
$regex = $result[regex];
// find first instance of $regex, where the $ordernum is unique, there should only be one match
if (preg_match('/'.$regex.'/', $ordernum)) {
$carrier_id = $result[carrier_id];
break; // remove break to show other matches
}
}
// check if $carrier_id is empty
if ($carrier_id <> "") {
echo $carrier_id;
} else {
echo "No carrier ID found.";
}
}
MySQL only option. Just search this:
SELECT carrier_id FROM `company_tbl_from_image` WHERE 'order number' REGEXP regex

Is it possible to insert sql query in php array value?

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.

perl DBI, fastest way to get a single scalar value

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...

MYSQL Search by arrays

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!

Doctrine query returns extra field in result

I have a Doctrine query;
$q = Doctrine_Query::create()->select('s.monthly_volume')
->from('SearchVolume s')
->innerJoin('s.Keywords k')
->where('k.group_id = ?',array($group_id));
I just want it to return the monthly_volume value in the result array. It currently returns monthly_volume and id, I don't want it to return the id in result.
Doctrine automatically adds the primary key field to the results in almost every type of hydration mode.
In a case like this where you want a simple array and only have a single field being selected, the answer is the Single Scalar Hydration mode. Use it like this:
$q = Doctrine_Query::create()->select('s.monthly_volume')
->from('SearchVolume s')
->innerJoin('s.Keywords k')
->where('k.group_id = ?');
$monthly_volumes = $q->execute(array($group_id), Doctrine_Core::HYDRATE_SINGLE_SCALAR);
You should find that $monthly_volumes is a simple one-dimensional array containing only the value(s) you wanted.