Can you make multilevel arrays with one query? - mysql

Is it possible to make a multilevel array using a mysql query? E.g. if I want to get 4 pictures for each product?
[1] => Array( 'name' => 'Product 1', 'picture' => array('picture1','picture2','picture3','picture4') )
[2] => Array( 'name' => 'Product 2', 'picture' => array('picture5','picture6','picture7','picture8') )
Or do I need to make a foreach to loop through the products and then in the foreach make a mysql query to get each products pictures?
EDIT:
My structure is:
P_Attributes
--------
id, int(15)
name, varchar(256)
P_AttributeValues
--------
id, int(15)
value, varchar(256)
attribute_id, int(15) [NOTE: This is connected to P_Attributes.id]
Then I want to get ALL P_AttributeValues to a P_Attribute row - and get it in ONE query. Is that possible?
EDIT 2:
With the query made by the accepted answers author I made it work with this PHP-code:
$attributevalues = $auctionClass->get_rows($id);
$attr_val = array();
foreach($attributevalues as $k => $v){
$attr_val[$v->AID]['attr_name'] = $v->AName;
$attr_val[$v->AID]['parameters'][] = array('attr_value_name' => $v->name, 'id' => $v->id);
}

Here is a good article on doing this type of query:
http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/
You might be able to adjust their examples to fit your needs.

If you just use a normal JOIN query and order it, you can get rows coming out of the records that can be formed in to the necessary hierarchical structure.
SELECT pa.id AS product_id, pa.name , pav.id AS product_attr_id, pav.value
FROM P_Attributes pa JOIN P_AttributeValues pav ON pav.attribute_id = pa.id
ORDER BY pa.id ASC, pav.id ASC
This will generate rows like this:
product_id, name, product_attr_id, value
1, "Product 1", 1, "picture1"
1, "Product 1", 2, "picture2"
1, "Product 1", 3, "picture3"
1, "Product 1", 4, "picture4"
2, "Product 2", 5, "picture5"
2, "Product 2", 6, "picture6"
2, "Product 2", 7, "picture7"
2, "Product 2", 8, "picture8"
No idea what MySQL extension you are using, presumably PHP as I mentioned it and you didn't correct me. If you fetch the associative array per record returned, which will be, per record, in this form:
array('product_id' => 1, 'name' => "Product 1",
'product_attr_id' => 1, 'value' => "picture1");
If you have a main data array called $products, you can produce it by just putting this code in the loop, assuming the record is called $productRec and filled in the loop before this.
if (!array_key_exists($productRec['product_id'], $products)) {
$products[$productRec['product_id']] = array('name' => $productRec['name'],
'picture' => array());
}
$products[$productRec['product_id']]['picture'][$productRec['product_attr_id']] =
$productRec['value'];
Using the IDs for the keys should be alright, presuming that they are primary keys with no duplicates. Will aid look up that way, rather than losing that data.

Depending on your table structure, you may be able to pull rows that have everything you're looking for, and then code the creation of the array from that row.

Related

Yii2 Select2 and related tables

I have two related tables. For example,
tbl_one
id
name
tbl_second
id
id_tblone(this is fk)
name
For example:
tbl_one
id: 1
name: input one
tbl_second
id: 1
id_tblone: 1
name: data one
id: 2
id_tblone: 1
name: data two
Select will show:
input one
input two
I want to use Kartik's select in a form that will save new data in tbl_second. So I need Select2 to select id_tblone (or id from tbl_one) and his according name. User needs to select, let's say proper category (from first table - I have fk that relates those two tables) and add some new data under that category in second table.
What would be proper way to do this?
Edit: i have managed so far to show proper names under proper id_tblone, but it saves data with value null in id_tblone column in my second table
In controller:
$query = array();
$query = (new \yii\db\Query())
->select(['id', 'name'])
->from('tbl_one')
->leftJoin('tbl_second', 'tbl_second.id_tblone = tbl_one.id')
->all();
in view:
$data = ArrayHelper::map($query,'name', 'id');
echo Select2::widget([
'name' => 'newname',
'data' => $data,
'pluginOptions' => [
'allowClear' => true
],
]);

Not equal condition with findAll()

I am trying to pull record from a table using the following code
$userId = Yii::$app->user->id;
$lists = PromoLists::findAll(['user_id' => $userId, 'list_type' => 'custom']);
which outputs a query like below
select * from promo_lists where user_id ='$userId' and list_type='custom'
But i am unable to find any thing in the documentation that would help me achieve it with the following condition.
select * from promo_lists where user_id ='$userId' and list_type='custom' and status!='deleted'
as the status is an ENUM field and there are 4 different status
'active','pending','rejected','deleted'
currently i used the following approach
PromoLists::findAll(['user_id' => $userId, 'list_type' => 'custom', 'status'=>['active','pending','rejected']]);
which outputsthe following query
select * from promo_lists where user_id ='$userId' and list_type='custom' and status in ('active','pending','rejected')
which somehow achieves the same thing but this query would need to be edited every time when there is a new status type added to the table column status.
i know i can do this by using PromoLists::find()->where()->andWhere()->all()
but how to check with != / <> operator using findAll().
Simply like this:
PromoLists::find()->where(['and',
[
'user_id' => $userId,
'list_type' => 'custom',
],
['<>', 'status', 'deleted'],
])->all();
Using operator format in condition
http://www.yiiframework.com/doc-2.0/guide-db-query-builder.html#operator-format
PromoLists::find()
->andWhere([
'user_id' => $userId,
'list_type' => 'custom',
['!=', 'status', 'deleted']
])
->all();

Cakephp query for enum field in database

I have a field in database. It's type is enum and it looks like
enum('NO ANSWER', 'ANSWERED', 'BUSY').
I need to put this values into dropdown. How can I write query in cakephp?
I tried:
$result = TableRegistry::get('Calls')->find('list', ['valueField' => 'disposition'])->distinct('disposition')->toArray();
But it returns
[
(int) 1 => null,
(int) 77 => '',
(int) 64 => 'NO ANSWER',
(int) 65 => 'ANSWERED',
(int) 72 => 'BUSY'
]
but I need something like this:
[
(int) 1 => 'NO ANSWER',
(int) 2 => 'ANSWERED',
(int) 3 => 'BUSY'
]
I need to put this values into dropdown
Unless the enum values are going to change frequently (and if the are, why would you use an enum..) just put the array of data you need somewhere:
$options = [
'NO ANSWER' => 'NO ANSWER',
'ANSWERED' => 'ANSWERED',
'BUSY' => 'BUSY'
];
And then use it:
echo $this->Form->select('field', $options);
Note that the key in $options is what will be submitted, the value is what will be displayed. More info about the select method is in the documentation.
I found this answer somewhere on SO, but I couldn't find it again. You can do this:
$cols = $this->Model->query("show columns from table_name like 'enum_column_name'")
$enum = explode(',', substr(str_replace(array("'", "(", ")"),'',$cols[0]['COLUMNS']['Type']), 4));
$options = array_combine($enum, $enum);
Then in your form, you can use the end of AD7six's answer and add:
echo $this->Form->select('field', $options);
The problem is that the values of enum are defined in the create table, they are not a piece of data available when you query your table's data. How can I get enum possible values in a MySQL database? SO topic describes how to get the values of the enum through a php code. Just make sure that you reassign the keys for the enum values so that the keys start from 1, and not from 0 (0 stands for empty value).

Merging Many to Many Results

I'm using the following SELECT statement:
SELECT *
FROM prefix_site_tmplvars
LEFT JOIN prefix_site_tmplvar_contentvalues
ON prefix_site_tmplvar_contentvalues.tmplvarid = prefix_site_tmplvars.id
LEFT JOIN prefix_site_content
ON prefix_site_tmplvar_contentvalues.contentid = prefix_site_content.id
WHERE prefix_site_tmplvar_contentvalues.value = "chocolate"
This is what I get back:
[id] => 2
[name] => flavor
[value] => chocolate
[id] => 2
[name] => type
[value] => cookie
This is the result I'd like to get:
[id] => 2
[flavor] => chocolate
[type] => cookie
Is there a way to combine my results so I don't have a bunch of rows referring to the same ID? If now, how should I handle this?
I'm using Modx and this is working with the Template Variable tables: http://wiki.modxcms.com/index.php/Template_Variable_Database_Tables
You can just use case statements:
SELECT
id,
MAX( CASE WHEN name = 'flavor' THEN value ELSE NULL END ) AS flavor,
MAX( CASE WHEN name = 'type' THEN value ELSE NULL END ) AS type
FROM prefix_site_tmplvars
LEFT JOIN prefix_site_tmplvar_contentvalues
ON prefix_site_tmplvar_contentvalues.tmplvarid = prefix_site_tmplvars.id
LEFT JOIN prefix_site_content
ON prefix_site_tmplvar_contentvalues.contentid = prefix_site_content.id
WHERE prefix_site_tmplvar_contentvalues.value = "chocolate"
GROUP BY id
Of course, this approach only works if you know ahead of time what keys you want to select; but it seems like in this case you do know (flavor + type).
Problem is, you might have
{ "id": 2, "flavor": "chocolate", "type": "cookie" }
and another row with
{ "id": 3, "flavor": "vanilla", "calories": "375" }
...I don't think there's an easy way to solve the problem in MySQL; you'd need to decide what keys to look for.
On the other hand you can collapse the rows in PHP:
while($tuple = ...fetch tuple from mysql cursor...)
{
list ($id, $name, $value) = $tuple;
if (!isset($objs[$id]))
{
// You might want to add also "'id' => $id" to the array definition
$objs[$id]= array ($name => $value);
}
else
{
$obj = $objs[$id];
$obj[$name] = $value;
$objs[$id] = $obj;
}
}
Now $objs contains the desired data; you still need to get it back into Modx, though.
You can do this with a group by:
SELECT id,
max(case when name = 'flavor' then value end) as flavor,
max(case when name = 'type' then value end) as type
FROM prefix_site_tmplvars LEFT JOIN
prefix_site_tmplvar_contentvalues
ON prefix_site_tmplvar_contentvalues.tmplvarid = prefix_site_tmplvars.id LEFT JOIN
prefix_site_content ON prefix_site_tmplvar_contentvalues.contentid = prefix_site_content.id
WHERE prefix_site_tmplvar_contentvalues.value = "chocolate"
group by id

MySQL query to pull only the latest record

I am creating a simple chat module where there are two levels.
Inbox
Conversation
in the Inbox i want to list the conversation between me and other users and display the latest chat message from that particular user.
messages(id, user_id, receiver_id, message, timestamp)
Dummy data
1, 1, 2, "hi there", today
2, 1, 4, "Hey Tommy", yesterday
3, 1, 2, "Wanna meet tonite?", now
Now when i display the list i want to just display two messages
one with user id - 4 and user id 2. But the latest message of userid 2 should be displayed only.
How to do a query where i can get only the latest row from a particular user ?
SELECT *
FROM (SELECT *
FROM `messages`
WHERE `user_id` = 1
ORDER BY `timestamp` DESC) TT
GROUP BY TT.`receiver_id`;
Hope the above helps!
This is beleive is an efficient way to acheive this:
SELECT M1.* FROM Messages M1 LEFT JOIN Messages M2
ON M1.ReceiverID = M2.ReceiverID AND M1.Timestamp < M2.Timestamp
WHERE M2.ID IS NULL
I would try it like this:
$user_id = 2;
$latestmessage = $this->Message->find('first', array(
'conditions' => array(
'user_id' => $user_id
),
'order' => 'id DESC'
));
This might work:
$latest_per_user_id = $this->Message->find('all', array(
'fields'=>'DISTINCT user_id',
'order' => 'timestamp DESC'
));
EDIT: If you just need info for one user you can do this:
$latest_msg_by_user = $this->Message->find('first', array(
'conditions'=>array('user_id'=>$user_id),
'order' => 'timestamp DESC'
));
You could put this in a loop to get it for each user.