mysql query optimization - join and orderby - sql

I need a little help by one mysql query optimization. It is a simple query but anything is not right and I can't found it :-(
I have 2 tables: products (> 40000 Rows) and product_tags (> 5 mil)
There is a relation betweet the tables 1 -> N . Every prdoduct can have many tags in the table product tags.
I have this simple Query:
EXPLAIN SELECT t.product_id, kwt.tag_id
FROM products AS t, product_tags AS kwt
WHERE 1
AND t.product_id = kwt.product_id
AND kwt.tag_id =11
ORDER BY t.order_date
wchich returns 55 results.
First Situation: if I have this table structure of the tables:
CREATE TABLE IF NOT EXISTS `products` (
`product_id` int(10) unsigned NOT NULL auto_increment,
`product_source_id` smallint(5) unsigned NOT NULL,
`order_date` int(10) unsigned NOT NULL,
PRIMARY KEY (`product_id`),
KEY `order_date` (`order_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
CREATE TABLE IF NOT EXISTS `product_tags` (
`product_tag_id` int(10) unsigned NOT NULL auto_increment,
`tag_id` int(10) unsigned NOT NULL,
`product_id` int(11) NOT NULL,
PRIMARY KEY (`product_tag_id`),
KEY `product_id` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
Then the Explain of the query is this:
+----+-------------+-------+-------+---------------+------------+---------+---------------------------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------------+---------+---------------------------+-------+-------------+
| 1 | SIMPLE | t | index | PRIMARY | order_date | 4 | NULL | 45392 | Using index |
| 1 | SIMPLE | kwt | ref | product_id | product_id | 4 | t.product_id | 3 | Using where |
+----+-------------+-------+-------+---------------+------------+---------+---------------------------+-------+-------------+
It is getting all the rows from table products, but there is nothing with temporary table.
Second Situation: If I add an index for the field "tag_id" in product_tags, then the picture is different:
+----+-------------+-------+--------+-------------------+---------+---------+-----------------------------+------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-------------------+---------+---------+-----------------------------+------+---------------------------------+
| 1 | SIMPLE | kwt | ref | product_id,tag_id | tag_id | 4 | const | 55 | Using temporary; Using filesort |
| 1 | SIMPLE | t | eq_ref | PRIMARY | PRIMARY | 4 | kwt.product_id | 1 | Using where |
+----+-------------+-------+--------+-------------------+---------+---------+-----------------------------+------+---------------------------------+
Now it selects only 55 rows, what is right, but the query is havy :(
Where is my mistake here ?
Thanks
Nik

this is what i would do:
Read these resources
http://dev.mysql.com/doc/refman/5.0/en/innodb-index-types.html
http://www.xaprb.com/blog/2006/07/04/how-to-exploit-mysql-index-optimizations/
Redesign your system to take advantage of a clustered primary key
Simplified schema:
drop table if exists products;
create table products
(
prod_id int unsigned not null auto_increment primary key,
name varchar(255) not null unique
)
engine = innodb;
drop table if exists tags;
create table tags
(
tag_id mediumint unsigned not null auto_increment primary key,
name varchar(255) not null unique
)
engine = innodb;
drop table if exists product_tags;
create table product_tags
(
tag_id mediumint unsigned not null,
prod_id int unsigned not null,
created_date date not null,
primary key (tag_id, prod_id), -- note the clustered composite index and the order !!
key (prod_id)
)
engine = innodb;
Test the schema
select
pt.tag_id,
pt.prod_id
from
product_tags pt
inner join products p on pt.prod_id = p.prod_id
where
pt.tag_id = 11
order by
pt.created_date
limit 10;
I may even change the product_tags PK to primary key (tag_id, prod_id, created_date) but it all depends on the typical queries you run. You could ofc, just create a non clustered secondary index on created date if you think that's gonna boost performance.
Hope this helps :)

Related

MariaDB foreign key auto generated index not created for the first column of PK

i'm facing a question without answer. I can't understand why the auto generated index from the FK creation is not working when the column seems to be the first one of PK, what i mean :
Create a simple schema with :
CREATE TABLE cat (name VARCHAR(255) PRIMARY KEY);
CREATE TABLE dog (name VARCHAR(255) PRIMARY KEY);
CREATE TABLE cat_dog_couple
(
cat_name VARCHAR(255),
dog_name VARCHAR(255),
PRIMARY KEY (cat_name, dog_name),
CONSTRAINT fk__cat_dog_couple__cat_name FOREIGN KEY (cat_name) references cat(name),
CONSTRAINT fk__cat_dog_couple__dog_name FOREIGN KEY (dog_name) references dog(name)
);
These indexes will be generated :
+----------------+------------+------------------------------+--------------+-------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name |
+----------------+------------+------------------------------+--------------+-------------+
| cat_dog_couple | 0 | PRIMARY | 1 | cat_name |
| cat_dog_couple | 0 | PRIMARY | 2 | dog_name |
| cat_dog_couple | 1 | fk__cat_dog_couple__dog_name | 1 | dog_name |
+----------------+------------+------------------------------+--------------+-------------+
Screen show index
I don't really understand why the index fk__cat_dog_couple__cat_name is not created?
Is it a bug ? A technical limitation ? A technical choice ?
Tested on MariaDB 10.4.x and 10.5.x.

How to scan join query results into a struct containing a slice of structs using sqlx

Here are the tables that are relevant to this question:
lists:
Table "public.lists"
Column | Type | Collation | Nullable | Default
-------------+--------+-----------+----------+-------------------
id | uuid | | not null | gen_random_uuid()
user_id | uuid | | not null |
list_name | text | | not null |
description | text | | not null |
created_at | bigint | | not null |
updated_at | bigint | | not null |
Indexes:
"lists_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"fk_user_id" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
Referenced by:
TABLE "todos" CONSTRAINT "fk_list_id" FOREIGN KEY (list_id) REFERENCES lists(id) ON DELETE SET NULL
and todos:
Table "public.todos"
Column | Type | Collation | Nullable | Default
------------+---------+-----------+----------+-------------------
id | uuid | | not null | gen_random_uuid()
list_id | uuid | | not null |
user_id | uuid | | not null |
content | text | | not null |
done | boolean | | not null | false
created_at | bigint | | not null |
updated_at | bigint | | not null |
Indexes:
"todos_pkey" PRIMARY KEY, btree (id)
"todos_content_key" UNIQUE CONSTRAINT, btree (content)
Foreign-key constraints:
"fk_list_id" FOREIGN KEY (list_id) REFERENCES lists(id) ON DELETE SET NULL
"fk_user_id" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
and these are the structs that I would ideally like to use:
type Todo struct {
ID string `db:"id"`
ListID string `db:"list_id"`
UserID string `db:"user_id"`
Content string `db:"content"`
Done bool `db:"done"`
CreatedAt int64 `db:"created_at"`
UpdatedAt int64 `db:"updated_at"`
}
type ListWithTodos struct {
ID string `db:"id"`
UserID string `db:"user_id"`
ListName string `db:"list_name"`
Description string `db:"description"`
Todos []Todo
CreatedAt int64 `db:"created_at"`
UpdatedAt int64 `db:"updated_at"`
}
What I would like to do is select a list and add all of its children todos (todos with a list_id equal to the list's id) to the field ListWithTodos.Todos. I currently have this query which returns all of the todos with the parent list attached (and I know this won't work with the struct I have):
SELECT l.*, t.* FROM lists l RIGHT JOIN todos t ON l.id=t.list_id WHERE l.id='insert uuid here';
and my go code and structs obviously don't work with the query.
What query would I use to get the results into a single struct and what sqlx functions would I use to do this?

How to set a compound primary key using a varchar field and an int auto increment field?

Using mariadb, I want to create a table like this:
CREATE TABLE `products` (
`producttype` varchar(15) NOT NULL,
`code` int(6) NOT NULL auto_increment,
`description` varchar(200) NOT NULL,
`price` double default 0.0,
`stock` int default 0,
PRIMARY KEY(`producttype`, `code`)
So the id of a product, for example a TV, could be: TV001.
This gives the next error: Incorrect table definition; there can be only one auto column and it must be defined as a key.
If you really want the id of a product to be formed by the type plus a auto incremental integer, how would you do this?
Thank you
A slight change to your definition: (the added key portion, to satisfy the error's ...one auto column and it must be defined as a key message)
CREATE TABLE `products` (
`producttype` varchar(15) NOT NULL,
`code` int(6) NOT NULL auto_increment,
`description` varchar(200) NOT NULL,
`price` double default 0.0,
`stock` int default 0,
key (`code`),
PRIMARY KEY(`producttype`, `code`));
...worked for me:
MariaDB [test]> CREATE TABLE `products` (
-> `producttype` varchar(15) NOT NULL,
-> `code` int(6) NOT NULL auto_increment,
-> `description` varchar(200) NOT NULL,
-> `price` double default 0.0,
-> `stock` int default 0,
-> key (`code`),
-> PRIMARY KEY(`producttype`, `code`));
Query OK, 0 rows affected (0.38 sec)
...then checking the table afterwards:
MariaDB [test]> desc products;
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| producttype | varchar(15) | NO | PRI | NULL | |
| code | int(6) | NO | PRI | NULL | auto_increment |
| description | varchar(200) | NO | | NULL | |
| price | double | YES | | 0 | |
| stock | int(11) | YES | | 0 | |
+-------------+--------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

Postgres Object-relation database doesn't seem to function properly

I've defined my tables so:
create table device (
id serial primary key,
manufacturerid integer references manufacturer(id) on delete restrict,
model text,
price real,
usagepros text,
usagecons text
);
create table robot (
numaxes integer,
capacity real,
reach real,
accuracy real,
installmethodid integer references installmethod(id) on delete restrict,
mass real
) inherits (device);
create table robotComplex(
id serial primary key,
name text
);
create table robotComplexDevice(
id serial primary key,
deviceId integer references device(id) on delete restrict,
robotcomplexid integer references robotcomplex(id) on delete cascade
);
etc...
I get the following when running sql commands:
id | manufacturerid | model | price | usagepros | usagecons | numaxes | capacity | reach | accuracy | installmethodid | mass
-----+----------------+-------+-------+-----------+-----------+---------+----------+-------+----------+-----------------+-------
159 | 117 | Robot | 100.3 | OK | NoOK | 6 | 15.3 | 15.4 | 76.1234 | 45 | 100.1
> select * from device;
id | manufacturerid | model | price | usagepros | usagecons
-----+----------------+-------+-------+-----------+-----------
159 | 117 | Robot | 100.3 | OK | NoOK
> select * from robotcomplex;
id | name
----+--------------
27 | Complex
> insert into robotcomplexdevice (deviceid, robotcomplexid) values (159, 27);
ERROR: insert or update on table "robotcomplexdevice" violates foreign key constraint "robotcomplexdevice_deviceid_fkey"
DETAIL: Key (deviceid)=(159) is not present in table "device".
For some reason, even though I've defined the "robot" table to inherit "deivice" table, I can't reference it. Maybe I don't get object-relational database model correctly. But if you can't reference the tables so then what is the point of object-relational model?
This is documented behaviour:
http://www.postgresql.org/docs/current/static/ddl-inherit.html#DDL-INHERIT-CAVEATS

How to change a primary key in SQL to auto_increment?

I have a table in MySQL that has a primary key:
mysql> desc gifts;
+---------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+-------+
| giftID | int(11) | NO | PRI | NULL | |
| name | varchar(80) | YES | | NULL | |
| filename | varchar(80) | YES | | NULL | |
| effectiveTime | datetime | YES | | NULL | |
+---------------+-------------+------+-----+---------+-------+
but I wanted to make it auto_increment.
The following statement failed. How can it be modified so that it can work? thanks
mysql> alter table gifts modify giftID int primary key auto_increment;
ERROR 1068 (42000): Multiple primary key defined
Leave off the primary key attribute:
ALTER TABLE gifts MODIFY giftID INT AUTO_INCREMENT;
Certain column attributes, such as PRIMARY KEY, aren't exactly properties of the column so much as shortcuts for other things. A column marked PRIMARY KEY, for example, is placed in the PRIMARY index. Futhermore, all columns in the PRIMARY index are given the NOT NULL attribute. (Aside: to have a multi-column primary key, you must use a separate constraint clause rather than multiple PRIMARY KEY column attributes.) Since the column is already in the PRIMARY index, you don't need to specify it again when you modify the column. Try SHOW CREATE TABLE gifts; to see the affects of using the PRIMARY KEY attribute.