inner join slow performance - php

I have a table offers that has over 100k rows so the below query is very slow (4sec - average).
SELECT cat1.id AS cat1id,
cat1.title_gr AS title,
cat1.order
FROM categories_groups_cat1 AS cat1
INNER JOIN
( SELECT categories_id, categories_groups_cat1_id FROM
categories_vs_groups
GROUP BY categories_groups_cat1_id ) AS vs
ON vs.categories_groups_cat1_id=cat1.id
INNER JOIN
( SELECT id, title_gr FROM
categories
GROUP BY title_gr ) AS cats
ON cats.id=vs.categories_id
INNER JOIN
( SELECT category_gr FROM
offers
GROUP BY category_gr ) AS offers
ON offers.category_gr=cats.title_gr
GROUP BY cat1.id
ORDER BY cat1.order ASC
table offers
`id` int(11) NOT NULL,
`title` text NOT NULL,
`description` text NOT NULL,
`image` text NOT NULL,
`price` float NOT NULL,
`start_price` float NOT NULL,
`brand` text NOT NULL
`category_gr` text NOT NULL
table categories_groups_cat1
`id` int(11) NOT NULL,
`order` int(11) NOT NULL,
`title_gr` text NOT NULL
table categories_vs_groups
`id` int(11) NOT NULL,
`categories_groups_cat1_id` int(11) NOT NULL,
`categories_id` int(11) NOT NULL
table categories
`id` int(11) NOT NULL,
`title_gr` char(255) NOT NULL
I try to choose from categories_groups_cat1 where offers exist, that's why I use the inner join. I don't know if it is completely correct. If there is another faster(performance) solution I would appreciate it

You should avoid sub-query that creates temp table. This will surely improve performance. Sub-queries that create temp table in memory kills performance, try to avoid as much as you can.
I have modified your code. There may small syntactic errors.
SELECT cat1.id AS cat1id,
cat1.title_gr AS title,
cat1.order
FROM categories_groups_cat1 AS cat1
INNER JOIN
categories_groups_cat1_id AS vs
ON vs.categories_groups_cat1_id=cat1.id
INNER JOIN
categories
AS cats
ON cats.id=vs.categories_id
INNER JOIN
offers
ON offers.category_gr=cats.title_gr
GROUP BY cat1.id,cats.title_gr, offers.category_gr
ORDER BY cat1.order ASC

Related

MYSQL Join to get data in specific format

I've recently changed updated my database with a couple of new tables and having trouble to get(select) data from three different tables.
There are 4 stores which exchange stock with each other, it gets recorded in database.
Table 'sites' has store id and name info.
CREATE TABLE `sites` (
`id` int(10) NOT NULL,
`name` varchar(35) NOT NULL
)
Table 'stock_exchange_new' has info about stock transfer date, from
store, to store etc.
CREATE TABLE `stock_exchange_new` (
`id` int(11) NOT NULL,
`transfer_date` date NOT NULL,
`from_site` int(11) NOT NULL,
`to_site` int(11) NOT NULL,
`transfer_ref` varchar(255) NOT NULL,
`note` varchar(300) NOT NULL,
`added_by` int(11) NOT NULL,
`added_at` datetime NOT NULL,
`edited_by` int(11) NOT NULL,
`edited_at` datetime NOT NULL
)
Table 'stock_item_txns' has the information about what item was
exchanged/transferred:
CREATE TABLE `stock_item_txns` (
`id` int(11) NOT NULL,
`stock_exchange_id` int(11) NOT NULL,
`item_id` int(11) NOT NULL,
`units_per_ctn` int(11) NOT NULL,
`qty` decimal(10,2) NOT NULL,
`ctn_price` decimal(10,2) NOT NULL,
`total_price` decimal(10,2) NOT NULL
)
Now, for one particular store, I need the data shown as compare to itself, eg: for Store 1, it should be something like this:
Store Name Total_Sent Total_Received
Store2 500 200
Store3 490 580
Store4 300 400
Tried so far...
SELECT GREATEST(s1.name, s2.name) AS from_store,
LEAST(s1.name, s2.name) AS to_store,
SUM(CASE WHEN s1.name < s2.name THEN si.total_price ELSE 0 END) AS received,
SUM(CASE WHEN s1.name > s2.name THEN si.total_price ELSE 0 END) AS sent
FROM stock_exchange_new se
INNER JOIN sites s1
ON se.from_site = s1.id
INNER JOIN sites s2
ON se.to_site = s2.id
INNER JOIN stock_item_txns si
ON se.id = si.stock_exchange_id
GROUP BY GREATEST(se.from_site, se.to_site),
LEAST(se.from_site, se.to_site)
HAVING MAX(GREATEST(se.from_site, se.to_site)) = '1'
Here's the fiddle, for better understanding.
Utilising a couple of sub queries, something like this (not tested):-
SELECT s2.name AS 'Store Name',
from_site_total,
to_site_total
FROM sites s1
CROSS JOIN sites s2
LEFT OUTER JOIN
(
SELECT from_site,
to_site,
SUM(stock_item_txns.total_price) AS from_site_total
FROM stock_exchange_new
INNER JOIN stock_item_txns ON stock_exchange_new.id = stock_item_txns.stock_exchange_id
GROUP BY from_site,
to_site
) sub_from_site
ON s1.id = sub_from_site.from_site
AND s2.id = sub_from_site.to_site
LEFT OUTER JOIN
(
SELECT to_site,
from_site,
SUM(stock_item_txns.total_price) AS to_site_total
FROM stock_exchange_new
INNER JOIN stock_item_txns ON stock_exchange_new.id = stock_item_txns.stock_exchange_id
GROUP BY to_site,
from_site
) sub_to_site
ON s1.id = sub_to_site.to_site
AND s2.id = sub_to_site.from_site
WHERE s1.name = 'Store1'
AND s2.name != 'Store1'

Display best selling items and associated image from another table

I would like to display the image of 4 best selling items on the index page but the sql I'm using does not work. I think the syntax is correct but doesn't show anything on the page. I have an order_items table and products table.
Products table
CREATE TABLE IF NOT EXISTS `products` (
`id` int(11) NOT NULL,
`date` datetime NOT NULL,
`category` int(11) NOT NULL,
`name` varchar(100) NOT NULL,
`image` varchar(100) CHARACTER SET utf8 NOT NULL,
`description` text NOT NULL,
`author` varchar(100) NOT NULL,
`price` decimal(5,2) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=27 ;
Order_items table
CREATE TABLE IF NOT EXISTS `order_items` (
`id` int(11) NOT NULL,
`order` int(11) NOT NULL,
`product` int(11) NOT NULL,
`price` decimal(8,2) NOT NULL,
`qty` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=68 ;
NOTE: in order_items table the field product corresponds to the ID of the product.
Finally, this is the sql statement I'm using but doesn't show anything.
public function bestSellingItems(){
$sql = "SELECT * FROM products AS p
INNER JOIN order_items AS od ON p.id = od.id
GROUP BY p.id, SELECT SUM(od.qty) AS total
ORDER BY SUM(od.qty) DESC limit 4";
}
I would appreciate any help.
You said it your self, product correspond to id and you used p.id = od.id , also, I didn't understand the part of the second select which I believe should throw an error(no from..) so I adjusted it a little bit - select the sum, alias it and order by that alias.
public function bestSellingItems(){
$sql = "SELECT p.*,od.*,sum(od.qty) as sum_qty FROM products AS p
INNER JOIN order_items AS od ON p.id = od.product
GROUP BY p.id
ORDER BY sum_qty DESC limit 4";
}
your join "INNER JOIN order_items AS od ON p.id = od.id " should be INNER JOIN order_items AS od ON p.id = od.Product

how to join four table in msql?

create table area (
area_id int(11) not null auto_increment,
area_name varchar(60) not null,
primary key (area_id)
);
create table mainCategory(
mc_id int(11) not null auto_increment,
mc_name varchar(60) not null,
area_id int(11) not null,
primary key(mc_id),
foreign key(area_id) references area(area_id)
);
create table subCategory(
sc_id int(11) not null auto_increment,
sc_name varchar(60) not null,
mc_id int(11) not null,
area_id int(11) not null,
primary key(sc_id),
foreign key(mc_id) references mc(mc_id),
foreign key(area_id) references area(area_id)
);
create table shopes(
s_id int(11) not null auto_increment,
s_name varchar(60) not null,
s_address varchar(120) not null,
s_work varchar(255) not null,
s_imagepath varchar(255) not null,
s_image varchar(255) not null,
area_id int(11) not null,
mc_id int(11) not null,
sc_id int(11) not null,
primary key(s_id),
foreign key(area_id) references area(area_id),
foreign key(mc_id) references mc(mc_id),
foreign key(sc_id) references sc(sc_id)
);
I want to select data from four table with mysql join. I am using
select s_name,s_address,s_work,s_image,area_name,mc_name from shopes inner join area on area.area_id=shopes.area_id inner join mainCategory on mc.area_id=area.area_id;
It is a three table join and it is not giving appropriate result. It is giving repeated result.
Try this:
select s.s_name, s.s_address, s.s_work, s.s_image, a.area_name, mc.mc_name from shope as s LEFT JOIN area as a on a.area_id=s.area_id LEFT JOIN mainCategory as mc on mc.area_id=a.area_id LEFT JOIN subCategory as sc on sc.area_id=a.area_id
I have used LEFT JOIN keyword instead of inner join, it's returns all rows from the left table (table1), with the matching rows in the right table (table2). The result is NULL in the right side when there is no match.
Use this syntax.
select a.coumn1,
a.coumn2,
b.coumn1,
c.coulumn1,
d.column1,
d.column2
from table1 a
left outer join table2 b
on a.id=b.id
left outer join table3 c
on b.id=c.id
left outer join table4 d
on a.id=d.id
where a.name = 'abc'
select s_name, s_address, s_work, s_image, area_name, mc_name
from shopes, area, mainCategory
where shopes.area_id = area.area_id
and shopes.mc_id = mainCategory.mc_id;
If still got duplicate data, use:
select distinct s_name, ...
Try it.
select shopes.s_name,s_address,s_work,s_image, area.area_name, maincategory.mc_name from shopes join area on shopes.area_id=area.area_id
join maincategory on shopes.mc_id=maincategory.mc_id;
select s_name,s_address,s_work,s_image,area_name,mc_name
from area
inner join area
on area.area_id=shopes.area_id
inner join mainCategory
on mc.area_id=area.area_id;

Mysql product filters with multiple options

for example I have a list o TVs, and each TV has some attributes like: Brand(Samsung,Sony etc..), Size(80cm, 116 cm etc), SmartTv(yes, no).
I have the following schema:
CREATE TABLE `products` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(150) NOT NULL,
)
CREATE TABLE `attributes` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(20) character set latin1 NOT NULL
)
CREATE TABLE `attributes_entity` (
`product_id` int(11) NOT NULL,
`attribute_id` int(11) NOT NULL,
`value_id` int(11) NOT NULL,
)
CREATE TABLE `attributes_values` (
`id` int(11) NOT NULL auto_increment,
`value` varchar(255) default NULL,
)
If i want all TV from samsung i say like this:
SELECT
`p`.`id`,
`p`.`name`
FROM `attributes_entity` `ae`
INNER JOIN `products` `p` ON `ae`.`product_id`=`p`.`id`
INNER JOIN `attributes` `a` ON `ae`.`attribute_id`=`a`.`id`
INNER JOIN `attributes_values` `av` ON `ae`.`value_id`=`av`.`id`
WHERE (`a`.`name`='samsung' AND `av`.`value`='samsung')
This is great but what if I want: All Samsung TVs that are smartTv:
SELECT
`p`.`id`,
`p`.`name`
FROM `attributes_entity` `ae`
INNER JOIN `products` `p` ON `ae`.`product_id`=`p`.`id`
INNER JOIN `attributes` `a` ON `ae`.`attribute_id`=`a`.`id`
INNER JOIN `attributes_values` `av` ON `ae`.`value_id`=`av`.`id`
WHERE (`a`.`name`='samsung' AND `av`.`value`='samsung')
//imposible query
and (`a`.`name`='smartv' AND `av`.`value`='yes')
How should i fix the query with multiple ANDs?
First idea, off the top of my head - try replacing your joins with an inner query, and count the number of matching attributes:
SELECT `p`.`id`, `p`.`name`
FROM `products` `p`
WHERE `p`.`id` IN (SELECT `ae`.`product_id`
FROM `attributes_entity` `ae`
INNER JOIN `attributes` `a` ON `ae`.`attribute_id`=`a`.`id`
INNER JOIN `attributes_values` `av` ON `ae`.`value_id`=`av`.`id`
WHERE ((`a`.`name`='samsung' AND `av`.`value`='samsung') OR
(`a`.`name`='smartv' AND `av`.`value`='yes'))
HAVING COUNT(*) >= 2 -- number of matching attributes required
);

How do I select MySQL records based on results from a related table?

I have two tables in this scenario: members and team_members. The members table is pretty self explanatory. The team members table stores the member's team information if they are a member of the team. If there is no row in the team members table that has a member_id of a user, then they are not in a team. What I want to do is get all the users that are not members of a team. Should I use left join, inner join, outer join, or just join? What would this query look like?
CREATE TABLE IF NOT EXISTS `members` (
`member_id` int(15) NOT NULL AUTO_INCREMENT,
`group_id` int(15) NOT NULL,
`display_name` text NOT NULL,
`email_address` text NOT NULL,
`password` text NOT NULL,
`status` tinyint(1) NOT NULL,
`activation_code` varchar(16) NOT NULL,
`date_joined` text NOT NULL,
PRIMARY KEY (`member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `team_members` (
`team_member_id` int(15) NOT NULL AUTO_INCREMENT,
`member_id` int(15) NOT NULL,
`team_id` int(15) NOT NULL,
`date_joined` text NOT NULL,
`date_left` text NOT NULL,
`total_xp` int(15) NOT NULL,
PRIMARY KEY (`team_member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
There's several ways to write this query.
To me this is the easiest to read and understand:
select * from members where member_id not in (select member_id from team_members).
This is a really simple way to write it. If you decide you want everything you can quickly comment out the where clause:
select m.* from members m left outer join team_members tm on m.member_id = tm.member_id
where tm.member_id is null
This way doesn't seem very popular from the SQL I read but I think it's straightforward:
select m.* from members m where not exists
(select member_id from team_members tm where tm.member_id = m.member_id)
On the face of it, the below query is fine
SELECT members.member_id
FROM members
LEFT OUTER JOIN team_members
ON team_members.member_id = members.member_id
WHERE team_members.member_id IS NULL
This will do, but on reading your question again, you seem to have a date_left column and if you want only those members who have not yet left a team then
SELECT members.member_id
FROM members
LEFT OUTER JOIN (SELECT *
FROM team_members
WHERE team_members.date_left != '') CURRENT_TEAMS
ON CURRENT_TEAMS.member_id = members.member_id
WHERE CURRENT_TEAMS.member_id IS NULL
SQLFiddle example
http://www.sqlfiddle.com/#!2/46b25/6/0

Categories