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
Related
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?
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'
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
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), ...
I've got a series of Post models that hasAndBelongsToMany Media models. In certain function calls inside of the Post model, I don't need to retrieve the entire list of Media models. However, when I use the following code:
$this->unbindModel( array('hasAndBelongsToMany' => array('Media')) );
// Rebind to get only the fields we need:
$this->bindModel(
array('hasAndBelongsToMany' => array(
'Media' => array(
'className' => 'Media',
'joinTable' => 'media_posts',
'foreignKey' => 'post_id',
'associationForeignKey' => 'media_id',
'limit' => 1,
'fields' => array('Media.type', 'Media.path', 'Media.title')
)
)
)
);
$this->find('all', $params);
This limit only works on one of the first retrieved Post model and all following Post models have no associated Media:
Array
(
[0] => Array
(
[Profile] => Array
(
)
[Media] => Array
(
[0] => Array
(
[type] => photo
[path] => ''
[title] => ''
)
)
)
[1] => Array
(
[Profile] => Array
(
)
[Media] => Array
(
)
)
)
Any suggestions would be great. Thanks!
why not use the containable behaviour
// you would probably want the next line in the app_model ot be able to use it with all models
$this->Post->actsAs = array('Containable')
$params['conditions'] = array(
);
$params['contain'] = array(
'Media' => array(
'fields' => array(
'type', 'path', 'title'
),
'limit' => 1
)
);
$this->Post->find('all', $params);
EDIT:
Just tried that and got this sql (Module <-> Tag):
SELECT `Module`.`id` FROM `modules` AS `Module` WHERE 1 = 1
and
SELECT `Tag`.`id`, `ModulesTag`.`module_id`, `ModulesTag`.`tag_id`
FROM `tags` AS `Tag`
JOIN `modules_tags` AS `ModulesTag`
ON (`ModulesTag`.`module_id` IN (1, 2, 3, 4) AND `ModulesTag`.`tag_id` = `Tag`.`id`)
WHERE `Tag`.`belongs_to` = 'Module'
ORDER BY `Tag`.`name` ASC
LIMIT 1
obviously that cannot return the wanted result, as you would have to do a query for each Module result (which then again would result in way too many queries).
As a conclusion I would return all Tags (in my example) as the overhead in too many result rows is better than the overhead of too many queries..
Cake fetches all the Habtm-related records in one batch query and then assembles them into the results array afterwards. Any additional conditions you specify in the association will be used as is in the query, so it'll look something like this:
SELECT … FROM Media WHERE Media.id in (1, 2, 3, …) LIMIT 1
So it'll only retrieve a single HABTM model.
There's no apparently easy solution for this. Maybe you could think about the original premise again and why the "first" (LIMIT 1) record is supposedly the right one, maybe you can find a different condition to query on.
Failing that, you could rebind your models so Media has a hasMany relationship to medias_posts, the pivot table. For hasMany and belongsTo queries, Cake automatically does JOIN queries. You could use a GROUP BY clause then, which would give you the desired result:
SELECT … FROM Media JOIN medias_posts … GROUP BY medias_posts.post_id
You might also want to experiment with passing the 'join' parameter with the query, to achieve that effect without extensive rebinding.
$this->Media->find('all', array('join' => array(…), …));
Try this:
$this->yourModel->hasAndBelongsToMany['Media'] = false; // or null
And then set your HABTM association manually
$this->yourModel->hasAndBelongsToMany['Media'] = array(........);
Or simply modify the association without nulling it:
$this->yourModel->HABTM['Media']['fields'] = array(....)
CakePHP has a very powerful tool for this containable behaviour