Mysql JSON column any index ( [*] ) not working - mysql

I have a JSON column named prices_and_tags in which there are values in JSON array:
[{'price' => 100, 'tag' => 'Watch'}, {'price' => 200, 'tag' => 'Book'}]
If I want to search prices_tags where tag = 'Watch' in any array index, it's not working.
Query is like this:
select * from products where prices_and_tags->"$[*].tag" = 'Watch'
this gives 0 results.
But this query works for first or second array index, like this:
select * from products where prices_and_tags->"$[0].tag" = 'Watch'
I referred to this: https://dev.mysql.com/doc/refman/5.7/en/json-path-syntax.html for query syntax.
Thanks.

Have you tried this
select * from products where prices_and_tags->"$.tag[*]" = 'Watch'
Ref: https://dev.mysql.com/doc/refman/5.7/en/json.html

Related

SQL query needed for a complex structure

I have a tricky SQL query that needs to be built to get the highest priority rule based on customer session and geo IP data.
I attached the following tables: rule, rule_attribute, rule_attribute_value.
rule - table where all rules are stored
Click here to see a screenshot of the 'rule' table
rule_attribute - table where all rule attributes are stored
Click here to see a screenshot of the 'rule_attribute' table
rule_attribute_value - table where all rule attribute values are stored
Click here to see a screenshot of the 'rule_attribute_value' table
When the customer logs in, I have access to all those attributes (customer_id, customer_group_id, country_id, subdivision_one_id, subdivision_two_id). Only customer_id and customer_group_id will always have values. The others are optional, but there is a dependency between them. We can't have subdivisions without selecting first a country. We can have a second subdivision without selecting a country and then the first subdivision.
What I would like to get is the highest priority rule that matches the session data in the most optimized way. I have a solution that involves some coding, but I want to see if it's possible directly through SQL.
Here are some examples of session data arrays:
Array
(
[customer_id] => 2
[customer_group_id] => 1
[current_store_id] => 0
[country_id] => 15
[subdivision_one_id] => 224
[subdivision_two_id] =>
)
Array
(
[customer_id] => 2
[customer_group_id] => 1
[current_store_id] => 0
[country_id] => 15
[subdivision_one_id] =>
[subdivision_two_id] =>
)
Array
(
[customer_id] => 3
[customer_group_id] => 2
[current_store_id] => 0
[country_id] =>
[subdivision_one_id] =>
[subdivision_two_id] =>
)
Without a better understanding of the rules and data this is the best I can come up with. It is based on your first array example -
SELECT `r`.*
FROM `rule_attribute_value` `rav`
INNER JOIN `rule` `r`
ON `rav`.`rule_id` = `r`.`rule_id`
INNER JOIN `rule_attribute` `ra`
ON `rav`.`attribute_id` = `ra`.`attribute_id`
WHERE
(`rav`.`store_id` = 0 AND `ra`.`attribute_code` = 'customer' AND `rav`.`value` = 2) OR
(`rav`.`store_id` = 0 AND `ra`.`attribute_code` = 'customer_group' AND `rav`.`value` = 1) OR
(`rav`.`store_id` = 0 AND `ra`.`attribute_code` = 'country' AND `rav`.`value` = 15) OR
(`rav`.`store_id` = 0 AND `ra`.`attribute_code` = 'subdivision_one' AND `rav`.`value` = 224)
GROUP BY `r`.`rule_id`
HAVING COUNT(DISTINCT `rav`.`attribute_id`) = 4 /* 4 IS THE NUMBER OF ATTRIBUTES BEING QUERIED */
ORDER BY `r`.`position` ASC
LIMIT 1;

Yii2 Update where the condition is an Array

This is the update code
$clients = OpClient::find(['id'])->where(['status'=>'Active'])->all();
foreach($clients as $client)
{
$array[] = $client['unit_id'];
$unit = OpUnit::find()->where(['id'=>$array]);
file_put_contents('test.txt',print_r($client['unit_id'],true));
$connection = Yii::$app->db;
$connection->createCommand()->update('op_unit', ['selected' => 'Yes'], 'id='.$array.'')->execute();
}
How should I type in the update query where the id is an array? It keep showing error Array to string conversion. Any advice will be apprecieated. Thanks
should be this way ..
$connection->createCommand()->update('user',
['selected' => 'Yes'],['id' => $array])->execute();
try the real sql code using
$myRawSql= $connection->createCommand()->update('user',
['selected' => 'Yes'],['id' => $array])>getRawSql();
var_dump($myRawSql);
For searching you can use the IN condition. i.e
->andWhere(['in', 'id', [1, 2, 3]])
// Query will be: WHERE id IN (1, 2, 3)
http://www.yiiframework.com/doc-2.0/guide-db-query-builder.html
in: operand 1 should be a column or DB expression. Operand 2 can be
either an array or a Query object. It will generate an IN condition.
If Operand 2 is an array, it will represent the range of the values
that the column or DB expression should be; If Operand 2 is a Query
object, a sub-query will be generated and used as the range of the
column or DB expression. For example, ['in', 'id', [1, 2, 3]] will
generate id IN (1, 2, 3). The method will properly quote the column
name and escape values in the range. The in operator also supports
composite columns. In this case, operand 1 should be an array of the
columns, while operand 2 should be an array of arrays or a Query
object representing the range of the columns.
So basically you need to pass your array to IN for search.
For update you can use same Where syntax in updateAll command i.e
// UPDATE customer SET status = 1 WHERE id IN (1, 2, 3)
http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#updating-multiple-rows
Customer::updateAll(['status' => Customer::STATUS_ACTIVE], ['in', 'id', [1, 2, 3]]);
Hope this helps. Thanks.
You can use updateAll query :
$update = OpUnit::updateAll(['selected' => 'Yes'],['id' => $array]);
It returns number of rows updated.
Refer : http://www.yiiframework.com/doc-2.0/yii-db-activerecord.html#updateAll()-detail

SQL query takes very long

I have a table, named Ads, which contains ~441.000 rows, and 21 columns.
I am trying to run the following query:
SELECT Ads.* FROM Ads WHERE Ads.countries_CountryId = 'FR'
I put an index, to the countries_CountryId field, which is of type char(2), but when I run the above query, it takes around 5-8 seconds to complete. This ammount of time looks immensly high for me, for such a medium sized table, and such a simple SQL query. Where should I look for the problem? Or is it normal for such a query to take so long?
I also tried to EXPLAIN the above query, and get the following results, but I don't know how to decipher this:(
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE Ads ref countries_CountryId countries_CountryId 2 const 24368 Using where
EDIT1: (response to Seth McClaine)
I've tried your suggestion, the way you suggested, returns an error, but if I run SELECT count(*) FROM Ads WHERE Ads.countries_CountryId = 'FR', it runs much much faster: 0.0052490234375
But the problem is, I am not printing out anything, I am using php to run the queries, and calculating their runtime for the moment, and at the end, outputting the queries run, and the time they took:
Array
(
[0] => Array
(
[query] => SET character_set_results = 'utf8', character_set_client = 'utf8', character_set_connection = 'utf8', character_set_database = 'utf8', character_set_server = 'utf8'
[duration] => 0.00481009483337
)
[1] => Array
(
[query] => SELECT * FROM Countries WHERE NameFormatted LIKE '%FRANCE%'
[duration] => 0.00234889984131
)
[2] => Array
(
[query] => SELECT Ads.* FROM Ads WHERE Ads.countries_CountryId = 'FR'
[duration] => 4.71820402145
)
[3] => Array
(
[query] => SELECT COUNT(*) FROM Ads WHERE Ads.countries_CountryId = 'FR'
[duration] => 0.0052490234375
)
)
Here's also the code snippet, that runs the queries:
public function query($query, $cacheit=true) {
if (!$this->isConnected()) {
$this->throwError("Querying <b>".$query."</b> failed, because MySQL is not connected!", self::MYSQL_NOT_CONNECTED, false);
return false;
}
$qstart=microtime(true);
$result=#mysql_query($query, $this->conn_resource);
$qduration=microtime(true) - $qstart;
if ($result===FALSE) $this->throwError("Querying <b>".$query."</b> failed!", self::MYSQL_QUERY_ERR);
$this->numQueries++;
$this->executedQueries[] = array(
'query' => $query,
'duration' => $qduration
);
return $result;
}
Have your tried executing the query in read-only mode?
As explained here in mysql documentation.
You may try starting your query with
START TRANSACTION READ ONLY
Also, you may want to try using explain statement to get more information about your query.
EXPLAIN SELECT Ads.* FROM ....
Hope this helps. Helped for me in several large queries.

How to use the table columns instead of variables in QueryExpression::addCase()

In CakePHPs new ORM, you can use the QueryBuilder to build (in theory) any query.
I want to select the value of one of two columns, depending on another value. In a regular query, that can be done as follows:
SELECT IF(from_id = 1, to_id, from_id) AS other_id FROM messages;
I am trying to archive the same query using the QueryBuilder and QueryExpression::addCase()
$messagesQuery = $this->Messages->find('all');
$messagesQuery->select([
'other_id' => $messagesQuery->newExpr()->addCase(
$messagesQuery->newExpr()->add(['from_id' => $this->authUser->id]),
['to_id', 'from_id'],
['integer', 'integer']
)
]);
This does not work, as the passed values are not integers, but rather table columns containing integers.
Through trial and error (using the method add() again), I got the following:
$messagesQuery = $this->Messages->find('all');
$messagesQuery->select([
'other_id' => $messagesQuery->newExpr()->addCase(
$messagesQuery->newExpr()->add(['from_id' => $this->authUser->id]),
[
$messagesQuery->newExpr()->add(['to_id']),
$messagesQuery->newExpr()->add(['from_id'])
],
['integer', 'integer']
)
]);
This results in the following query:
SELECT (CASE WHEN from_id = 1 THEN to_id END) AS `other_id` FROM messages Messages
Now, the ELSE part is missing, although the CakePHP book states:
Any time there are fewer case conditions than values, addCase will automatically produce an if .. then .. else statement.
The examples in the CakePHP book are not very helpful in this case, as they only use static integers or strings as values, for example:
#SELECT SUM(CASE published = 'Y' THEN 1 ELSE 0) AS number_published, SUM(CASE published = 'N' THEN 1 ELSE 0) AS number_unpublished FROM articles GROUP BY published
$query = $articles->find();
$publishedCase = $query->newExpr()->addCase($query->newExpr()->add(['published' => 'Y']), 1, 'integer');
$notPublishedCase = $query->newExpr()->addCase($query->newExpr()->add(['published' => 'N']), 1, 'integer');
$query->select([
'number_published' => $query->func()->sum($publishedCase),
'number_unpublished' => $query->func()->sum($unpublishedCase)
])
->group('published');
Is there a way to get the method addCase to use the two table columns as values instead of just static values?
As it turns out, I was just one logical step short of the solution in my previous edit.
As the CakePHP book correctly states:
Any time there are fewer case conditions than values, addCase will automatically produce an if .. then .. else statement.
For that to work though, both the conditions and values have to be an array, even if there is only one condition. (This the CakePHP book does not state.)
This code:
$messagesQuery = $this->Messages->find('all');
$messagesQuery->select([
'other_id' => $messagesQuery->newExpr()->addCase(
[
$messagesQuery->newExpr()->add(['from_id' => $this->authUser->id])
],
[
$messagesQuery->newExpr()->add(['to_id']),
$messagesQuery->newExpr()->add(['from_id'])
],
['integer', 'integer']
)
]);
results in this query:
SELECT (CASE WHEN from_id = 1 THEN to_id ELSE from_id END) AS `other_id` FROM messages Messages
Eureka

Sub Query at join not coming in right way

Hiii, Currently I am stuck with a problem. I am finding no way to remove quotes generated by the zend on query generation of a sub query which is placed in a join operation.
The $selectInnerQuery creates the sub query which is used in the join operation this is called as inneranswer table. This is used to join with answer table. $select is used for the final query. Please help me on this...
$selectInnerQuery= $sql->select()->from(array('answer' => 'tblanswer'))->columns(array('aid' => new Expression('answer.aid'),'count' => new Expression('count(answer.qid)')));
$innerstatement = $sql->getSqlStringForSqlObject($selectInnerQuery);
$select = $sql->select()->from(array('answer' => 'tblanswer'))->columns($fieldsToSelect);
$select->join(array('inneranswer' => $innerstatement), 'inneranswer.aid = answer.aid', array(),'inner');
The query that I am getting from zend is
SELECT `answer`.* FROM `tblanswer` AS `answer`
inner join `SELECT answer.aid AS ``aid``, count(answer.qid) AS ``count`` FROM ``tblanswer`` AS ``answer`` WHERE answer.qid !=0 GROUP BY answer.qid, answer.baid` AS `inneranswer` ON `inneranswer`.`aid` = `answer`.`aid`
I have tried new Expression but it does not work in join operation.
When you are specifying your $innerstatement on joining, specify as like "{$innerstatement}", this may solve your quotes problem and also check your inner statement query returns sql query or it returns as object.
Came across the same issue today. Join table array should contain Select object instead of select query.
$selectObj = $sql->select()->from(array('answer' => 'tblanswer'))->columns(array('aid' => new Expression('answer.aid'),'count' => new Expression('count(answer.qid)')));
$select = $sql->select()->from(array('answer' => 'tblanswer'))->columns($fieldsToSelect);
$select->join(array('inneranswer' => $selectObj), 'inneranswer.aid = answer.aid', array(),'inner');