Mysql: get all distinct values from field serialized by PHP - php

I have a movies table (has data from a legacy project) with the field genre which contains values serialized by PHP like:
a:3:{i:0;s:9:"Animation";i:1;s:9:"Adventure";i:2;s:5:"Drama";}
I'm working in a search page, & I need to find all unique genres of the current search result to be used as a filter in the page,
as an example, if the search result was these 2 movies:
The Dark Knight (action, crime, drama)
Black Knight (fantasy, adventure, comedy)
I want to know the combination of there genres, which will be:
['action', 'crime', 'drama', 'fantasy', 'adventure', 'comedy']
how to get the genres array? (I'm using Yii2).

You should unserialize your data
$data = 'a:3:{i:0;s:9:"Animation";i:1;s:9:"Adventure";i:2;s:5:"Drama";}';
$data = unserialize($data);
print_r($data);
and you will get
Array
(
[0] => Animation
[1] => Adventure
[2] => Drama
)
If you need to search the entire table for "Drama" to decide which shows/movies to display, you could always use wildcards in your search
select * from table where column like '%Drama%'
but of course make sure to take appropriate database precautions.

Suppose you have a serialize string that have various values in different position and you need to search from serialized field. You want particular key’s value using MySQL query. It is very easy with MySQL “%like%” statement but “%like%” fetches more matches which you do not require.
Search from serialized field: Suppose you have a following serialize string in database:
a:9:{s:2:"m1";s:4:"1217";s:2:"m2";s:8:"9986-961";s:2:"m3";s:19:"1988-03-07 00:00:00";s:2:"m4";s:0:"";s:2:"m5";s:0:"";s:2:"m6";s:0:"";s:2:"m7";s:3:"104";s:2:"m8";s:6:"150000";s :2:"m9";s:18:"Ok Then, Yes It Is";}
And you need the row in which the m9 value is ‘Yes It Is’.
So the sql will be like:
SELECT * FROM table WHERE field REGEXP '.*"array_key";s:[0-9]+:".array_value.".*'
test code
SELECT * FROM table WHERE field REGEXP '.*"m9";s:[0-9]+:".Ok Then, Yes It Is.".*'

Related

Query Serialized Data

I 1000+ have rows of SQL data that look like the followinng
umeta_id | user_id | meta_key | meta_value
433369 | 44 | all_contests | a:1:{s:12:"all_contests";a:2:{i:5011;a:1:{s:7:"entries";i:3;}i:8722;a:1:{s:7:"entries";i:3;}}}
433368 | 63 | all_contests | a:1:{s:12:"all_contests";a:2:{i:5032;a:1:{s:7:"entries";i:3;}i:8724;a:1:{s:7:"entries";i:3;}}}
When you unserialize one of those values you get an array like the following:
Array
(
[all_contests] => Array
(
[5011] => Array
(
[entries] => 3
)
[8722] => Array
(
[entries] => 3
)
)
)
I am trying to create a leaderboard out of all the users for a given contest id. The "all_contests" key holds an array keyed by the ids of all the contests the user signs up for. Inside that is the entries for the given contest.
The query needs to look at all the rows containing 'all_contests' keys and find the 10 highest entries values for a given contest id.
I'm not even sure that it is possible to reliably search inside of a piece of serialized data the way that I'm looking to.
No, it's impractical to search inside a piece of serialized data.
You can do it with enough meticulous usage of MySQL String Functions like INSTR(), SUBSTR(), FIELD(), and so on. But writing queries like that is time-consuming to develop the query, and it won't have good performance.
What you're doing is a variation on a common mistake: storing a comma-separated list in a column of one table.
This is avoiding creating the intersection table to represent a many-to-many relationship. In other words, you have two tables for users and contests, and you need a third table, in which each row represent's one user's participation in one contest.
See my answer to Is storing a delimited list in a database column really that bad?
The answer is about comma-separated lists, but it applies equally to serialized arrays like you're doing.

Matching json array from mysql table

i have a column in my users database called "tags" which could look like this:
`[1,2,3,4]`
That array only has numbers.
I am now wondering if I can select from the database where the json array has specific value in it. For example:
`SELECT * FROM users WHERE tags = "HAS 1 IN ARRAY"`
I was testing by just matching the number 1 but then it will probably return values with 10 to 19 if any user has number 10-19 in that array.
Try this:
SELECT * FROM users WHERE tags REGEXP '[[:<:]]1[[:>:]]';
For more info, see docs Regular Expressions.
PS I would recommend to decompose table, move tags into dependent table. On large table regexp will be slow...

Joining MySQL tables and comparing columns to list data

I have been struggling with the proper way to extract the data I need. I am using MySQL with PHP and will be putting the data into a list format. My only problem is with the actual query itself. Here is how the DB is setup:
I have a 'chars' table for characteristics with the columns 'id', 'descrip', and 'class'.
I have a 'animal' table with the columns 'animal_id', 'charlist', ...
In 'chars' id is an incremented int, 'descrip' and 'class' are text/strings.
'id' and 'descrip' are different for every row but 'class' will be the same sometimes having a value like 'habitat', 'size', 'diet', et cetra.
What I am going for is a list that will look like this:
Habitatundergroundarctic
Dietfishinsectsomnivore
Here is where I start to have trouble. On a 'Details' page I am showing all of the data specific to a certain animal (whichever is clicked) and to reference the 'characteristics' each animal has it's own 'charlist' value in the database. This value is a string of numbers that reference the 'chars' table, like '2,55,67,90,122'.
So I've pulled all my (specific) animal data to a php variable on the page that I want to supply with the information. Now I need to use the 'charlist' data from that animal to lookup and list the characteristics relative to the animal.
My queries are looking like this, and I know I'm waaay off:
SELECT * FROM 'chars' LEFT INNER JOIN 'animals' ON chars.id IN (animals.charlist) WHERE ...
I've tried a lot of different ways and this is where I get lost. My brain is telling me to join the tables, find the numbers that are in the charlist WHERE animal.animal_id = mysql_real_escape_string($animal_id) - and of course MySQL tells me I can't do it this way.
I know that for the titles of the lists I'll probably have to do this with a GROUP BY condition.
Any help on the query and query syntax would be great. I'm fairly new to MySQL and I'm very happy and eager to learn how to do this right.
Thanks for reading.
a IN (x) returns true if (and only if) a equals x.
If x is a string (even one delimited by commas), then the statement will only be true if a is equal to that same string. That is, 123 IN ('123,456,789') is false. Note that this is not the same as passing multiple arguments such as 123 IN (123, 456, 789).
In MySQL, one could instead use FIND_IN_SET(), which expects a string delimited by commas. However, storing delimited lists in database columns is a really bad idea. You should instead define a third, relationship, table in which you store foreign keys into both of your existing tables: (animal_id, characteristic_id):
CREATE TABLE animal_characteristics (
PRIMARY KEY (animal_id, characteristic_id),
FOREIGN KEY (animal_id) REFERENCES animal (animal_id),
FOREIGN KEY (characteristic_id) REFERENCES chars (id)
) SELECT animal.animal_id, chars.id AS characteristic_id
FROM animal JOIN chars ON FIND_IN_SET(chars.id, animal.charlist)
;
Then you can do:
SELECT *
FROM animals
JOIN animal_characteristics USING (animal_id)
JOIN chars ON chars.id = animal_characteristics.characteristic_id
WHERE ...

Storing multiple values in one MYSQL field

I am currently planning out how a table will look in MYSQL database. I want to do something like the below, where plant1 and plant2 would be the columns, and then each of those plants would have characteristics assigned to them as you see below.
Is it possible to display info this way in MYSQL?
Array (
[plant1] => Array (
[image_url] => http://www.example.com/image1.png
[botanical_name] => Foo
[common_name] => Bar
),
[plant2] => Array (
[image_url] => http://www.example.com/image2.png
[botanical_name] => Foo
[common_name] => Bar
)
Well, i don't suggest that, but you can insert php array to that field and then parse it in php when you are getting data from database. So the content of that field would be like that:
$plant1 = array('plant1' => array('image_url' => 'http://www.example.com/image1.png', 'botanical_name' => 'Foo', 'common_name' => 'Bar'));
$plant2 = array('plant2' => array('image_url' => 'http://www.example.com/image2.png', 'botanical_name' => 'Foo', 'common_name' => 'Bar'));
You can insert $plant1 $plant2 to your mysql fields and then print it like this to get wanted result:
print_r(array_merge($mysql_plant1, $mysql_plant2));
Function array_merge connects arrays together.
You can do it storing as a serialized value (http://www.php.net/manual/en/function.serialize.php) but it's not a good idea.
For that you will have to create another table to store the caracteristics of each plant like:
**plants**
id_plant
name
**plants_ccharacteristics**
id_plant_characteristic
fk_plant
name
value
In that way you can store it properly.
As an extra point, that structure you want to store looks quite good for use NoSQL databases like MongoDB.
Your table design is flawed. What you should have is Plant being a row, with each item in the array being a column in the plants table.
If you need Plants to be a column in another table, then assign the first column of the plants table to be a unique, auto-incrementing key and then put that key in the column of the other table. Eg:
**Garden_Plants**
gp_key
gp_garden_id
gp_plant_id <-- p_id from Plants_Table
**Plants_Table**
p_id
p_botanical_name
p_common_name
p_image_url
you would have a table called plants, this would have a related table called plantDetails that has a referencing ID from the plant in question.
this would give 1 plant, with multiple references
This doesn't seem optimised for querying the database, however if you have a specific reason for doing it this way please let us know..
A more optimised solution:
Have a table called Plants, with fields ID, image_url, botanical_name, common_name.
Store all plants in that table. ID is a unique Identifier for that plant, probably set with autonumber so cannot be duplicated
If you need to store some kind of relationship between 2 plants, have a table called plant_relationships with fields for something like plant_ID_1 and plant_ID_2
You can then query your full list of plants directly, or query the relationships table to find plants related to other plants
If you want multiple values in one mysql field, i would suggest to put your information in one string before storing it in the database. Each substring need to be separated by a character that will not be used in the substrings.
$value1 = aaa
$value2 = bbb
$value3 = ccc
$valueAll = aaa:bbb:ccc
When you get the data out of the DB, you will need to split the string with the : or the other character selected.

cakephp fetch from among multiple values in single column

I'm using cakePHP 2.3 to build an application with a restaurant database. Now, the person before me has saved restaurant details in a table called 'locations' which has a row for cuisine which has multiple values in one column, like 1 location can have chinese, indian, korean etc.
If I fetch data using post and retrieve from the column using exact value like this:
$restaurant_cuisine = $this->params['url']['restaurant_cuisine'];
$data = $this->Location->findAllByCuisine($restaurant_cuisine);
Then it is displaying value only if it exactly matches...eg:If i post "north indian,chinese,mughlai" then it matches it word to word.
I want a query where I can search Chinese and all restaurants having chinese display even if they have other cuisines.
Please help!
you can't use magic find type because they don't allow LIKE conditions. You must use full syntax:
$data = $this->Location->find(
'all',
array(
'conditions' => array('Location.cuisine LIKE' => "%$restaurant_cuisine%")
));

Categories