MariaDB's Qcache_hits and Com_select increase together - mariadb

MariaDB's QCache_hits and Com_select increase together.
For example.
MySQL
show global status - Com_select is 0. Qcache_hits is 0.
1st select : select * from test_table where id = 1 - Com_select is 1. Qcache_hits is 0.
2nd select : select * from test_table where id = 1 - Com_select is 1. Qcache_hits is 1.
3rd select : select * from test_table where id = 1 - Com_select is 1. Qcache_hits is 2.
MariaDB
show global status - Com_select is 0. Qcache_hits is 0.
1st select : select * from test_table where id = 1 - Com_select is 1. Qcache_hits is 0.
2nd select : select * from test_table where id = 1 - Com_select is 2. Qcache_hits is 1.
3rd select : select * from test_table where id = 1 - Com_select is 3. Qcache_hits is 2.
Why even when the number of Com_select increases if the cache is hit?
My environment is Ubunut 12.04(x64) and MariaDB 5.5.35.
MariaDB [test]> show global status where Variable_name in ('Com_select', 'Qcache_hits');
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_select | 79 |
| Qcache_hits | 6 |
+---------------+-------+
2 rows in set (0.00 sec)
MariaDB [test]> insert into testtable values (11, 3);
Query OK, 1 row affected (0.00 sec)<br/>
MariaDB [test]> select * from testtable where id = 11;
+----+------+
| id | name |
+----+------+
| 11 | 3 |
+----+------+
1 row in set (0.00 sec)
MariaDB [test]> show global status where Variable_name in ('Com_select', 'Qcache_hits') ;
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_select | 80 |
| Qcache_hits | 6 |
+---------------+-------+
2 rows in set (0.00 sec)
MariaDB [test]> select * from testtable where id = 11;
+----+------+
| id | name |
+----+------+
| 11 | 3 |
+----+------+
1 row in set (0.00 sec)
MariaDB [test]> select * from testtable where id = 11;
+----+------+
| id | name |
+----+------+
| 11 | 3 |
+----+------+
1 row in set (0.00 sec)
MariaDB [test]> show global status where Variable_name in ('Com_select', 'Qcache_hits') ;
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_select | 82 |
| Qcache_hits | 8 |
+---------------+-------+
2 rows in set (0.00 sec)
MariaDB [test]>

I can verify this with MariaDB 5.5.39 and 10.0.14. I'm not sure whether this was an intentional change (IMHO the MariaDB behavior is more correct), but at least according to this section in the MariaDB knowledge base it is supposed to behave the same way as MySQL here:
Note that queries returned from the query cache do not increment the
Com_select status variable, so to find the total number of valid
queries run on the server, add Com_select to Qcache_hits.
( https://mariadb.com/kb/en/mariadb/documentation/optimization-and-tuning/buffers-caches-and-threads/query-cache/ )
So I've filed this bug report about it:
https://mariadb.atlassian.net/browse/MDEV-7216

Related

Conditional unique constraint in mariadb

I have this table in mariadb
create or replace table test_table (
col_key int primary key,
col_a int not null,
col_b int not null check(col_b in (0, 1))
);
There is an additional constraint that the pair (col_a, col_b) must be unique only if col_b = 1
For example, you are allowed to have
| col_key | col_a | col_b |
| ------- | ----- | ----- |
| 1 | 1 | 0 |
| 2 | 1 | 0 |
But not
| col_key | col_a | col_b |
| ------- | ----- | ----- |
| 1 | 1 | 1 |
| 2 | 1 | 1 |
I think of 2 approaches:
Since col_b only has 2 values, I can take advantage of the fact that in mariadb null can by pass unique check. So instead of 0 and 1, I change the definition of the table to this and treat null as 0 in application code.
create or replace table test_table (
col_key int primary key,
col_a int not null,
col_b int check(col_b is null or col_b = 1),
unique (col_a, col_b)
);
The upside is I have the database handle the check for me. The downside is the application code become a bit complex and the code tightly couple to mariadb's implementation.
When update or insert, write query like this
update test_table t
set t.col_b = 1
where t.col_key = 2
and not exists (select 1 from test_table t2 where t2.col_a = 1 and t2.col_b = 1)
The upside is the application code actually works with 0 and 1 values. The downside is... I unsure if this work or not. Does the database lock the table when it run the update query? Is there any chance that some other process inserts a row with col_b = 1 after the subquery returns the result?
According to your question, the combination of cola and colb is unique, if colb has the value 1. This condiition can be already defined in the table definition:
CREATE TABLE test_table (
col_key int primary key,
col_a int not null,
col_b int check(col_b is null or col_b = 1),
unique (col_a, col_b)
)
Since the unique index allows multiple NULL values, multiple same combinations of col_a and col_b are allowed, as long the value of col_b is NULL.
For updating you don't need a subquery, since the server checks this condition itself. For not raising an error, just use UPDATE IGNORE
UPDATE IGNORE test_table SET col_b=1 WHERE col_key=2
With the first solution the logic is defined in the database (and doesn't need to be defined in your application) and it is much faster since it can use an index, while the subquery performs a table scan.
MariaDB [test]> insert into test_table select seq,1,NULL from seq_1_to_1000;
Query OK, 1000 rows affected (0,081 sec)
Records: 1000 Duplicates: 0 Warnings: 0
MariaDB [test]> explain update test_table set col_b=1 where col_key=18 and not exists (select 1 from test_table t2 where t2.col_a=1 and t2.col_b=1);
+------+-------------+------------+-------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+------------+-------+---------------+---------+---------+-------+------+-------------+
| 1 | PRIMARY | test_table | const | PRIMARY | PRIMARY | 4 | const | 1 | |
| 2 | SUBQUERY | t2 | ALL | NULL | NULL | NULL | NULL | 1000 | Using where |
+------+-------------+------------+-------+---------------+---------+---------+-------+------+-------------+
2 rows in set (0,003 sec)
MariaDB [test]> alter table test_table add unique (col_a, col_b);
Query OK, 0 rows affected (0,079 sec)
Records: 0 Duplicates: 0 Warnings: 0
MariaDB [test]> explain update test_table set col_b=1 where col_key=18;
+------+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+------------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | test_table | range | PRIMARY | PRIMARY | 4 | NULL | 1 | Using where |
+------+-------------+------------+-------+---------------+---------+---------+------+------+-------------+

Storing attributes with multiple values in relational database

I am storing product attributes in a relational table in a MariaDB database the following way:
I have a main table, called Products which provide the name, description, and other simple information about a product, and another table, ProductAttributes, with the following structure: Id|ProductId|Attribute|Value where Id is an autoincremented primary key, and ProductId is a reference to a row in the Products table.
I can store simple attribute value relations to a product in this way, say ie, height, weight, length of a product. My problems start, when a product's attribute, ie color can have multiple possible values.
I could add multiple lines to the ProductAttributes table when storing multi-valued attributes, ie:
1|yy|color|red
2|yy|color|blue
and from this schema, I could easily retrieve a single product's attributes, but I am having trouble on how to go forward when trying to compare two products based on their attributes.
Is there any other way to store multiple values for a single attribute in a relational database to maintain their searchability?
As of now, to find similar attributed products I am doing a similar query:
SELECT * FROM ProductAttributes base
INNER JOIN ProductAttributes compare ON compare.ProductId != base.ProductId
WHERE base.Attribute = compare.Attribute
AND base.Value = compare.Value
AND base.ProductId = 'x'
GROUP BY compare.ProductId
My problem is, that this query will return the products with a red and blue color, as similar to products with a blue color.
Btw, I can not change my attributes tables to a one attribute per column representation, because I do not know from the get-go how many attributes will I have, and even if I knew, I have way too many possible attributes and differences on each product category, to represent this in a traditional table.
A possible pitfall is, that I also want to compare products to one another with missing attributes. Ie, if a product has a length attribute specified, but another one has no length attribute, they could still be similar. Right now, to make this kind of comparison, in the background, I am transposing my attributes table, to a simple table, and on that table, perform this query:
SELECT b.ProductId as BaseProduct, s.ProductId as SimProduct
FROM tmp_transposed_product_attributes b
CROSS JOIN tmp_transposed_product_attributes s ON b.ProductId != s.ProductId
WHERE (b.attribute1 = s.attribute1 OR b.attribute1 IS NULL OR s.attribute1 IS NULL)
AND (b.attribute2 = s.attribute2 OR b.attribute2 IS NULL OR s.attribute2 IS NULL) ...
If I'm following correctly for the product comparison, I like to use EXISTS or NOT EXISTS to help find things like that, which may also help avoid having to transpose the data.
For example, given this sample table data:
MariaDB [test]> select * from productattributes;
+----+-----------+-----------+-------+
| id | productID | attribute | value |
+----+-----------+-----------+-------+
| 1 | yy | height | 5 |
| 2 | yy | color | red |
| 3 | yy | weight | 10 |
| 4 | yy | length | 6 |
| 5 | yy | color | blue |
| 6 | zz | color | white |
| 7 | zz | height | 5 |
| 8 | zz | length | 8 |
+----+-----------+-----------+-------+
8 rows in set (0.00 sec)
To find all similar attributes between the two, but has different values (removes attribute/values pairs that are the same) use a NOT EXISTS query to same table like so:
MariaDB [test]> SELECT * FROM `productattributes` pA
-> WHERE productID IN ('yy', 'zz')
-> AND NOT EXISTS (SELECT * FROM productattributes pB
-> WHERE pA.attribute = pB.attribute
-> AND pA.value = pB.value
-> AND pA.productID != pB.productID)
-> ORDER BY productID, attribute;
+----+-----------+-----------+-------+
| id | productID | attribute | value |
+----+-----------+-----------+-------+
| 2 | yy | color | red |
| 5 | yy | color | blue |
| 4 | yy | length | 6 |
| 3 | yy | weight | 10 |
| 6 | zz | color | white |
| 8 | zz | length | 8 |
+----+-----------+-----------+-------+
6 rows in set (0.00 sec)
Then to find attribute/value pairs that ARE the same between the two, simply remove the NOT portion of the query:
MariaDB [test]> SELECT * FROM `productattributes` pA
-> WHERE productID IN ('yy', 'zz')
-> AND EXISTS (SELECT * FROM productattributes pB
-> WHERE pA.attribute = pB.attribute
-> AND pA.value = pB.value
-> AND pA.productID != pB.productID)
-> ORDER BY productID, attribute;
+----+-----------+-----------+-------+
| id | productID | attribute | value |
+----+-----------+-----------+-------+
| 1 | yy | height | 5 |
| 7 | zz | height | 5 |
+----+-----------+-----------+-------+
2 rows in set (0.00 sec)
Here's the query without the command line junk:
SELECT * FROM `productattributes` pA
WHERE productID IN ('yy', 'zz')
AND NOT EXISTS (SELECT * FROM productattributes pB
WHERE pA.attribute = pB.attribute
AND pA.value = pB.value
AND pA.productID != pB.productID)
ORDER BY productID, attribute;
EDIT:
To cover the case where there is an attribute that is in one but not the other, then the value check of the query can be removed:
MariaDB [test]> SELECT * FROM `productattributes` pA
-> WHERE productID IN ('yy', 'zz')
-> AND NOT EXISTS (SELECT * FROM productattributes pB
-> WHERE pA.attribute = pB.attribute
-> AND pA.productID != pB.productID)
-> ORDER BY productID, attribute;
+----+-----------+-----------+-------+
| id | productID | attribute | value |
+----+-----------+-----------+-------+
| 3 | yy | weight | 10 |
+----+-----------+-----------+-------+
1 row in set (0.00 sec)

MariaDB : Trigger keeps getting syntax error after insert

I have two tables. Table vitamin contains column vitamin_name and price. Table vitamin_variety contains columns price_variety and price_variety_extreme. I want to create an AFTER INSERT Trigger, that will insert the price of vitamin if the value of vitamin_name contains extreme into price_variety_extreme and not contains extreme into price_variety .
Here's what I mean if I insert this into vitamin, the price will be input into vitamin_variety
table = vitamin
+----------------+-------+
| vitamin_name | price |
+----------------+-------+
| extreme pill 1 | 10 |
| pill 1 | 5 |
| extreme pill 2 | 20 |
| pill 2 | 10 |
+----------------+-------+
table = vitamin_variety
+---------------+-----------------------+
| price_variety | price_variety_extreme |
+---------------+-----------------------+
| 5 | 10 |
| 10 | 20 |
+---------------+-----------------------+
my query keeps getting syntax error. it keeps saying
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use
near '' at line 5
what's wrong?
create trigger vitamin_ain after insert on vitamin
for each ROW
if vitamin.vitamin_name like 'extreme%' then
update vitamin_variety
set price_variety_extreme = new.price_variety_extreme;
else if vitamin.vitamin_name not like 'extreme%' then
update vitamin_variety
set price_variety = new.price_variety;
end if;
There are some syntax issues, and other situations, but the conditions inside the trigger could look like the code below:
First, (not right or wrong) the else if could be simplified to an else. If the first condition finds extreme, there is no need to inverse check for not like extreme. (unless some strange requirement expects that handling)
Of course, feel free to keep the else if check as-is, if it makes more sense to you, but I used this code:
if new.vitamin_name like 'extreme%' then
update vitamin_variety
set price_variety_extreme = new.price;
else
update vitamin_variety
set price_variety = new.price;
end if
The 'syntax issues':
In the if condition, change vitamin.vitamin_name to new.vitamin. Columns from trigger insert actions are referenced from new, not the table name.
If keeping your original else if, need to change the same vitamin.vitamin_name reference mentioned in item 1.
References new.price_variety_extreme and new.price_variety do not exist from the vitamin insert (these columns are not in vitamin), but new.price is, and the other vitamin columns.
The 'other situations':
Without any rows already present in the vitamin_variety table, no update will occur, because there are not any rows to update. This situation will make it seem as if the trigger is not working, but there is nothing for the trigger to update.
The desired output to vitamin_variety cannot be achieved with the current info. The update will set ALL columns of one or the other to the last inserted price, because there is no unique row reference. (no WHERE clause to limit what row(s) get updated)
Here's some sample interaction:
As the first other situation mentioned, there are no rows in the vitamin_variety table, so I added one:
MariaDB [test]> insert into vitamin_variety (price_variety, price_variety_extreme) values (0, 0);
Query OK, 1 row affected (0.08 sec)
MariaDB [test]> select * from vitamin_variety;
+----+---------------+-----------------------+
| id | price_variety | price_variety_extreme |
+----+---------------+-----------------------+
| 1 | 0 | 0 |
+----+---------------+-----------------------+
1 row in set (0.00 sec)
Then, with the trigger added to vitamin, inserted a couple of rows:
MariaDB [test]> insert into vitamin( vitamin_name, price ) values ('pilly 5', 7);
Query OK, 1 row affected (0.10 sec)
MariaDB [test]> insert into vitamin( vitamin_name, price ) values ('extreme pill 5', 3);
Query OK, 1 row affected (0.12 sec)
Show those inserts:
MariaDB [test]> select * from vitamin;
+----+----------------+-------+
| id | vitamin_name | price |
+----+----------------+-------+
| 8 | pilly 5 | 7 |
| 9 | extreme pill 5 | 3 |
+----+----------------+-------+
2 rows in set (0.00 sec)
Show the triggered price updates to the vitamin_variety table:
MariaDB [test]> select * from vitamin_variety;
+----+---------------+-----------------------+
| id | price_variety | price_variety_extreme |
+----+---------------+-----------------------+
| 1 | 7 | 3 |
+----+---------------+-----------------------+
1 row in set (0.00 sec)
=====
EDIT:
One possible way to achieve your goal, given this vitamin_variety table structure:
CREATE TABLE `vitamin_variety` (
`pill_name` varchar(25) NOT NULL,
`price_variety` int(11) NOT NULL DEFAULT '0',
`price_variety_extreme` int(11) NOT NULL DEFAULT '0',
UNIQUE KEY `pill_name` (`pill_name`)
);
Then changing the vitamin table trigger conditions to:
if new.vitamin_name like 'extreme%' then
insert into vitamin_variety (pill_name, price_variety_extreme)
values (replace(new.vitamin_name, 'extreme ', ''), new.price)
on duplicate key update price_variety_extreme = new.price;
else
insert into vitamin_variety (pill_name, price_variety)
values (new.vitamin_name, new.price)
on duplicate key update price_variety = new.price;
end if
Then running insert SQL based on your original vitamin table details:
MariaDB [test]> insert into vitamin (vitamin_name, price) values ('extreme pill 1', 10);
Query OK, 1 row affected (0.09 sec)
MariaDB [test]> insert into vitamin (vitamin_name, price) values ('pill 1', 5);
Query OK, 1 row affected (0.16 sec)
MariaDB [test]> insert into vitamin (vitamin_name, price) values ('extreme pill 2', 20);
Query OK, 1 row affected (0.10 sec)
MariaDB [test]> insert into vitamin (vitamin_name, price) values ('pill 2', 10);
Query OK, 1 row affected (0.10 sec)
Gives:
MariaDB [test]> select * from vitamin_variety;
+-----------+---------------+-----------------------+
| pill_name | price_variety | price_variety_extreme |
+-----------+---------------+-----------------------+
| pill 1 | 5 | 10 |
| pill 2 | 10 | 20 |
+-----------+---------------+-----------------------+
2 rows in set (0.00 sec)

How to create functional index on mariadb?

I'm trying to create functional index on mariadb 5.5 like below...
MariaDB [testdb]> create table foo(c1 datetime);
Query OK, 0 rows affected (0.43 sec)
MariaDB [testdb]> alter table foo add c2 varchar(10) AS (date_format(c1, '%Y-%m-%d'));
Query OK, 0 rows affected (0.22 sec)
Records: 0 Duplicates: 0 Warnings: 0
MariaDB [testdb]> desc foo;
+-------+-------------+------+-----+---------+---------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+---------+
| c1 | datetime | YES | | NULL | |
| c2 | varchar(10) | YES | | NULL | VIRTUAL |
+-------+-------------+------+-----+---------+---------+
2 rows in set (0.03 sec)
MariaDB [testdb]> create index idx_foo on foo(c1, c2);
ERROR 1904 (HY000): Key/Index cannot be defined on a non-stored computed column
MariaDB [testdb]>
Is there any how to create compositional column based index with VIRUAL column?
Any advice would be appreciated.
Thanks in advance.
See documentation:
Generated (Virtual and Persistent/Stored) Columns
...
MariaDB until 10.2.2
In MariaDB 10.2.2 and before, the following statements apply to
indexes for generated columns:
Defining indexes on VIRTUAL generated columns is not supported.
...
...

MariaDB ALTER TABLE DROP COLUMN infinite

I use below command to drop a column activetime from my table. While this command never end. I waited for 5 mins while there is only 100 records in MY_TABLENAME
ALTER TABLE MY_TABLENAME DROP COLUMN activetime;
The column activetime is defined as below:
| activetime | bigint(20) | NO | | NULL | |

Resources