Working on a support ticketing system with not a lot of tickets (~3,000). To get a summary grid of ticket information, there are five LEFT JOIN statements on custom field table (j25_field_value) containing about 10,000 records. The query runs too long (~10 seconds) and in cases with a WHERE clause, it runs even longer (up to ~30 seconds or more).
Any suggestions for improving the query to reduce the time to run?
Four tables:
j25_support_tickets
CREATE TABLE `j25_support_tickets` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`category_id` int(11) NOT NULL DEFAULT '0',
`user_id` int(11) DEFAULT NULL,
`email` varchar(50) DEFAULT NULL,
`subject` varchar(255) DEFAULT NULL,
`message` text,
`modified_date` datetime DEFAULT NULL,
`priority_id` tinyint(3) unsigned DEFAULT NULL,
`status_id` tinyint(3) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3868 DEFAULT CHARSET=utf8
j25_support_priorities
CREATE TABLE `j25_support_priorities` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=14 DEFAULT CHARSET=utf8
j25_support_statuses
CREATE TABLE `j25_support_statuses` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=utf8
j25_field_value (id, ticket_id, field_id, field_value)
CREATE TABLE `j25_support_field_value` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`ticket_id` int(11) DEFAULT NULL,
`field_id` int(11) DEFAULT NULL,
`field_value` tinytext,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=10889 DEFAULT CHARSET=utf8
Also, ran this:
SELECT LENGTH(field_value) len FROM j25_support_field_value ORDER BY len DESC LIMIT 1
note: the result = 38
The query:
SELECT DISTINCT t.id as ID
, (select p.title from j25_support_priorities p where p.id = t.priority_id) as Priority
, (select s.title from j25_support_statuses s where s.id = t.status_id) as Status
, t.subject as Subject
, t.email as SubmittedByEmail
, type.field_value AS IssueType
, ver.field_value AS Version
, utype.field_value AS UserType
, cust.field_value AS Company
, refno.field_value AS RefNo
, t.modified_date as Modified
FROM j25_support_tickets AS t
LEFT JOIN j25_support_field_value AS type ON t.id = type.ticket_id AND type.field_id =1
LEFT JOIN j25_support_field_value AS ver ON t.id = ver.ticket_id AND ver.field_id =2
LEFT JOIN j25_support_field_value AS utype ON t.id = utype.ticket_id AND utype.field_id =3
LEFT JOIN j25_support_field_value AS cust ON t.id = cust.ticket_id AND cust.field_id =4
LEFT JOIN j25_support_field_value AS refno ON t.id = refno.ticket_id AND refno.field_id =5
ALTER TABLE j25_support_field_value
ADD INDEX (`ticket_id`,`field_id`,`field_value`(50))
This index will work as a covering index for your query. It will allow the joins to use only this index to look up the values. It should perform massively faster than without this index, since currently your query would have to read every row in the table to find what matches each combination of ticket_id and field_id.
I would also suggest converting your tables to InnoDB engine, unless you have a very explicit reason for using MyISAM.
ALTER TABLE tablename ENGINE=InnoDB
As above - a better index would help. You could probably then simplify your query into something like this too (join to the table only once):
SELECT t.id as ID
, p.title as Priority
, s.title as Status
, t.subject as Subject
, t.email as SubmittedByEmail
, case when v.field_id=1 then v.field_value else null end as IssueType
, case when v.field_id=2 then v.field_value else null end as Version
, case when v.field_id=3 then v.field_value else null end as UserType
, case when v.field_id=4 then v.field_value else null end as Company
, case when v.field_id=5 then v.field_value else null end as RefNo
, t.modified_date as Modified
FROM j25_support_tickets AS t
LEFT JOIN j25_support_field_value v ON t.id = v.ticket_id
LEFT JOIN j25_support_priorities p ON p.id = t.priority_id
LEFT JOIN j25_support_statuses s ON s.id = t.status_id;
You can do away with the subqueries for starters and just get them from another join. You can add an index to j25_support_field_value
alter table j25_support_field_value add key(id, field_type);
I assume there is an index on id in j25_support_tickets - if not and if they are unique, add a unique index alter table j25_support_tickets add unique key(id); If they're not unique, remove the word unique from that statement.
In MySQL, a join usually requires an index on the field(s) that you are using to join on. This will hold up and produce very reasonable results with huge tables (100m+), if you follow that rule, you will not go wrong.
are the ids in j25_support_tickets unique? If they are you can do away with the distinct - if not, or if you are getting exact dupicates in each row, still do away with the distinct and add a group by t.id to the end of this:
SELECT t.id as ID
, p.title as Priority
, s.title as Status
, t.subject as Subject
, t.email as SubmittedByEmail
, type.field_value AS IssueType
, ver.field_value AS Version
, utype.field_value AS UserType
, cust.field_value AS Company
, refno.field_value AS RefNo
, t.modified_date as Modified
FROM j25_support_tickets AS t
LEFT JOIN j25_support_field_value AS type ON t.id = type.ticket_id AND type.field_id =1
LEFT JOIN j25_support_field_value AS ver ON t.id = ver.ticket_id AND ver.field_id =2
LEFT JOIN j25_support_field_value AS utype ON t.id = utype.ticket_id AND utype.field_id =3
LEFT JOIN j25_support_field_value AS cust ON t.id = cust.ticket_id AND cust.field_id =4
LEFT JOIN j25_support_field_value AS refno ON t.id = refno.ticket_id AND refno.field_id =5
LEFT JOIN j25_support_priorities p ON p.id = t.priority_id
LEFT JOIN j25_support_statuses s ON s.id = t.status_id;
Switch to InnoDB.
After switching to InnoDB, make the PRIMARY KEY for j25_support_field_value be (ticket_id, field_id) (and get rid if id). (Tacking on field_value(50) will hurt, not help.)
A PRIMARY KEY is a UNIQUE KEY, so don't have both.
Use VARCHAR(255) instead of the nearly-equivalent TINYTEXT.
EAV schema sucks. My ran on EAV.
Related
I have this select:
select t.id, c.user, t.title, pp.foto, t.data from topics t
inner join cadastro c on t.user = c.id
left join profile_picture pp on t.user = pp.user
left join (
select c.topic, MAX(c.data) cdata from comments c
group by c.topic
)c on t.id = c.topic
where t.community = ?
order by ifnull(cdata, t.data) desc
limit 15
I want to select topics and order them by their date or the date of the topic comments, if it has comments.
Unfortunately, this is taking more than 9 seconds.
I don't think the problem here is indexing, but the way I am writing the select itself.
`topics` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`user` INT(11) UNSIGNED NOT NULL,
`title` varchar(100) NOT NULL,
`description` varchar(1000),
`community` INT(11) UNSIGNED NOT NULL,
`data` datetime NOT NULL,
`ip` varchar(20),
PRIMARY KEY (`id`),
FOREIGN KEY (`user`) REFERENCES cadastro (`id`),
FOREIGN KEY (`community`) REFERENCES discussion (`id`)
)
`comments` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`user` INT(11) UNSIGNED NOT NULL,
`comment` varchar(1000) NOT NULL,
`topic` INT(11) UNSIGNED NOT NULL,
`data` datetime NOT NULL,
`ip` varchar(20),
`delete` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
FOREIGN KEY (`user`) REFERENCES cadastro (`id`),
FOREIGN KEY (`topic`) REFERENCES topics (`id`)
)
Your EXPLAIN gives you a strong hint. The first row in that results says, using temporary, using filesort implying that it's not using a an index.
It might be possible to improve this query by adding indexes and removing some conditionals, but I think in this case a better solution exists.
Why not add a new column to topics that indicates the last time a comment was added? (like a last_modified). Every time a comment gets added, just update that column for that topic as well.
It's effectively denormalizing this. I think this a valid usecase and it's always going to be faster than fixing this messy query.
You are performing a full table scan on the table comments on every query. How many rows does it have? At least create the following index:
comments (topic, data);
to avoid reading the whole table every time.
I know you've said you don't think the problem is indexing, but 9 out of 10 times I've had this problem that's exactly what it's been down to.
Ensure you have an index created on each table that you're using in the query and include the columns specified in the join.
Also, as NiVeR said, don't use the same alias multiple times.
Here's a refactoring of that query, unsure if I've mixed up or missed a column name/alias or two though.
select t.id, c.user, t.title, pp.foto, t.data from topics t
inner join cadastro c on t.user = c.id
left join profile_picture pp on t.user = pp.user
left join (
select com.topic, MAX(com.data) comdata from comments com
group by com.topic
)com1 on t.id = com1.topic
where t.community = ?
order by ifnull(com1.comdata, t.data) desc
limit 15
I'm trying to do a link exchange script and run into a bit of trouble.
Each link can be visited by an IP address a number of x times (frequency in links table). Each visit costs a number of credits (spend limit given in limit in links table)
I've got the following tables:
CREATE TABLE IF NOT EXISTS `contor` (
`key` varchar(25) NOT NULL,
`uniqueHandler` varchar(30) DEFAULT NULL,
`uniqueLink` varchar(30) DEFAULT NULL,
`uniqueUser` varchar(30) DEFAULT NULL,
`owner` varchar(50) NOT NULL,
`ip` varchar(15) DEFAULT NULL,
`credits` float NOT NULL,
`tstamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`key`),
KEY `uniqueLink` (`uniqueLink`),
KEY `uniqueHandler` (`uniqueHandler`),
KEY `uniqueUser` (`uniqueUser`),
KEY `owner` (`owner`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `links` (
`unique` varchar(30) NOT NULL DEFAULT '',
`url` varchar(1000) DEFAULT NULL,
`frequency` varchar(5) DEFAULT NULL,
`limit` float NOT NULL DEFAULT '0',
PRIMARY KEY (`unique`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I've got the following query:
$link = MYSQL_QUERY("
SELECT *
FROM `links`
WHERE (SELECT count(key) FROM contor WHERE ip = '$ip' AND contor.uniqueLink = links.unique) <= `frequency`
AND (SELECT sum(credits) as cost FROM contor WHERE contor.uniqueLink = links.unique) <= `limit`")
There are 20 rows in the table links.
The problem is that whenever there are about 200k rows in the table contor the CPU load is huge.
After applying the solution provided by #Barmar:
Added composite index on (uniqueLink, ip) and droping all other indexes except PRIMARY, EXPLAIN gives me this:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY l ALL NULL NULL NULL NULL 18
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 15
2 DERIVED pop_contor index NULL contor_IX1 141 NULL 206122
Try using a join rather than a correlated subquery.
SELECT l.*
FROM links AS l
LEFT JOIN (
SELECT uniqueLink, SUM(ip = '$ip') AS ip_visits, SUM(credits) AS total_credits
FROM contor
GROUP BY uniqueLink
) AS c
ON c.uniqueLink = l.unique AND ip_visits <= frequency AND total_credits <= limit
If this doesn't help, try adding an index on contor.ip.
The current query is of the form:
SELECT l.*
FROM `links` l
WHERE l.frequency >= ( SELECT COUNT(ck.key)
FROM contor ck
WHERE ck.uniqueLink = l.unique
AND ck.ip = '$ip'
)
AND l.limit >= ( SELECT SUM(sc.credits)
FROM contor sc
WHERE sc.uniqueLink = l.unique
)
Those correlated subqueries are going to each your lunch. And your lunchbox too.
I'd suggest testing an inline view that performs both of the aggregations from contor in one pass, and then join the result from that to the links table.
Something like this:
SELECT l.*
FROM ( SELECT c.uniqueLink
, SUM(c.ip = '$ip' AND c.key IS NOT NULL) AS count_key
, SUM(c.credits) AS sum_credits
FROM `contor` c
GROUP
BY c.uniqueLink
) d
JOIN `links` l
ON l.unique = d.uniqueLink
AND l.frequency >= d.count_key
AND l.limit >= d.sum_credits
For optimal performance of the aggregation inline view query, provide a covering index that MySQL can use to optimize the GROUP BY (avoiding a Using filesort operation)
CREATE INDEX `contor_IX1` ON `contor` (`uniqueLink`, `credits`, `ip`) ;
Adding that index renders the uniqueLink index redundant, so also...
DROP INDEX `uniqueLink` ON `contor` ;
EDIT
Since we have a guarantee that contor.key column is non-NULL (i.e. the NOT NULL constraint), this part of the query above is unneeded AND c.key IS NOT NULL, and can be removed. (I also removed the key column from the covering index definition above.)
SELECT l.*
FROM ( SELECT c.uniqueLink
, SUM(c.ip = '$ip') AS count_key
, SUM(c.credits) AS sum_credits
FROM `contor` c
GROUP
BY c.uniqueLink
) d
JOIN `links` l
ON l.unique = d.uniqueLink
AND l.frequency >= d.count_key
AND l.limit >= d.sum_credits
I am trying to execute the following query
SELECT `id`,
`name`,
`ownerid`,
`creationdata`,
`motd`,
(SELECT Count(*)
FROM guild_membership a,
players_online b
WHERE a.player_id = b.player_id
AND a.guild_id = id) AS `online`,
(SELECT Max(b.level)
FROM guild_membership a,
players b
WHERE a.player_id = b.id
AND a.guild_id = id) AS `toplevel`,
(SELECT Min(a.level)
FROM players a,
guild_membership b
WHERE a.id = b.player_id
AND b.guild_id = id) AS `lowlevel`
FROM `guilds`
WHERE `name` = 'Wideswing Poleaxe'
LIMIT 1;
The tables used in here are the followin
CREATE TABLE IF NOT EXISTS `players` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`group_id` int(11) NOT NULL DEFAULT '1',
`account_id` int(11) NOT NULL DEFAULT '0',
`level` int(11) NOT NULL DEFAULT '1',
...
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE,
KEY `vocation` (`vocation`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `guilds` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`ownerid` int(11) NOT NULL,
`creationdata` int(11) NOT NULL,
`motd` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY (`name`),
UNIQUE KEY (`ownerid`),
FOREIGN KEY (`ownerid`) REFERENCES `players`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `guild_membership` (
`player_id` int(11) NOT NULL,
`guild_id` int(11) NOT NULL,
`rank_id` int(11) NOT NULL,
`nick` varchar(15) NOT NULL DEFAULT '',
PRIMARY KEY (`player_id`),
FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (`rank_id`) REFERENCES `guild_ranks` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;
I am trying to get the MAX level and MIN level on the players table inside one guild
However I am always getting toplevel and lowlevel the same value and tis always the lowest level
I am not sure what is wrong
First thing I notice is: you are using LIMIT without ORDER BY. So from the guilds table you expect to find more than one entry for name = 'Wideswing Poleaxe', but only look at the first the DBMS happens to find. Is this desired?
Next thing I see is the out-dated join syntax. Where did you get this from? A twenty year old book? No, stop, twenty years ago this syntax was already made redundant, so it must be even older ;-) Use explicit joins instead (JOIN ... ON ...)
As to your subqueries: You are comparing with id without any qualifier, so the DBMS will take this to be guild_membership.id or players_online resp. players.id, where you really want it to be guild.id. This should explain that you get unexpected values.
As to how the query is built: You could join to the aggregated player data instead. And use alias names that match the tables.
select
guilds.id,
guilds.name,
guilds.ownerid,
guilds.creationdata,
guilds.motd,
players.online,
players.toplevel,
players.lowlevel
from guilds
left join
(
select
gms.guild_id,
max(p.level) as toplevel,
min(p.level) as lowlevel,
sum((select count(*) from players_online po where po.player_id = p.id)) as online
from guild_membership gms
join players p on p.id = gms.player_id
group by gms.guild_id
) players on players.guild_id = guilds.id
where guilds.name = 'Wideswing Poleaxe';
You can change the left outer join (left join) to an inner join (join), if you don't need to see guilds without any player.
I think the problem is here: a.guild_id = id
The id being used is from players, not guilds, as it is still part of the sub-query.
You shouldn't need all those subqueries, JOINs are almost always faster and should usually be first technique tried.
Try this...
SELECT `id`, `name`, `ownerid`, `creationdata`, `motd`
, COUNT(po.player_id) AS online
, MAX(p.level) AS toplevel
, MIN(p.level) AS lowlevel
FROM `guilds` AS g
LEFT JOIN guild_membership AS gm ON g.id = gm.guild_id
LEFT JOIN players AS p ON gm.player_id = p.player_id
LEFT JOIN players_online AS po ON gm.player_id = po.player_id
WHERE g.`name` = 'Wideswing Poleaxe'
;
COUNT only counts non-null values; similarly MAX, MIN, and most other aggregate functions ignore null values (only returning null if only null values were processed).
You should consider modifying your query like
SELECT g.`id`,
g.`name`,
g.`ownerid`,
g.`creationdata`,
g.`motd`,
(SELECT Count(*)
FROM guild_membership a,
players_online b
WHERE a.player_id = b.player_id
AND a.guild_id = id) AS `online`,
(SELECT Max(b.level)
FROM players b join guild_membership a on a.player_id = b.id
AND a.guild_id = g.id) AS `toplevel`,
(SELECT Min(a.level)
FROM players a join
guild_membership b on a.id = b.player_id
AND b.guild_id = g.id) AS `lowlevel`
FROM `guilds` g
WHERE g.`name` = 'Wideswing Poleaxe'
LIMIT 1;
There are two tables over here: first is for basic details of "poll" and
second for the "answer". My requirement is like this:
i want poll records to display only for users who didn't answer the poll
I tried this query but it is not working:
select p.* from mycom_poll as p
LEFT JOIN mycom_polls_result as pr on (p.b_id != pr.b_poll_id
And pr.b_user_id !=14) where p.v_status = 'enable'
And p.v_country like '%India%'
AND p.d_activate_date <= '2015-04-17' order by p.b_id limit 1"
CREATE TABLE IF NOT EXISTS `mycom_poll` (
`b_id` bigint(20) NOT NULL AUTO_INCREMENT,
`b_user_id` bigint(20) NOT NULL DEFAULT '0',
`v_code` varchar(50) NOT NULL,
`v_question` varchar(1000) CHARACTER SET utf8 NOT NULL,
`v_country` varchar(100) NOT NULL,
`d_activate_date` date NOT NULL,
`dt_created_date` datetime NOT NULL,
`dt_updated_date` datetime NOT NULL,
`v_ip` varchar(20) NOT NULL,
`v_status` varchar(20) NOT NULL DEFAULT 'enable',
PRIMARY KEY (`b_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=9 ;
INSERT INTO `mycom_poll` (`b_id`, `b_user_id`, `v_code`, `v_question`, `v_country`, `d_activate_date`, `dt_created_date`, `dt_updated_date`, `v_ip`, `v_status`) VALUES(8, 0, '20150417115406-192168113C5ZJpXu2hM', 'Who is chief minstor of Gujarat in 2015 Rajyasabha?', '["Global","India","United States"]', '2015-04-15', '2015-04-17 11:56:12', '2015-04-18 07:29:11', '127.0.0.1', 'enable');
CREATE TABLE IF NOT EXISTS `mycom_polls_result` (
`b_id` bigint(20) NOT NULL AUTO_INCREMENT,
`b_poll_id` bigint(20) NOT NULL,
`b_user_id` bigint(20) NOT NULL,
`b_poll_answer` bigint(20) NOT NULL,
`dt_created_date` datetime NOT NULL,
`dt_updated_date` datetime NOT NULL,
`v_ip` varchar(20) NOT NULL,
`v_status` varchar(20) NOT NULL DEFAULT 'enable',
PRIMARY KEY (`b_id`),
KEY `b_poll_id` (`b_poll_id`,`b_user_id`),
KEY `b_poll_id_2` (`b_poll_id`,`b_user_id`,`b_poll_answer`),
KEY `b_poll_id_3` (`b_poll_id`,`b_user_id`,`b_poll_answer`),
KEY `b_user_id` (`b_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `mycom_polls_result` (`b_id`, `b_poll_id`, `b_user_id`, `b_poll_answer`, `dt_created_date`, `dt_updated_date`, `v_ip`, `v_status`) VALUES
(1, 8, 14, 64, '2015-04-18 13:27:50', '2015-04-18 13:27:50', '127.0.0.1', 'enable');
Your query:
select p.* from mycom_poll as p
LEFT JOIN mycom_polls_result as pr on (p.b_id != pr.b_poll_id
And pr.b_user_id !=14) where p.v_status = 'enable'
And p.v_country like '%India%'
AND p.d_activate_date <= '2015-04-17' order by p.b_id limit 1
First I am going to point out the mistake of this query, what you are doing here is, you are left joining two tables on id of first table not equals to id of second table which will join all the table data whose ids are not same, when you do a left join, you always get the data of left table and if data not present in right table you get null.
So in where clause you should check any if the field of second table is null or not like this.
select p.* from mycom_poll as p
LEFT JOIN mycom_polls_result as pr on p.b_id = pr.b_poll_id
where pr.b_user_id !=14 p.v_status = 'enable'
And p.v_country like '%India%' AND p.d_activate_date <= '2015-04-17'
and pr.b_poll_id is null order by p.b_id limit 1
I request you to understand left join first.
At the moment you are using LEFT JOIN which is correct to force all rows of the mycom_poll table to be considered, but you're joining on p.b_id != pr.b_poll_id which will associate each poll row in mycom_poll with all the unrelated results rows in mycom_polls_result that don't belong to it.
You probably need to join on p.b_id = pr.b_poll_id, and then require rows with pr.b_poll_id IS NULL in your WHERE clause.
You also seem to be trying to restrict results to pr.b_user_id !=14, which is fine, but that needs to be stated along with other filtering conditions in the WHERE clause. The LEFT JOIN ON clause is just going to be used to declare the column relationships between your tables.
SELECT p.*
FROM mycom_poll as p
LEFT JOIN mycom_polls_result as pr
ON p.b_id = pr.b_poll_id
WHERE
pr.b_user_id !=14
AND p.v_status = 'enable'
AND p.v_country like '%India%'
AND p.d_activate_date <= '2015-04-17'
AND pr.b_poll_id IS NULL
ORDER BY p.b_id LIMIT 1
HTH
I need some help with a MySQL query. I have two tables, one with offers and one with statuses. An offer can has one or more statuses. What I would like to do is get all the offers and their latest status. For each status there's a table field named 'added' which can be used for sorting.
I know this can be easily done with two queries, but I need to make it with only one because I also have to apply some filters later in the project.
Here's my setup:
CREATE TABLE `test`.`offers` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`client` TEXT NOT NULL ,
`products` TEXT NOT NULL ,
`contact` TEXT NOT NULL
) ENGINE = MYISAM ;
CREATE TABLE `statuses` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`offer_id` int(11) NOT NULL,
`options` text NOT NULL,
`deadline` date NOT NULL,
`added` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
Should work but not very optimal imho :
SELECT *
FROM offers
INNER JOIN statuses ON (statuses.offer_id = offers.id
AND statuses.id =
(SELECT allStatuses.id
FROM statuses allStatuses
WHERE allStatuses.offer_id = offers.id
ORDER BY allStatuses.added DESC LIMIT 1))
Try this:
SELECT
o.*
FROM offers o
INNER JOIN statuses s ON o.id = s.offer_id
ORDER BY s.added
LIMIT 1