Ideal data structure for accessing related elements in a set - php

Let's say I have two sets
Set 1: A, B, C
Set 2: X, Y, Z
When element B is accessed we should also retrieve A and C together with B, the other elements in Set 1
I can achieve this with permuting each set and creating an entry in a hashtable for each element of the set and storing/copying all values available in that set, such as;
A: A, B, C
B: A, B, C
C: A, B, C
But that has a memory cost and can become inefficient for large data sets. I'm trying to achieve this without going for a graph database. Data is stored in MySQL. Any suggestions?

For example, you can to store pointers to identifiers of elements (or identifiers of abstract "couples" of elements (which are not really exist in database), if you will use the second way) in dataset in database for each data row. Then, in PHP, you should to build an array like this:
array(
'id1' => array(
'value' => 'A',
'links' => array('id2', 'id3')
),
'id2' => array(
'value' => 'B',
'links' => array('id1', 'id3')
),
'id3' => array(
'value' => 'C',
'links' => array('id1', 'id2')
)
);
Or, you can to mark couple of (A, B, C) with an identifier and than got an array like this:
array(
'couples' => array(
'c1' => array('id1', 'id2', 'id3')
),
'values' => array(
'id1' => array(
'value' => 'A',
'links' => array('c1')
),
'id2' => array(
'value' => 'B',
'links' => array('c1')
),
'id3' => array(
'value' => 'C',
'links' => array('c1')
)
)
);
Then, just write simple function to get all linked elements. In first case, you must to loop over links and simply get item from current array by current key, in second case, you must to loop over links, and get all elements from current couple by key in couples, and get all items from it.

I think you've just reached the chief driving force behind the creation of NOSQL DB's and SQL alternative :-)
There are two way to get this done with MySQL (assuming sets of arbitrary length) :
1- Have a reference on each set in your DB and use its unique identifier as a column in the item tables, it should be something like this :
id set_id name whatever_col1 whatever_col2
1 1 A ... ...
2 1 B ... ...
3 1 C ... ...
4 2 X ... ...
5 2 Y ... ...
...
Or create a key-pairs relation table having the couples (A,B), (A,C), (B,C) (X,Y), ...

Related

MongoDb + PHP: Cannot specify more than one positional proj. per query

I have a "users" collection containing 2 arrays of objects (cards and prizes) each of these 2 can contain data from different companies marked with the "id_company" field.
$list = $users->find(
array(
'cards.id_company' => '... id company ...',
'cards.is_active' => true,
),
array(
'projection' => array(
'_id' => 1,
'full_name' => 1,
'cards.$.id_company' => '...id company...',
'prizes.$.id_company' => '...id company...',
),
'limit' => 5,
)
);
I'm trying to select only the data relating to one company, consequently excluding the others, but I get the following error: Cannot specify more than one positional proj. per query.
Any solutions?

MySQL result multiple arrays

i.e : i have 2 tables
Product ( id, name )
Photo ( id, name, photo_id )
And I need to get result in array like this:
array(
'id' => 1,
'name' => product,
'photos' => array(
array('id' => 1, 'name' => 'photo1')
array('id' => 2, 'name' => 'photo2')
)
}
Is it possible in PHP using clear SQL?
I know that is possible to get 2 arrays and connect it but I have many records and I dont want to wase time to quering.
You have to add a foreign_key in your photo table "product_id".
Then create a method getPhotos() in your Product class with will collect all photos for your product.
Is it possible in PHP using clear SQL?
Not in a single SQL call. With a single call, this is the closest you can get:
array(
'id' => 1,
'name' => product,
'photo_id' => 1,
'photo_name' => 'photo1')
),
array(
'id' => 1,
'name' => product,
'photo_id' => 2,
'photo_name' => 'photo2')
)
Your only choice for the format you want is to run queries separately or to combine them into the data structure you want.
As mentioned, this is not possible with SQL. SQL is based on the relational model which is a 1-Normal-Form data model. That means, the result relation is also flat (no nested relations in a relation).
However, there are good frameworks which generate intermediary models in your corresponding target language (e.g. Python, Java, ...) that circumvent the impression of a flat data model. Check for example Django.
https://docs.djangoproject.com/en/1.8/topics/db/models/
Moo

CakePHP join with comma separated ids

Here I want to join two table with comma separated ids
For example my data is like:
[Restaurant] => Array
(
[RST_ID] => 171
[RST_NAME] => oneone
[RST_IMAGE] =>
[RST_CAT_ID] => 2,4,6
[RST_CT_ID] => 27
[RST_IS_TOP] => 3
[RST_QR_CODE] =>
[RST_CREATED_DATE] => 1394536725
[RST_MODIFIED_DATE] => 1394536725
[RST_STATUS] => 1
)
[Category] => Array
(
[CAT_ID] => 2
[CAT_NAME] => Vegetarian
[CAT_CREATED_DATE] => 1375175962
[CAT_MODIFIED_DATE] => 1375175962
[CAT_STATUS] => 1
)
My Model Code:
var $belongsTo = array(
'Category' => array(
'className' => 'Category',
'foreignKey' => 'RST_CAT_ID',
'conditions' => array('Category.CAT_ID IN ( Restaurant.RST_CAT_ID)')
)
);
Real Query:
SELECT
`Restaurant`.`RST_ID`, `Restaurant`.`RST_NAME`, `Restaurant`.`RST_IMAGE`,
`Restaurant`.`RST_CAT_ID`, `Restaurant`.`RST_CT_ID`, `Restaurant`.`RST_IS_TOP`,
`Restaurant`.`RST_QR_CODE`, `Restaurant`.`RST_CREATED_DATE`,
`Restaurant`.`RST_MODIFIED_DATE`, `Restaurant`.`RST_STATUS`,
`Category`.`CAT_ID`, `Category`.`CAT_NAME`, `Category`.`CAT_CREATED_DATE`,
`Category`.`CAT_MODIFIED_DATE`, `Category`.`CAT_STATUS`, `City`.`CT_ID`,
`City`.`CT_NAME`, `City`.`CT_CREATED_DATE`, `City`.`CT_MODIFIED_DATE`,
`City`.`CT_STATUS`
FROM `dailybit_dailybites`.`restaurant` AS `Restaurant`
LEFT JOIN `dailybit_dailybites`.`category` AS `Category`
ON (`Restaurant`.`RST_CAT_ID` = `Category`.`CAT_ID`
AND `Category`.`CAT_ID` IN ( `Restaurant`.`RST_CAT_ID`))
LEFT JOIN `dailybit_dailybites`.`city` AS `City`
ON (`Restaurant`.`RST_CT_ID` = `City`.`CT_ID`)
WHERE 1 = 1
So what’s the solution here?
It's giving me just one category data that for first id only.
First have a look at this question: MySQL search in comma list
As you can see the belongsTo query is just generating a join on the single id, CakePHP by default doesn't respect this special case. You will have to alter your query and pass all the ids manually, but your DB design is bad and it doesn't follow the CakePHP conventions at all.
How do you prevent duplicates (which would waste space)
How do you remove a given value (Requires custom function, leading to possibility of errors?
How do you respond to performance issues as the size of my tables increase?
Instead of changing the query you should change this awkward DB design. You want to use HABTM here and a join table: Restaurant hasAndBelongsToMany Categoryy.
restaurants <-> restaurants_categories <-> categories
If you insist on using this bad DB design you'll have to use bindModel() and set the conditions manually:
'conditions' => array('FIND_IN_SET (Category.CAT_ID, ' . $listOfIds. ')')
I haven't tested this, try it yourself, see FIND_IN_SET() vs IN()
You'll have to have another method that gets you all the ids you want here before. Like I said, this is ineffectice and bad design.
You have to set your foreign Key false and find_in_set condition
var $belongsTo = array(
'Category' => array(
'className' => 'Category',
'foreignKey' => false,
'conditions' => array('FIND_IN_SET(Category.CAT_ID,Restaurant.RST_CAT_ID)')
)
);
// you can pass an array at the place of 'Restaurant.RST_CAT_ID'

Performance mySQL: Long string in one column in database table or add extra columns?

if I have a muli-dimensional array like that I take from a form submit:
$participants = array(
'participant1'=> array(
'name'=>'jim',
'age' => '15',
'grade' => '8th'),
'participant2'=> array(
'name'=>'tom',
'age' => '17',
'grade' => '9th'),
....
);
Is it better two store whole array into one db column named "Participants" or create a separate column in the row for each participant PERFORMANCE wise if i have a maximum number of participants?
using separate column would be better at the point of normalization also if you need only name or age than it would be better you dont need to fetch all

How do I combine two arrays in PHP based on a common key?

I'm trying to join two associative arrays together based on an entry_id key. Both arrays come from individual database resources, the first stores entry titles, the second stores entry authors, the key=>value pairs are as follows:
array (
'entry_id' => 1,
'title' => 'Test Entry'
)
array (
'entry_id' => 1,
'author_id' => 2
I'm trying to achieve an array structure like:
array (
'entry_id' => 1,
'author_id' => 2,
'title' => 'Test Entry'
)
Currently, I've solved the problem by looping through each array and formatting the array the way I want, but I think this is a bit of a memory hog.
$entriesArray = array();
foreach ($entryNames as $names) {
foreach ($entryAuthors as $authors) {
if ($names['entry_id'] === $authors['entry_id']) {
$entriesArray[] = array(
'id' => $names['entry_id'],
'title' => $names['title'],
'author_id' => $authors['author_id']
);
}
}
}
I'd like to know is there an easier, less memory intensive method of doing this?
Is it possible you can do a JOIN in the SQL used to retrieve the information from the database rather than fetching the data in multiple queries? It would be much faster and neater to do it at the database level.
Depending on your database structure you may want to use something similar to
SELECT entry_id, title, author_id
FROM exp_weblog_data
INNER JOIN exp_weblog_titles
ON exp_weblog_data.entry_id = exp_weblog_titles.entry_id
WHERE field_id_53 = "%s" AND WHERE entry_id IN ("%s")
Wikipedia has a bit on each type of join
Otherwise the best option may be to restructure the first array so that it is a map of the entry_id to the title
So:
array(
array(
'entry_id' => 1,
'title' => 'Test Entry 1',
),
array(
'entry_id' => 3,
'title' => 'Test Entry 2',
),
)
Would become:
array(
1 => 'Test Entry 1',
3 => 'Test Entry 2',
)
Which would mean the code required to merge the arrays is simplified to this:
$entriesArray = array();
foreach ($entryAuthors as $authors) {
$entriesArray[] = array(
'id' => $authors['entry_id'],
'title' => $entryNames[$authors['entry_id']],
'author_id' => $authors['author_id']
);
}
I've rearranged some of my code to allow for a single SQL query, which looks like:
$sql = sprintf('SELECT DISTINCT wd.field_id_5, wd.entry_id, mb.email, mb.screen_name
FROM `exp_weblog_data` wd
INNER JOIN `exp_weblog_titles` wt
ON wt.entry_id=wd.entry_id
INNER JOIN `exp_members` mb
ON mb.member_id=wt.author_id
WHERE mb.member_id IN ("%s")
AND wd.entry_id IN ("%s")',
join('","', array_unique($authors)),
join('","', array_unique($ids))
);
This solves my problem quite nicely, even though I'm making another SQL call. Thanks for trying.
In response to your comment on Yacoby's post, will this SQL not give the output you are after?
SELECT exp_weblog_data.entry_id, exp_weblog_data.field_id_5 AS title_ie, exp_weblog_titles.author_id
FROM exp_weblog_data LEFT JOIN exp_weblog_titles
ON exp_weblog_data.entry_id = exp_weblog_titles.entry_id
WHERE exp_weblog_data.field_id_53 = "%S"
Every entry in exp_weblog_data where field_id_53 = "%S" will be joined with any matching authors in exp_weblog_titles, if a an entry has more than one author, two or more rows will be returned.
see http://php.net/manual/en/function.array-merge.php

Categories