Perl and MySQL user input to select a row? - mysql

Suppose I have a simple database table that doesn't have an ID_KEY but has a name column. I want to display the output like this
+----+---------+
| | name |
+----+---------+
| 1 | dog |
| 2 | cat |
| 3 | penguin |
| 4 | lax |
| 5 | whale |
| 6 | ostrich |
+----+---------+
Then have a <STDIN> for like, say, 3 to select penguin. 3 is just the line number that appears when you do the select call.
Is there any way to do this, or is it only possible with an id key associated and then a subsequent select statement matching that id key?

I misunderstood you at first but I've caught on. But it doesn't make much sense, as when you're entering a number into a Perl program you won't be working with the MySQL command-line tool, and won't be able to see what numbers to enter..
What you need to do is to write your Perl program so that it prints all the name fields from the table together with a line number. Then your program can translate from an input animal number to its name because it knows what it printed.
Something like this would work. Of course you will have to set the name, IP address and credentials correctly so that DBI can connect to the database.
use strict;
use warnings;
use DBI;
my $dbh = DBI->connect(
'DBI:mysql:database=animal_test',
'username',
'password',
{ RaiseError => 1 },
);
my $names = map #$_, $dbh->selectall_arrayref('SELECT name FROM animals');
for my $i ( 0 .. $#$names ) {
printf "%3d: %s\n", $i+1, $names->[$i];
}
print "\n";
print "Enter animal number: ";
my $animal = <>;
chomp $animal;
my $name = $names->[$animal-1];
printf "Animal chosen is %s\n", $name;

Option 1 - You would have put a id field in the DB if you want to find by integer 3 because row 3 will not always be penguin from an SQL query.
Option 2 - Dump the data into and array or hash and use the index of that to find the item from with in the variable and not the DB when 3 is captured from STIN.

Just use query:
my $select = $dbh->prepare('
SET #id:=0;
SELECT name,
#id = #id+1
FROM table
');

Related

Query products with Doctrine ind Akeneo

I like to fetch some products from the Database with a custom command in akeneo.
I'm using the ProductRepositoryInterface
public function read()
{
return $this->repository->findBy(
[
'enabled' => true,
'family' => ['projector', 'projector_child', 'projector_parent'],
]
);
}
And this is the generated query:
SELECT t0.id AS id1, t0.is_enabled AS is_enabled2, t0.created AS created3, t0.updated AS updated4, t0.family_id AS family_id5 FROM pim_catalog_product t0 WHERE t0.is_enabled = ? AND t0.family_id IN (?)
As you can see in the Statement, the family is threaded as an Id. But I want to search by the family code.
What I have to change?
In the Pim/Component/Catalog/Model/AbstractProduct is an attribute for the family and familyId. So there have to be a way to query for the family code.
Maybe it's relevant, but this is an Akeneo 1.6 installation.
So first, to query products in Akeneo, you should use the Product Query Builder (PQB). If you're using the 1.6, here is the link to the documentation to use it, it's pretty straightforward: https://docs.akeneo.com/1.6/cookbook/catalog/product/query.html
To have an exhaustive list of the filters on attributes & fields that can be used with the PQB, you can use the php app/console pim:product:query-help command on your PIM.
As you noticed, the family is not an attribute but a field, you'll find it in the field filters of the command above:
php app/console pim:product:query-help
Useable field filters...
+-----------------+--------------------------------+-----------------------------------------------------------+
| field | operators | filter_class |
+-----------------+--------------------------------+-----------------------------------------------------------+
| family | IN, NOT IN, EMPTY, NOT EMPTY | Pim\Bundle\CatalogBundle\Doctrine\ORM\Filter\FamilyFilter |
| family.id | IN, NOT IN, EMPTY, NOT EMPTY | Pim\Bundle\CatalogBundle\Doctrine\ORM\Filter\FamilyFilter |
| family.code | IN, NOT IN, EMPTY, NOT EMPTY | Pim\Bundle\CatalogBundle\Doctrine\ORM\Filter\FamilyFilter |
+-----------------+--------------------------------+-----------------------------------------------------------+
You can see now that you can search on the family.code field.
For your example, you'll end up with something like this:
<?php
// Get a new instance of the PQB
$pqbFactory = $this->getContainer()->get('pim_catalog.query.product_query_builder_factory');
$pqb = $pqbFactory->create([
'default_locale' => 'en_US',
'default_scope' => 'ecommerce'
]);
// Now you can search for products with your family codes
$pqb->addFilter(
'family.code',
'IN',
['projector', 'projector_child', 'projector_parent']
);
// Retrieve your products
$productsCursor = $pqb->execute();
foreach ($productsCursor as $product) {
// your custom logic
}

Using LIKE with prepared statement

I have a statement I wish to execute to find if a column containing a string contains a certain value.
+----+------+
| id | st |
+----+------+
| 0 | 2183 |
| 1 | 5820 |
| 2 | 2984 |
| ...| ... |
+----+------+
Say I wish to find all rows where st contains a 1, I would use these where conditions:
WHERE st LIKE "%1%"
OR st LIKE "1%"
OR st LIKE "1"
OR st LIKE "%1"
But how do I do this in a prepared statement?
$ps = $db->prepare("
SELECT id
FROM table
WHERE st LIKE "%:a%"
OR st LIKE ":a%"
OR st LIKE ":a"
OR st LIKE "%:a"
");
$ps->execute(array(
':a' => $var
));
This doesn't work evidently.
The % sign must be part of $var not part of the prepared Statement. also you only need %:a% it include all other parts of your where clause
$ps = $db->prepare("
SELECT id
FROM table
WHERE st LIKE :a
");
$ps->execute(array(
':a' => "%".$var."%"
));
ps = $db->prepare("
SELECT id
FROM table
WHERE st LIKE :a");
$ps->execute(array(
':a' => "%".$var."%"
));
Try above code.
And one more thing if you require rows which contains 1 in it,then there is no requirement of 4 like condition you only can achieve using '%1%'.
Hope this will help.

Choosing from multiple columns by column name stored in a column

I want to select from a table like this
|id|col_name|col1|col2|col3|col4|col5|...|col100|
| 1|col1 | 142| 241| 333| 417| 713|...| 125|
| 2|col5 | 927| 72| 403| 104| 136|...| 739|
| 3|col100 | 358| 842| 150| 125| 174|...| 103|
Select from column specified by col_name field. Something like
SELECT id,valueof(col_name) val FROM table1
which returns
|id|val|
| 1|142|
| 2|136|
| 3|103|
If you are using PHP (or modify the logic accordingly), you can do something like-
$colNames = array(col1, col5, col100); // or SELECT col_name FROM
table_name and store it in $colNames
foreach ($colNames as $val) {
$query = "SELECT $val FROM table_name WHERE col_name={$val}";
//execute this and store its result one-by-one into another array.
}
as I'm not sure if it could be done with single query.

Perl query and get column name

I need to make a query where I will be looking for a specific string through several columns and I need to know which column (name) contains the value that I need.
In the example below I need a query where I can ask which column contains the value 1000000000002101214 and that it returns f1. DB is MySQL and I need the programming to be done in Perl.
+---------------------+---------------------+---------------------+
| f1 | f2 | f3 |
+---------------------+---------------------+---------------------+
| 1000000000002101214 | 1000000000001989129 | 1000000000001881637 |
| 1000000000002080453 | 1000000000001968481 | 1000000000001862284 |
| 1000000000002085919 | 1000000000001973677 | 1000000000001866854 |
| 1000000000002075076 | 1000000000001963189 | 1000000000001857288 |
+---------------------+---------------------+---------------------+
I was able to find an almost-answer to my question from another site where I could get the column names of the fields in the table with the following:
my #cols = #{$sth->{NAME}}; # or NAME_lc if needed
while (#row = $sth->fetchrow_array) {
print "#row\n";
}
$sth->finish;
foreach ( #cols ) {
printf( "Note: col : %s\n", $_ );
}
The problem is partially resolved. In the example table I provided in the original question I needed to know on which column my answer resides, the query contains several OR statemens:
SELECT * FROM table WHERE (f1='1000000000002101214' OR f2='1000000000002101214' OR f3='1000000000002101214')
And I need the result to show that the column name where the number is located is f1. So....
Any thoughts?
I don't even know where to start
Check out Perl's DBI module. Read the documentation. You'll have to do something like below:
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
#Connect to your database, provide id, password
my $dbh = DBI->connect('dbi:mysql:perltest','root','password') or die "Connection Error: $DBI::errstr\n";
#Write your query
my $sql = "select * from database_schema";
my $sth = $dbh->prepare($sql);
#Execute it
$sth->execute or die "SQL Error: $DBI::errstr\n";
#Fetch the value
while (my #row = $sth->fetchrow_array) {
#Do something with your result
print "#row\n";
}
If you are new to Perl then see: http://learn.perl.org/
Edit: Query to find out column name based on the value found in column.
Select 'f1'
from database_schema
where database_schema.f1 = 1000000000002101214
union
Select 'f2'
from database_schema
where database_schema.f2 = 1000000000002101214
union
Select 'f3'
from database_schema
where database_schema.f3 = 1000000000002101214

How do I select noncontiguous characters from a string of text in MySQL?

I have a table with millions of rows and a single column of text that is exactly 11,159 characters long. It looks like this:
1202012101...(to 11,159 characters)
1202020120...
0121210212...
...
(to millions of rows)
I realize that I can use
SELECT SUBSTR(column,2,4) FROM table;
...if I wanted to pull out characters 2, 3, 4, and 5:
1202012101...
1202020120...
0121210212...
^^^^
But I need to extract noncontiguous characters, e.g. characters 1,5,7:
1202012101...
1202020120...
0121210212...
^ ^ ^
I realize this can be done with a query like:
SELECT CONCAT(SUBSTR(colm,1,1),SUBSTR(colm,5,1),SUBSTR(colm,7,1)) FROM table;
But this query gets very unwieldy to build for thousands of characters that I need to select. So for the first part of the question - how do I build a query that does something like this:
SELECT CHARACTERS(string,1,5,7) FROM table;
Furthermore, the indices of the characters I want to select are from a different table that looks something like this:
char_index keep_or_discard
1 keep
2 discard
3 discard
4 discard
5 keep
7 discard
8 keep
9 discard
10 discard
So for the second part of the question, how could I build a query to select specific characters from the first table based on whether keep_or_discard="keep" for that character's index in the second table?
this function does what you want:
CREATE DEFINER = `root`#`localhost` FUNCTION `test`.`getsubset`(selection mediumtext, longstring mediumtext)
RETURNS varchar(200)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT 'This function returns a subset of characters.'
BEGIN
SET #res:='';
SET #selection:=selection;
WHILE #selection<>'' DO
set #pos:=CONVERT(#selection, signed);
set #res := concat_ws('',#res,SUBSTRING(longstring,#pos,1));
IF LOCATE(',',#selection)=0 THEN
SET #selection:='';
END IF;
set #selection:=SUBSTRING(#selection,LOCATE(',',#selection)+1);
END WHILE;
RETURN #res;
END
Note: the CONVERT('1,2,3,4',signed) will yield 1, but it will give a warning.
I have it defined to be available in the database test.
The function takes two parameters; a string(!) with a list of positions, and a long string from where you want the characters taken.
An example of using this:
mysql> select * from keepdiscard;
+---------+------------+
| charind | keepordisc |
+---------+------------+
| 1 | keep |
| 2 | discard |
| 3 | keep |
| 4 | discard |
| 5 | keep |
| 6 | keep |
+---------+------------+
6 rows in set (0.00 sec)
mysql> select * from test;
+-------------------+
| longstring |
+-------------------+
| abcdefghijklmnopq |
| 123456789 |
+-------------------+
2 rows in set (0.00 sec)
mysql> select getsubset(group_concat(charind ORDER BY charind),longstring) as result from keepdiscard, test where keepordisc='keep' group by longstring;
+--------+
| result |
+--------+
| 1356 |
| acef |
+--------+
2 rows in set, 6 warnings (0.00 sec)
The warnings stem from the fast conversion to integer that is done in the function. (See comment above)
How about dynamic sql? (You will need to build the select part of the query)
CREATE PROCEDURE example_procedure()
BEGIN
--
--build the concat values here
--
SET #ids := '';
SET #S = 'SELECT #ids := built_concat_of_values FROM table';
PREPARE n_StrSQL FROM #S;
EXECUTE n_StrSQL;
DEALLOCATE PREPARE n_StrSQL;
END
You can write a php script to do this for you:
<?php
//mysql connect
$conn = mysql_connect('localhost', 'mysql_user', 'mysql_password');
if (!$conn) {
echo 'Unable to connect to DB: ' . mysql_error();
exit;
}
//database connect
$db = mysql_select_db('mydb');
if (!$db) {
echo 'Unable to select mydb: ' . mysql_error();
exit;
}
//get the keep numbers you’re going to use.
//and change the number into string so, for example, instead of 5 you get 'SUBSTR(colm,5,1)'
$result = mysql_query("SELECT number FROM number_table WHERE keep_or_discard='keep'");
$numbers = array();
while ($row = mysql_fetch_assoc($result)) {
$row = 'SUBSTR(colm,' . $row . ',1)';
$numbers = $row;
}
//implode the array so you get one long string with all the substrings
//eg. 'SUBSTR(colm,1,1),SUBSTR(colm,5,1),SUBSTR(colm,12,1)'
$numbers = implode(",", $numbers);
//pull the numbers you need and save them to an array.
$result = mysql_query("SELECT " . $numbers . " FROM table");
$concat = array();
while ($row = mysql_fetch_assoc($result)) {
$concat= $row;
}
And there you have an array with the correct numbers.
I'm sorry if you can't/don't want to use PHP for this, I just don't really know how to do this without PHP, Perl, Python or some other similar language. Hopefully this solution will help somehow...
The source of your difficulty is that your schema does not represent the true relationships between the data elements. If you wanted to achieve this with "pure" SQL, you would need a schema more like:
table
ID Index Char
1 0 1
1 1 2
1 2 0
charsToKeep
ID Index Keep
1 0 false
1 1 true
1 2 true
Then, you could perform a query like:
SELECT Char FROM table t JOIN charsToKeep c ON t.ID = c.ID WHERE c.Keep = true
However, you probably have good reasons for structuring your data the way you have (my schema requires much more storage space per character and the processing time is also probably much longer from what I am about to suggest).
Since SQL does not have the tools to understand the schema you have embedded into your table, you will need to add them with a user-defined function. Kevin's example of dynamic SQL may also work, but in my experience this is not as fast as a user-defined function.
I have done this in MS SQL many times, but never in MySql. You basically need a function, written in C or C++, that takes a comma-delimited list of the indexes you want to extract, and the string from which you want to extract them from. Then, the function will return a comma-delimited list of those extracted values. See these links for a good starting point:
http://dev.mysql.com/doc/refman/5.1/en/adding-functions.html
http://dev.mysql.com/doc/refman/5.1/en/adding-udf.html
To build the concatenated list of indexes you want to extract from the char_index table, try the group_concat function:
http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat
Hope this helps!