How can i improve GROUP BY statement performance? - mysql

I have a big query that i need to optimize.
select game_id,
count(user_id),
count(distinct user_id),
provider_id,
aggregator,
country,
sum(sum_bets),
sum(sum_wins),
currency,
any_value(date)
from (select transactions.game_id as game_id,
transactions.user_id as user_id,
games.provider_id as provider_id,
games.aggregator as aggregator,
users.country as country,
transactions.currency as currency,
IF(transactions.type_id IN (2, 16), transactions.amount, 0) as sum_bets,
IF(transactions.type_id IN (3, 18, 20), transactions.amount, 0) as sum_wins,
transactions.date_create as date
from `transactions`
inner join `games` on transactions.game_id = games.id
inner join `users` on transactions.user_id = users.id
where transactions.status_id = 1
and transactions.type_id in (2, 3, 16, 18, 20)
and transactions.game_id is not null
and users.country is not null
union all
select transactions_bonus.game_id as game_id,
transactions_bonus.user_id as user_id,
games.provider_id as provider_id,
games.aggregator as aggregator,
users.country as country,
transactions_bonus.currency as currency,
IF(transactions_bonus.type IN (5, 6), transactions_bonus.amount_bonus, 0) as sum_bets,
IF(transactions_bonus.type IN (4, 7, 10), transactions_bonus.amount_bonus, 0) as sum_wins,
transactions_bonus.date as date
from `transactions_bonus`
inner join `games` on transactions_bonus.game_id = games.id
inner join `users` on transactions_bonus.user_id = users.id
where transactions_bonus.status in (2, 3, 4, 5, 6)
and transactions_bonus.type in (4, 5, 6, 7, 10)
and transactions_bonus.game_id is not null
and users.country is not null) as popular_games
group by game_id, provider_id, aggregator, country, currency
Table transactions
-- auto-generated definition
create table transactions
(
id bigint unsigned auto_increment
primary key,
string_id varchar(36) null,
user_id bigint unsigned not null,
game_id smallint unsigned null,
bonus_id smallint unsigned null,
jackpot_id bigint unsigned null,
prize_id bigint unsigned null,
tournament_id bigint unsigned null,
amount int not null,
commission int null,
aggregator_amount int unsigned default '0' not null,
aggregator_currency varchar(16) collate utf8_bin null,
aggregator_fee int unsigned default '0' not null,
currency varchar(3) not null,
status_id tinyint unsigned not null,
balance int null,
gateway_method varchar(64) null,
account varchar(128) collate utf8_bin null comment 'Аккаунт в платежной системе (номер карты, телефон, кошелек)',
ip varchar(39) null,
comment varchar(128) null,
date_create datetime default CURRENT_TIMESTAMP not null,
date_complete datetime null,
date_cancel datetime null,
type_id tinyint unsigned not null,
manager_id int null,
event_id int null,
constraint transactions_ibfk_1
foreign key (user_id) references users (id)
on update cascade,
constraint transactions_ibfk_2
foreign key (game_id) references games (id)
on update cascade,
constraint transactions_ibfk_3
foreign key (jackpot_id) references jackpots (id)
on update cascade,
constraint transactions_ibfk_4
foreign key (tournament_id) references tournaments (id)
on update cascade,
constraint transactions_prize_id_foreign
foreign key (prize_id) references prizes (id)
on update cascade
)
engine = InnoDB;
create index date_create
on transactions (date_create);
create index game_id
on transactions (game_id);
create index jackpot_id
on transactions (jackpot_id);
create index string_id
on transactions (string_id);
create index tournament_id
on transactions (tournament_id);
create index type_id
on transactions (type_id);
create index user_id
on transactions (user_id);
Table transactions_bonus
-- auto-generated definition
create table transactions_bonus
(
id bigint unsigned auto_increment
primary key,
account tinyint unsigned default '1' not null comment 'Номер счета. 1 - бонусный счет казино, 2 - бонусный счет спорта',
type tinyint unsigned not null,
user_id bigint unsigned not null,
admin_id bigint unsigned null,
transaction_id bigint unsigned null,
bonus_id bigint unsigned null,
tournament_id bigint unsigned null,
prize_id bigint unsigned null,
game_id smallint unsigned null,
amount_bonus int not null,
amount_deposit int null,
currency varchar(3) not null,
wager float not null,
max_bet int unsigned null,
max_transfer int unsigned default '0' not null comment 'Максимальная сумма перевода с бонусного счета на основной',
balance int not null,
status tinyint unsigned not null,
comment text null,
date timestamp default CURRENT_TIMESTAMP not null,
date_cancel timestamp null,
date_wagering_complete timestamp null,
constraint transactions_bonus_ibfk_1
foreign key (admin_id) references users (id)
on update cascade,
constraint transactions_bonus_ibfk_3
foreign key (tournament_id) references tournaments (id)
on update cascade,
constraint transactions_bonus_ibfk_4
foreign key (user_id) references users (id)
on update cascade,
constraint transactions_bonus_ibfk_5
foreign key (game_id) references games (id)
on update cascade,
constraint transactions_bonus_ibfk_6
foreign key (prize_id) references prizes (id)
on update cascade,
constraint transactions_bonus_ibfk_7
foreign key (bonus_id) references bonuses (id)
on update cascade,
constraint transactions_bonus_ibfk_8
foreign key (transaction_id) references transactions (id)
on update cascade
)
engine = InnoDB;
create index account
on transactions_bonus (account);
create index admin_id
on transactions_bonus (admin_id);
create index bonus_id
on transactions_bonus (bonus_id);
create index game_id
on transactions_bonus (game_id);
create index status
on transactions_bonus (status);
create index tournament_id
on transactions_bonus (tournament_id);
create index transaction_id_2
on transactions_bonus (transaction_id);
create index type
on transactions_bonus (type);
create index user_id
on transactions_bonus (user_id);
Table games
-- auto-generated definition
create table games
(
id smallint unsigned auto_increment
primary key,
string_id varchar(96) collate utf8_bin not null,
provider_id tinyint unsigned not null,
aggregator tinyint unsigned not null,
name varchar(255) not null,
game_type tinyint unsigned not null,
source tinyint unsigned not null,
technology tinyint unsigned not null,
device_type tinyint unsigned not null,
status tinyint unsigned default '0' not null,
block_kyc tinyint unsigned default '0' not null comment 'Блокировка для не верифицированных пользователей',
block_jackpots tinyint unsigned default '0' not null comment 'Не участвует в джекпотах',
is_blocked_tournaments tinyint unsigned default '0' not null,
is_wagering tinyint default 0 null,
is_bonuses tinyint unsigned default '0' null,
is_jackpots tinyint unsigned default '0' not null,
freespins tinyint unsigned default '0' not null comment 'Участвует в фриспинах',
has_demo tinyint(1) default 0 not null,
multiplier decimal(8, 2) unsigned default 0.00 not null,
image varchar(128) null,
sorting int unsigned default '0' not null,
created_at timestamp default CURRENT_TIMESTAMP null,
updated_at timestamp null,
blocked smallint unsigned default '0' null,
constraint string_id
unique (string_id, aggregator),
constraint games_ibfk_1
foreign key (provider_id) references games_providers (id)
)
engine = InnoDB;
create index provider_id
on games (provider_id);
Table users
-- auto-generated definition
create table users
(
id bigint unsigned auto_increment
primary key,
name varchar(255) charset utf8 null,
surname varchar(255) null,
email varchar(255) null,
phone varchar(12) null,
password varchar(255) not null,
user_group tinyint(1) not null,
status tinyint(1) default 0 not null,
network varchar(32) null,
network_uid varchar(32) null,
login varchar(255) not null,
ip_registration varchar(39) not null,
ip_auth varchar(39) null,
country varchar(3) charset utf8 null,
country_already_changed tinyint unsigned default '0' not null comment 'Страну можно изменить только один раз',
birthday_already_changed tinyint unsigned default '0' not null comment 'Дату рождения можно установить один раз',
city varchar(255) null,
postcode varchar(15) null,
date_birth date null,
date_registration timestamp default CURRENT_TIMESTAMP not null,
date_auth timestamp null,
date_last_activity timestamp null,
comment varchar(265) null,
affiliate_id varchar(32) charset utf8 null,
affiliate_user_id varchar(255) null,
affiliate_company_id varchar(32) null,
affiliate_payload varchar(192) charset utf8 null,
affiliate_link_type tinyint unsigned default '0' not null comment 'Тип партнерской ссылки, по которой перешел юзер',
favorite_bets tinyint unsigned default '0' not null comment 'Любимые ставки игрока',
timezone varchar(64) charset utf8 null,
region varchar(255) null,
address varchar(265) null,
kyc_status tinyint unsigned default '1' not null,
role_id tinyint null,
sms_autentification tinyint unsigned default '0' not null,
remember_token varchar(100) null,
created_at timestamp default CURRENT_TIMESTAMP not null,
updated_at timestamp null,
kyc_expire date null,
sex varchar(1) charset utf8 default 'm' null,
login_attempt int default 0 not null,
language varchar(2) charset utf8 default 'en' not null,
session_uuid varchar(36) null,
session_token varchar(64) null,
avatar varchar(265) null,
referral_user_id bigint unsigned null,
level tinyint unsigned default '0' not null,
points int unsigned default '0' not null,
shop_points int unsigned default '0' not null,
last_points_used timestamp null,
max_withdrawal_amount int unsigned null,
promocode_error_attempts int unsigned default '0' null,
promocode_block_to datetime null,
bonus_blocked tinyint unsigned default '0' not null comment 'Заблокирован для участия в бонусах',
bonus_blocked_date timestamp null,
blocked tinyint unsigned default '0' not null comment 'Блокировка Sumsub',
service_status varchar(32) null comment 'Статус сервиса SumSub',
constraint login
unique (login),
constraint network
unique (network, network_uid),
constraint users_email_unique
unique (email),
constraint users_ibfk_1
foreign key (referral_user_id) references users (id)
on update cascade
)
engine = InnoDB
collate = utf8mb4_unicode_ci;
create index date_last_activity
on users (date_last_activity);
create index referral_user_id
on users (referral_user_id);
These two merged tables have combined 30 million rows, without GROUP BY statement, it executes within a 1.5-2 seconds. With GROUP BY, i can wait up to 5 minutes until it executes.
Obviously, i can execute script without GROUP BY, and then format and aggregate it in my code.
But so many records will breach the limit of my RAM, if i will save it in array or JSON.

Be aware that count(user_id) counts the number of rows that have user_id IS NOT NULL. It seems like it won't be NULL, so you may as well do COUNT(*).
In older versions ANY_VALUE(date) does not work but MIN(date) works.
There are lot of IN(...) lists; this makes it virtually impossible to fashion any useful INDEXes. Here are some that could help:
transactions: INDEX(status_id, type_id)
If it weren't for COUNT(DISTINCT user_id), I would recommend doing the SUMs in each subquery, then adding the subtotals.
What is the value of innodb_buffer_pool_size? How much RAM? How big are the tables (in GB)? If things can't be cached adequately, then I/O is the problem and I will suggest changing BIGINTs (8 bytes each) to more civilized datatypes.
"two merged tables have combined 30 million rows" -- Is it 30M before or after filtering (WHERE)?
Please provide EXPLAIN SELECT ...

Related

Create two tables customer_data and order_data in SQL in a single execution via PHP

CREATE TABLE IF NOT EXISTS customers_data (
id int(20) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL,
INVOICE_ID varchar(50) NOT NULL,
_NAME varchar(255) NOT NULL,
MOBILE bigint(12) NOT NULL,
GSTIN varchar(20) NOT NULL,
_ADDRESS varchar(255) NOT NULL,
EMAIL varchar(255) NOT NULL,
_STATE varchar(50) NOT NULL,
MODE varchar(10) NOT NULL,
_DATE date NOT NULL DEFAULT current_timestamp(),
total_qty int(11) NOT NULL,
total_amount decimal(40,2) NOT NULL,
total_sc_gst decimal(30,2) NOT NULL,
Round_Off float(2,2) NOT NULL,
grand_total decimal(50,0) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
customer_data created sucessfully.
But there is an error in order_data regarding foreign key. Remember I want to create these two table in a same time.
CREATE TABLE IF NOT EXISTS order_data (
id int(20) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL,
cus_id int(20) NOT NULL,
ITEM varchar(255) NOT NULL,
HSN varchar(50) NOT NULL,
QTY int(15) NOT NULL,
RATE decimal(40,2) NOT NULL,
S_C_GST decimal(30,2) NOT NULL,
TOTAL decimal(50,2) NOT NULL,
_DATE timestamp NOT NULL DEFAULT current_timestamp(),
FOREIGN KEY(cus_id) REFERENCES customers_data(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
I got this error.
#1005 - Can't create table test. (errno: 150 "Foreign
key constraint is incorrectly formed")
I think you are using MySQL. You have not used Constraint with Foreign Key. Try the below query.
CREATE TABLE IF NOT EXISTS order_data (
id int(20) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL,
cus_id int(20) NOT NULL,
ITEM varchar(255) NOT NULL,
HSN varchar(50) NOT NULL,
QTY int(15) NOT NULL,
RATE decimal(40,2) NOT NULL,
S_C_GST decimal(30,2) NOT NULL,
TOTAL decimal(50,2) NOT NULL,
_DATE timestamp NOT NULL DEFAULT current_timestamp(),
**CONSTRAINT <FK_NAME>** FOREIGN KEY(cus_id) REFERENCES customers_data(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Making the table "at the same time" is not the problem you need to solve, (it's not possible anyway).
You can't make a foreign key if the data type is different from the data type of the referenced column.
order_data.cus_id is an INT column.
customers_data.id is an INT UNSIGNED column.
Change either one or the other data type, so they are the same.

Errors Adding Foreign Keys (MySQL) (Error Code 1215)

I am trying to add a foreign key between two different sets of tables, the first set is customer and shopping_cart. I tried looking at the other posts regarding this error but I couldn't get it to work after looking at them.
CREATE TABLE `usale`.`customer` (
CREATE TABLE `usale`.`customer` (
`customer_id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
`first_name` VARCHAR(45) NOT NULL,
`last_name` VARCHAR(45) NOT NULL,
`email` VARCHAR(50) NULL DEFAULT NULL,
`active` TINYINT(1) NOT NULL DEFAULT TRUE,
`create_date` DATETIME NOT NULL,
`last_update` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
`student_student_id` BIGINT NOT NULL,
`student_school_id` BIGINT NOT NULL,
`shopping_cart_cart_id` TINYINT UNSIGNED NOT NULL,
PRIMARY KEY (`customer_id`, `student_student_id`, `student_school_id`)),
INDEX `fk_customer_student1_idx` (`student_student_id` ASC,`student_school_id` ASC),
INDEX `fk_customer_shopping_cart1_idx` (`shopping_cart_cart_id` ASC),
CONSTRAINT `fk_customer_student1`
FOREIGN KEY (`student_student_id`)
REFERENCES `usale`.`student` (`student_id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_customer_shopping_cart1`
FOREIGN KEY (`shopping_cart_cart_id`)
REFERENCES `usale`.`shopping_cart` (`cart_id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;`customer_id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
`first_name` VARCHAR(45) NOT NULL,
`last_name` VARCHAR(45) NOT NULL,
`email` VARCHAR(50) NULL DEFAULT NULL,
`active` TINYINT(1) NOT NULL DEFAULT TRUE,
`create_date` DATETIME NOT NULL,
`last_update` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
`student_student_id` BIGINT NOT NULL,
`student_school_id` BIGINT NOT NULL,
`shopping_cart_cart_id` TINYINT UNSIGNED NOT NULL,
PRIMARY KEY (`customer_id`, `student_student_id`, `student_school_id`))
CREATE TABLE IF NOT EXISTS `frankapp`.`shopping_cart` (
`cart_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`last_update` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`cart_total` DECIMAL(12,2) NULL,
PRIMARY KEY (`cart_id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
ALTER TABLE customer ADD CONSTRAINT `fk_customer_shopping_cart1`
FOREIGN KEY (`shopping_cart_cart_id`)
REFERENCES `usale`.`shopping_cart`(`cart_id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION
I'm getting the error: "ERROR: Cannot add foreign key constraint
Error Code: 1215"
For the second set of tables they are named shopping_cart and shopping_cart_item
CREATE TABLE IF NOT EXISTS `usale`.`shopping_cart_item` (
`shopping_cart_item_id` BIGINT NOT NULL,
`shopping_cart_cart_id` TINYINT UNSIGNED NOT NULL,
`inventory_item_inventory_item_id` BIGINT UNSIGNED NOT NULL,
`inventory_item_student_id` BIGINT NOT NULL,
`inventory_item_inventory_type` VARCHAR(45) NOT NULL,
`inventory_item_student_student_id` BIGINT NOT NULL,
`inventory_item_student_customer_id` BIGINT NOT NULL,
`inventory_item_student_school_id` BIGINT NOT NULL,
`inventory_item_edition_edition_id` BIGINT UNSIGNED NOT NULL,
`inventory_item_edition_author_id` BIGINT NOT NULL,
PRIMARY KEY (`shopping_cart_item_id`, `shopping_cart_cart_id`, `inventory_item_inventory_item_id`, `inventory_item_student_id`, `inventory_item_inventory_type`, `inventory_item_student_student_id`, `inventory_item_student_customer_id`, `inventory_item_student_school_id`, `inventory_item_edition_edition_id`, `inventory_item_edition_author_id`),
INDEX `fk_shopping_cart_items_shopping_cart1_idx` (`shopping_cart_cart_id` ASC),
INDEX `fk_shopping_cart_item_inventory_item1_idx` (`inventory_item_inventory_item_id` ASC, `inventory_item_student_id` ASC, `inventory_item_inventory_type` ASC, `inventory_item_student_student_id` ASC, `inventory_item_student_customer_id` ASC, `inventory_item_student_school_id` ASC, `inventory_item_edition_edition_id` ASC, `inventory_item_edition_author_id` ASC))
CREATE TABLE IF NOT EXISTS `frankapp`.`shopping_cart` (
`cart_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`last_update` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`cart_total` DECIMAL(12,2) NULL,
PRIMARY KEY (`cart_id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
ALTER TABLE shopping_cart_item ADD CONSTRAINT `fk_shopping_cart_items_shopping_cart1`
FOREIGN KEY (`shopping_cart_cart_id`)
REFERENCES `usale`.`shopping_cart`(`cart_id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION
Once again I am getting error 1215: "ERROR: Cannot add foreign key constraint
Error Code: 1215"

No auto Increment when MySql Database converted to Postgres database

In Mysql, ID is Auto Increment but when converted to Postgres there is no Auto Increment .
Mysql database
CREATE TABLE IF NOT EXISTS `cities` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`state_id` bigint(20) NOT NULL,
`district_id` bigint(20) NOT NULL,
`name` varchar(255) NOT NULL,
`description` varchar(1000) DEFAULT NULL,
`created_by` int(11) NOT NULL,
`modified_by` int(11) DEFAULT NULL,
`is_active` char(1) NOT NULL DEFAULT 'Y',
`created` datetime DEFAULT NULL,
`modified` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `state_id` (`state_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=26 ;
After Converted to postgres database
CREATE TABLE cities (
"id" bigint NOT NULL,
state_id bigint NOT NULL,
district_id bigint NOT NULL,
"name" varchar(255) NOT NULL,
description varchar(1000),
created_by int NOT NULL,
modified_by int,
is_active char(1) NOT NULL,
created timestamp,
modified timestamp,
PRIMARY KEY ("id")
);
INSERT INTO cities(state_id, district_id, city_type, name, description,
created_by, modified_by, is_active, created, modified)
VALUES
(1, 1, '', 'Ramtek', null, 1, null, 'Y',
'2015-04-16 10:44:11', '2015-04-16 10:44:11');
You could use bigserial in such cases. It provides an auto increment feature in postgres:
CREATE TABLE cities (
"id" bigserial PRIMARY KEY,
Not Null constraint is provided by default.
Docs : http://www.postgresql.org/docs/current/static/datatype.html#DATATYPE-SERIAL

Error on creating FOREIGN KEY

CREATE TABLE IF NOT EXISTS `servergraph_server` (
`id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(64) NOT NULL,
`ip` VARCHAR(16) NOT NULL,
`port` SMALLINT UNSIGNED NOT NULL
)
CREATE TABLE IF NOT EXISTS `servergraph_data` (
`id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
`serverid` INT NOT NULL,
`time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
`fps` SMALLINT UNSIGNED NOT NULL,
`replay` TINYINT UNSIGNED NOT NULL,
`dropped_packets` INT UNSIGNED NOT NULL,
`online` TINYINT UNSIGNED NOT NULL,
`clients0` TINYINT UNSIGNED NOT NULL,
`clients1` TINYINT UNSIGNED NOT NULL,
`clients2` TINYINT UNSIGNED NOT NULL,
`clients3` TINYINT UNSIGNED NOT NULL,
FOREIGN KEY (`serverid`) REFERENCES `servergraph_server` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
)
This are the tables I'd like to create.
But I am always getting an error on the FOREIGN KEY line.
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FOREIG' at line 1
I am using this version of MySQL: 5.5.35-0+wheezy1
What is wrong?
Thanks, floube
SOLUTION:
ALTER TABLE `servergraph_data` ADD CONSTRAINT `fk_server` FOREIGN KEY (`serverid`) REFERENCES `servergraph_server` (`id`)
instead of the FOREIGN KEY line in CREATE TABLE servergraph_data
Place semi colons after the create table statements:
CREATE TABLE IF NOT EXISTS `servergraph_server` (
`id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(64) NOT NULL,
`ip` VARCHAR(16) NOT NULL,
`port` SMALLINT UNSIGNED NOT NULL
);
CREATE TABLE IF NOT EXISTS `servergraph_data` (
`id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
`serverid` INT NOT NULL,
`time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
`fps` SMALLINT UNSIGNED NOT NULL,
`replay` TINYINT UNSIGNED NOT NULL,
`dropped_packets` INT UNSIGNED NOT NULL,
`online` TINYINT UNSIGNED NOT NULL,
`clients0` TINYINT UNSIGNED NOT NULL,
`clients1` TINYINT UNSIGNED NOT NULL,
`clients2` TINYINT UNSIGNED NOT NULL,
`clients3` TINYINT UNSIGNED NOT NULL,
FOREIGN KEY (`serverid`) REFERENCES `servergraph_server` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
);

MySQL: Can this Join query be optimized?

I have two tables. create command for those tables are as follows
CREATE TABLE `invoice` (
`Id` bigint(20) NOT NULL,
`VersionNumber` int(11) NOT NULL,
`CreationDate` datetime default NULL,
`ModificationDate` datetime default NULL,
`CreateBy` bigint(20) default NULL,
`ModifyBy` bigint(20) default NULL,
`BusinessId` varchar(255) character set utf8 default NULL,
`Status` int(11) default NULL,
`VendorId` bigint(20) default NULL,
`CustomerId` bigint(20) default NULL,
`OrderDate` date default NULL,
`ExpectedDate` date default NULL,
`DeliveryDate` date default NULL,
`InvoiceFor` int(11) default NULL,
`Reference` varchar(255) character set utf8 default NULL,
`Agent` varchar(255) character set utf8 default NULL,
`BnAgent` varchar(255) character set utf8 default NULL,
`Note` varchar(255) character set utf8 default NULL,
`HasVoucher` char(1) character set utf8 default NULL,
PRIMARY KEY (`Id`),
KEY `CustomerId` (`CustomerId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `invoiceitem` (
`Id` bigint(20) NOT NULL,
`VersionNumber` int(11) NOT NULL,
`CreationDate` datetime default NULL,
`ModificationDate` datetime default NULL,
`CreateBy` bigint(20) default NULL,
`ModifyBy` bigint(20) default NULL,
`BusinessId` varchar(255) default NULL,
`Status` int(11) default NULL,
`InvoiceId` bigint(20) default NULL,
`ProductId` bigint(20) default NULL,
`PackageQty` decimal(19,5) default NULL,
`PackagePrice` decimal(19,5) default NULL,
`ItemPerPackage` decimal(19,5) default NULL,
`ItemQty` decimal(19,5) default NULL,
`ItemPrice` decimal(19,5) default NULL,
`StoreQty` decimal(19,5) default NULL,
`RevenuePercent` decimal(19,5) default NULL,
`PurchasePrice` decimal(19,5) default NULL,
`Vat` decimal(19,5) default NULL,
`TotalAmount` decimal(19,5) default NULL,
`InvoiceItemFor` int(11) default NULL,
`LifeTimeUptoDate` datetime default NULL,
PRIMARY KEY (`Id`),
KEY `invoiceid_productid` (`InvoiceId`,`ProductId`),
KEY `ProductId` (`ProductId`),
CONSTRAINT `FK_invoiceitem` FOREIGN KEY (`InvoiceId`) REFERENCES `invoice` (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Purpose of this query
I have to show the list of invoices to the admin. He need to see the invoice date, Invoice number, customer Id, Product id and if the customer take that product previously then that previous date.
Now the Query
SELECT iv.id, iv.CustomerId, ii.ProductId, MAX(iv2.orderdate) AS PriviousDate
FROM invoice AS iv
INNER JOIN invoice AS iv2
ON iv.CustomerId=iv2.CustomerId
AND iv.OrderDate>iv2.OrderDate
INNER JOIN invoiceItem AS ii
ON iv.id=ii.invoiceId
INNER JOIN invoiceItem AS ii2
ON iv2.id=ii2.invoiceId
AND ii.ProductId=ii2.ProductId
WHERE iv.Status=0
AND ii.Status=0
AND iv2.Status=0
AND ii2.Status=0
GROUP BY ii.ProductId, iv.CustomerId
Here iv.id and ii.id is primary key
customerId and Productid fields are also indexes
and invoiceId is the foreginkey (iv.id=ii.invoiceId )
I am running this query in my MySql server (local)
But Most of the time I got timeout. How can I optimized this query?
the Explain for this query is as follows:
now I apply
create index invoiceid_productid
on invoiceItem(invoiceId, productId)
after that the explain result is
Not much here (it would have been helpful to see what you're indexes are) but assuming the invoice ids increase in line with invoice date....I suspect this might be slightly faster:
SELECT iv.id, iv.CustomerId, ii.ProductId, MAX(iv2.orderdate) AS PriviousDate
FROM invoice AS iv
INNER JOIN invoice AS iv2
ON iv.CustomerId=iv2.CustomerId
AND iv.id>iv2.id
INNER JOIN invoiceItem AS ii
ON iv.id=ii.invoiceId
INNER JOIN invoiceItem AS ii2
ON iv2.id=ii2.invoiceId
AND ii.ProductId=ii2.ProductId
AND ii.invoiceId>ii2.invoiceId
WHERE iv.Status=0
AND ii.Status=0
AND iv2.Status=0
AND ii2.Status=0
GROUP BY ii.ProductId, iv.CustomerId
But you really need an index on ProductId, Status and InvoiceId in the invoiceItem table before it's going to be appreciable faster.
Looking at your EXPLAIN results, the problem is clearly the invoiceItem table. It has 200,000 rows, and MySQL isn't using any indexes to access it.
So you should look at creating indexes to speed access to it.
I mocked up your tables:
create table invoice (
id int auto_increment not null,
customerid int not null,
orderdate timestamp not null,
status int default 0 not null,
primary key(id)
);
create table invoiceItem (
id int auto_increment not null,
invoiceId int not null,
productId int not null,
status int default 0 not null,
primary key (id)
);
I created these indexes:
create index invoiceId on invoiceItem(invoiceId);
create index cod on invoice(customerid, orderdate);
create index stat on invoice(status);
My EXPLAIN (on empty tables) now has all tables using type ref.
You can also see this tutorial which is for MySQL ;-)