get the last record of table in select query - sql

This is a follow up on another problem i had with getting-the-last-record-inserted-into-a-select-query
I am trying to edit a query that Andrea was kind enough to help me with yesterday which works fine for one page but I am trying to create a similar query without much luck.
What I need to to is for every board display the board name, the count of topics and messages linked to that board and the user, topic and date of the last message (which does work)
What i need is to get the board name, the topic and message count
This is my table structure
CREATE TABLE `boards` (
`boardid` int(2) NOT NULL auto_increment,
`boardname` varchar(255) NOT NULL default '',
PRIMARY KEY (`boardid`)
);
CREATE TABLE `messages` (
`messageid` int(6) NOT NULL auto_increment,
`topicid` int(4) NOT NULL default '0',
`message` text NOT NULL,
`author` varchar(255) NOT NULL default '',
`date` datetime(14) NOT NULL,
PRIMARY KEY (`messageid`)
);
CREATE TABLE `topics` (
`topicid` int(4) NOT NULL auto_increment,
`boardid` int(2) NOT NULL default '0',
`topicname` varchar(255) NOT NULL default '',
`author` varchar(255) NOT NULL default '',
PRIMARY KEY (`topicid`)
);
and the query I have come up with based on the query that Andrea did for me. What this query outputs in the boardname, the number of topics and messages (which says 1 even though there are 5), the topic author and messagecount (which isn't needed), the author and date of the last post (which is needed) but not the topic name which is needed
SELECT b.boardname, count( DISTINCT t.topicname ) AS topics, count( lm.message ) AS message, t.author as tauthor,
(select count(message) from messages m where m.topicid = t.topicid) AS messagecount,
lm.author as lauthor, lm.date
FROM topics t
INNER JOIN messages lm
ON lm.topicid = t.topicid AND lm.date = (SELECT max(m2.date) from messages m2)
INNER JOIN boards b
ON b.boardid = t.boardid
GROUP BY t.topicname
This my original query that does what I wanted but get the first post, not the last
SELECT b.boardid, b.boardname, count( DISTINCT t.topicname ) AS topics, count( m.message ) AS message, m.author AS author, m.date AS date, t.topicname AS topic
FROM boards b
INNER JOIN topics t ON t.boardid = b.boardid
INNER JOIN messages m ON t.topicid = m.topicid
INNER JOIN (
SELECT topicid, MAX( date ) AS maxdate
FROM messages
GROUP BY topicid
) test ON test.topicid = t.topicid
GROUP BY boardname
ORDER BY boardname
any help with this much appreciated

You need to define "LAST", in terms of an ORDER BY clause. Once you do that, you can just reverse the direction of your order and add LIMIT 1 to the query.

SELECT b.*, m.*, t,*
(
SELECT COUNT(*)
FROM topics ti
WHERE ti.boardid = b.boardid
) AS topiccount,
(
SELECT COUNT(*)
FROM topics ti, messages mi
WHERE ti.boardid = b.boardid
AND mi.topicid = ti.topicid
) AS messagecount
FROM boards b
LEFT JOIN
messages m
ON m.messageid = (
SELECT mii.messageid
FROM topics tii, messages mii
WHERE tii.boardid = b.boardid
AND mii.topicid = tii.topicid
ORDER BY
mii.date DESC
LIMIT 1
)
LEFT JOIN
topics t
ON t.topicid = m.topicid

Related

Duplicate rows returned even though group by is used

This is my query
SELECT p.book FROM customers_books p
INNER JOIN books b ON p.book = b.id
INNER JOIN bookprices bp ON bp.book = p.book
WHERE b.status = 'PUBLISHED' AND bp.currency_code = 'GBP'
AND p.book NOT IN (SELECT cb.book FROM customers_books cb WHERE cb.customer = 1)
GROUP BY p.book, p.created_date ORDER BY p.created_date DESC
This is the data in my customers_books table,
I expect only 8,6,1 of books IDs to return but query is returning 8,6,1,1
table structures are here
CREATE TABLE "public"."customers_books" (
"id" int8 NOT NULL,
"created_date" timestamp(6),
"book" int8,
"customer" int8,
);
CREATE TABLE "public"."books" (
"id" int8 NOT NULL,
"created_date" timestamp(6),
"status" varchar(255) COLLATE "pg_catalog"."default",
)
CREATE TABLE "public"."bookprices" (
"id" int8 NOT NULL,
"currency_code" varchar(255) COLLATE "pg_catalog"."default",
"book" int8
)
what do you think I am doing wrong here.
I really dont want to use p.created_date in group by but I was forced to use because of order by
You have too many joins in the outer query:
SELECT b.book
FROM books b INNER JOIN
bookprices bp
ON bp.book = p.book
WHERE b.status = 'PUBLISHED' AND bp.currency_code = 'GBP' AND
NOT EXISTS (SELECT 1
FROM customers_books cb
WHERE cb.book = p.book AND cb.customer = 1
) ;
Note that I replaced the NOT IN with NOT EXISTS. I strongly, strongly discourage you from using NOT IN with a subquery. If the subquery returns any NULL values, then NOT IN returns no rows at all. It is better to sidestep this issue just by using NOT EXISTS.

SQL query : SELECT

CREATE TABLE WRITTEN_BY
( Re_Id CHAR(15) NOT NULL,
Pub_Number INT NOT NULL,
PRIMARY KEY(Re_Id, Pub_Number),
FOREIGN KEY(Re_Id) REFERENCES RESEARCHER(Re_Id),
FOREIGN KEY(Pub_Number) REFERENCES PUBLICATION(Pub_Number));
CREATE TABLE WORKING_ON
( Re_Id CHAR(15) NOT NULL,
Pro_Code CHAR(15) NOT NULL,
PRIMARY KEY(Re_Id, Pro_Code, Subpro_Code)
FOREIGN KEY(Re_Id) REFERENCES RESEARCHER(Re_Id));
Re_Id stands for ID of a researcher
Pub_Number stands for ID of a publication
Pro_Code stands for ID of a project
Written_by table stores information about a Publication's ID and it's author
Working_on table stores information about a Project's ID and who is working on it
Now, I have this query :
For each project, find the researcher who wrote the most number of publications .
This is what i've done so far :
SELECT Pro_Code,WORK.Re_Id
FROM WORKING_ON AS WORK , WRITTEN_BY AS WRITE
WHERE WORK.Re_Id = WRITE.Re.Id
so I got a table which contains personal ID and project's ID of a researcher who has at least 1 publication. But what's next ? How to solve this problem?
You haven't said which platform you're on but try this. It handles the case where there are ties as well.
select g.Pro_Code, g.Re_Id, g.numpublished
from
(
SELECT work.Pro_Code, WORK.Re_Id, count(WRITE.pub_number) as numpublished
FROM WORKING_ON WORK JOIN WRITTEN_BY AS WRITE ON WORK.Re_Id = WRITE.Re_Id
GROUP BY work.Pro_Code, WORK.Re_Id
) g
inner join
(
select Pro_code, max(numpublished) as maxpublished
from (
SELECT work.Pro_Code, WORK.Re_Id, count(WRITE.pub_number) numpublished
FROM WORKING_ON WORK JOIN WRITTEN_BY AS WRITE ON WORK.Re_Id = WRITE.Re_Id
GROUP BY work.Pro_Code, WORK.Re_Id
) g2
group by Pro_code
) m
on m.Pro_code = g.Pro_Code and m.maxpublished = g.numpublished
Some platforms will allow you to write it this way:
with g as (
SELECT work.Pro_Code, WORK.Re_Id, count(WRITE.pub_number) as numpublished
FROM WORKING_ON WORK JOIN WRITTEN_BY AS WRITE ON WORK.Re_Id = WRITE.Re_Id
GROUP BY work.Pro_Code, WORK.Re_Id
)
select g.Pro_Code, g.Re_Id, g.numpublished
from g
inner join
(
select Pro_code, max(numpublished) as maxpublished
from g
group by Pro_code
) m
on m.Pro_code = g.Pro_Code and m.maxpublished = g.numpublished
I think that you are looking for something like the following :
select
tm.pro_code as pro_code,
tm.re_id as re_id,
max(total) as max_pub
from (
select *
from (
select
wo.pro_code as pro_code
wr.re_id as re_id,
count(wr.pub_number) as total
from
written_by wr,
working_on wo
where
wr.re_id = wo.re_id
group by wr.re_id,wo.pro_code
)
) tm
group by pro_code
If you are using MS SQL, this should work:
With cte as (
select a.Re_Id, Pub_Number,Pro_Code, COUNT(distinct Pub_Number) as pubs
from WRITTEN_BY a
inner join WORKING_ON b
on a.Re_Id = b.Re_Id)
SELECT Re_Id,pubs from cte
HAVING pubs = MAX(pubs)
GROUP BY Re_Id

sql queries slower than expected

Before I show the query here are the relevant table definitions:
CREATE TABLE phpbb_posts (
topic_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
poster_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
KEY topic_id (topic_id),
KEY poster_id (poster_id),
);
CREATE TABLE phpbb_topics (
topic_id mediumint(8) UNSIGNED NOT NULL auto_increment
);
Here's the query I'm trying to do:
SELECT p.topic_id, p.poster_id
FROM phpbb_topics AS t
LEFT JOIN phpbb_posts AS p
ON p.topic_id = t.topic_id
AND p.poster_id <> ...
WHERE p.poster_id IS NULL;
Basically, the query is an attempt to find all topics where the number of times someone other than the target user has posted in is zero. In other words, the topics where the only person who has posted is the target user.
Problem is that query is taking a super long time. Here's the EXPLAIN for it:
Array
(
[id] => 1
[select_type] => SIMPLE
[table] => t
[type] => index
[possible_keys] =>
[key] => topic_approved
[key_len] => 1
[ref] =>
[rows] => 146484
[Extra] => Using index
)
Array
(
[id] => 1
[select_type] => SIMPLE
[table] => p
[type] => ref
[possible_keys] => topic_id,poster_id,tid_post_time
[key] => tid_post_time
[key_len] => 3
[ref] => db_name.t.topic_id
[rows] => 1
[Extra] => Using where; Not exists
)
My general assumption when it comes to SQL is that JOINs of any are super fast and can be done in no time at all assuming all relevant columns are primary or foreign keys (which in this case they are).
I tried out a few other queries:
SELECT COUNT(1)
FROM phpbb_topics AS t
JOIN phpbb_posts AS p
ON p.topic_id = t.topic_id;
That returns 353340 pretty quickly.
I then do these:
SELECT COUNT(1)
FROM phpbb_topics AS t
JOIN phpbb_posts AS p
ON p.topic_id = t.topic_id
AND p.poster_id <> 77198;
SELECT COUNT(1)
FROM phpbb_topics AS t
JOIN phpbb_posts AS p
ON p.topic_id = t.topic_id
WHERE p.poster_id <> 77198;
And both of those take quite a while (between 15-30 seconds). If I change the <> to a = it takes no time at all.
Am I making some incorrect assumptions? Maybe my DB is just foobar'd?
I think replacing index on phpbb_posts(topic_id) to composite index on 2 fields should improve performance of your query :
CREATE TABLE phpbb_posts (
topic_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
poster_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
--KEY topic_id (topic_id),
KEY topic_id_poster_id (topic_id,poster_id)
KEY poster_id (poster_id),
);
Your indexes look sufficient to me... could you try this query and let me know how the performance compares to your original?
SELECT sub.topic_id
FROM (
SELECT t.topic_id
FROM phpbb_topics AS t
WHERE
EXISTS (
SELECT *
FROM phpbb_posts p
WHERE
p.topic_id = t.topic_id
AND p.poster_id = 77198
)
) sub
WHERE
NOT EXISTS (
SELECT *
FROM phpbb_posts p
WHERE
p.topic_id = sub.topic_id
AND p.poster_id <> 77198
)
My thoughts are that by limiting the topics to only those that the poster in question has actually posted in, that the anti-join (implemented in this case with NOT EXISTS instead of a LEFT JOIN) will have to check much fewer topics for posters other than the one being searched.
SELECT t.topic_id
FROM phpbb_topics AS t
JOIN phpbb_posts AS p1
ON p1.topic_id = t.topic_id
AND p1.poster_id = $poster_id
LEFT JOIN phpbb_posts AS p2
ON p2.topic_id = t.topic_id
AND p2.poster_id <> $poster_id
WHERE p2.poster_id IS NULL
That made it a ton faster. I'm getting all the posts where the target user has posted with the topic info attached to that and then getting all the people other than the target who've posted.
There'll be lots of duplicates in the p1.poster_id column but since I'm not actually getting that row I figure duplicates in that column don't matter a whole lot.
Thanks!

query optimisation with join on ordered data

My question concerns the optimization of a query sql.
My query retrieves a list of members and their last training.
To get the latest training I do a join on the result of a query returning a complete list of training for all members.
This query works but it is very slow, I'm really interrested if someone would have a solution for it to execute faster.
My query (about 16s):
SELECT
m.nom,
m.prenom,
m.ville,
m.maj,
mbf.libelle,
mbf.datefin,
m.id as idmb
FROM
membres m
LEFT JOIN (
select *
from membreform
where idformation = 1
order by datefin DESC
) as mbf ON mbf.idmembre = m.id
WHERE
role > 0 AND visible = 1
group by m.id
ORDER BY m.maj DESC
limit 0 , 20
My data structure :
membreform (1000 entries)
id int(11) NOT NULL AUTO_INCREMENT,
idmembre int(11) NOT NULL,
libelle varchar(128) NOT NULL,
idformation int(11) NOT NULL,
datedebut date NOT NULL,
datefin date NOT NULL DEFAULT '0000-00-00',
descript text NOT NULL,
KEY id (id),
KEY idmembre (idmembre),
KEY idformation (idformation)
membres (500 entries)
id int(3) NOT NULL AUTO_INCREMENT,
nom varchar(255) NOT NULL,
prenom varchar(255) NOT NULL,
ville varchar(255) NOT NULL,
email varchar(255) NOT NULL,
maj datetime NOT NULL,
role tinyint(4) NOT NULL DEFAULT '1',
PRIMARY KEY (id),
KEY role (role),
KEY maj (maj)
I tested this other way (about 0.40s) but I dont find that really clean
SELECT
m.nom,
m.prenom,
m.ville,
m.maj,
m.id as idmb,
(select
libelle
from
membreform
where
idformation = 1
AND m.id = membreform.idmembre
order by datefin DESC
limit 1
) libelle,
(select
datefin
from
membreform
where
idformation = 1
AND m.id = membreform.idmembre
order by datefin DESC
limit 1
) datefin
FROM
membres m
WHERE
role > 0 AND visible = 1
group by m.id
ORDER BY m.maj DESC
limit 0 , 20
I'm open to any suggestions because I am a bit stuck
thank you
You can use temporary table to increase performace. E.g (MS SQL)
1 Step - Get the memberform data into temp table by adding all relevant where conditions
select *
INTO #TempMembreform
from membreform
where idformation = 1 ...
order by datefin DESC
2 step - do the left outer join as you did in the above first example.
E.g.
SELECT
m.nom,
m.prenom,
m.ville,
m.maj,
mbf.libelle,
mbf.datefin,
m.id as idmb
FROM
membres m
LEFT JOIN #TempMembreForm as mbf
ON mbf.idmembre = m.id
WHERE
role > 0 AND visible = 1
group by m.id
ORDER BY m.maj DESC
limit 0 , 20
3 Step - add NOLOCK keyword if the data is not mision crical (e.g. Bank tracnsactions )
select *
INTO #TempMembreform
from membreform WITH(NOLOCK)
where idformation = 1
order by datefin DESC
SELECT
m.nom,
m.prenom,
m.ville,
m.maj,
mbf.libelle,
mbf.datefin,
m.id as idmb
FROM
membres m with(nolock)
LEFT JOIN #TempMembreForm as mbf
ON mbf.idmembre = m.id
WHERE
role > 0 AND visible = 1
group by m.id
ORDER BY m.maj DESC
limit 0 , 20
My profile

Mysql - help me optimize this query

About the system:
-The system has a total of 8 tables
- Users
- Tutor_Details (Tutors are a type of User,Tutor_Details table is linked to Users)
- learning_packs, (stores packs created by tutors)
- learning_packs_tag_relations, (holds tag relations meant for search)
- tutors_tag_relations and tags and
orders (containing purchase details of tutor's packs),
order_details linked to orders and tutor_details.
For a more clear idea about the tables involved please check the The tables section in the end.
-A tags based search approach is being followed.Tag relations are created when new tutors register and when tutors create packs (this makes tutors and packs searcheable). For details please check the section How tags work in this system? below.
Following is a simpler representation (not the actual) of the more complex query which I am trying to optimize:- I have used statements like explanation of parts in the query
============================================================================
select
SUM(DISTINCT( t.tag LIKE "%Dictatorship%" )) as key_1_total_matches,
SUM(DISTINCT( t.tag LIKE "%democracy%" )) as key_2_total_matches,
td.*, u.*, count(distinct(od.id_od)), `if (lp.id_lp > 0) then some conditional logic on lp fields else 0 as tutor_popularity`
from Tutor_Details AS td JOIN Users as u on u.id_user = td.id_user
LEFT JOIN Learning_Packs_Tag_Relations AS lptagrels ON td.id_tutor = lptagrels.id_tutor
LEFT JOIN Learning_Packs AS lp ON lptagrels.id_lp = lp.id_lp
LEFT JOIN `some other tables on lp.id_lp - let's call learning pack tables set (including
Learning_Packs table)`
LEFT JOIN Order_Details as od on td.id_tutor = od.id_author LEFT JOIN Orders as o on
od.id_order = o.id_order
LEFT JOIN Tutors_Tag_Relations as ttagrels ON td.id_tutor = ttagrels.id_tutor
JOIN Tags as t on (t.id_tag = ttagrels.id_tag) OR (t.id_tag = lptagrels.id_tag)
where `some condition on Users table's fields`
AND CASE WHEN ((t.id_tag = lptagrels.id_tag) AND (lp.id_lp > 0)) THEN `some
conditions on learning pack tables set` ELSE 1 END
AND CASE WHEN ((t.id_tag = wtagrels.id_tag) AND (wc.id_wc > 0)) THEN `some
conditions on webclasses tables set` ELSE 1 END
AND CASE WHEN (od.id_od>0) THEN od.id_author = td.id_tutor and `some conditions on Orders table's fields` ELSE 1 END
AND ( t.tag LIKE "%Dictatorship%" OR t.tag LIKE "%democracy%")
group by td.id_tutor HAVING key_1_total_matches = 1 AND key_2_total_matches = 1
order by tutor_popularity desc, u.surname asc, u.name asc limit
0,20
=====================================================================
What does the above query do?
Does AND logic search on the search keywords (2 in this example - "Democracy" and "Dictatorship").
Returns only those tutors for which both the keywords are present in the union of the two sets - tutors details and details of all the packs created by a tutor.
To make things clear - Suppose a Tutor name "Sandeepan Nath" has created a pack "My first pack", then:-
Searching "Sandeepan Nath" returns Sandeepan Nath.
Searching "Sandeepan first" returns Sandeepan Nath.
Searching "Sandeepan second" does not return Sandeepan Nath.
======================================================================================
The problem
The results returned by the above query are correct (AND logic working as per expectation), but the time taken by the query on heavily loaded databases is like 25 seconds as against normal query timings of the order of 0.005 - 0.0002 seconds, which makes it totally unusable.
It is possible that some of the delay is being caused because all the possible fields have not yet been indexed, but I would appreciate a better query as a solution, optimized as much as possible, displaying the same results
==========================================================================================
How tags work in this system?
When a tutor registers, tags are entered and tag relations are created with respect to tutor's details like name, surname etc.
When a Tutors create packs, again tags are entered and tag relations are created with respect to pack's details like pack name, description etc.
tag relations for tutors stored in tutors_tag_relations and those for packs stored in learning_packs_tag_relations. All individual tags are stored in tags table.
====================================================================
The tables
Most of the following tables contain many other fields which I have omitted here.
CREATE TABLE IF NOT EXISTS `users` (
`id_user` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL DEFAULT '',
`surname` varchar(155) NOT NULL DEFAULT '',
PRIMARY KEY (`id_user`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=636 ;
CREATE TABLE IF NOT EXISTS `tutor_details` (
`id_tutor` int(10) NOT NULL AUTO_INCREMENT,
`id_user` int(10) NOT NULL DEFAULT '0',
PRIMARY KEY (`id_tutor`),
KEY `Users_FKIndex1` (`id_user`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=51 ;
CREATE TABLE IF NOT EXISTS `orders` (
`id_order` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id_order`),
KEY `Orders_FKIndex1` (`id_user`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=275 ;
ALTER TABLE `orders`
ADD CONSTRAINT `Orders_ibfk_1` FOREIGN KEY (`id_user`) REFERENCES `users`
(`id_user`) ON DELETE NO ACTION ON UPDATE NO ACTION;
CREATE TABLE IF NOT EXISTS `order_details` (
`id_od` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_order` int(10) unsigned NOT NULL DEFAULT '0',
`id_author` int(10) NOT NULL DEFAULT '0',
PRIMARY KEY (`id_od`),
KEY `Order_Details_FKIndex1` (`id_order`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=284 ;
ALTER TABLE `order_details`
ADD CONSTRAINT `Order_Details_ibfk_1` FOREIGN KEY (`id_order`) REFERENCES `orders`
(`id_order`) ON DELETE NO ACTION ON UPDATE NO ACTION;
CREATE TABLE IF NOT EXISTS `learning_packs` (
`id_lp` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_author` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id_lp`),
KEY `Learning_Packs_FKIndex2` (`id_author`),
KEY `id_lp` (`id_lp`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=23 ;
CREATE TABLE IF NOT EXISTS `tags` (
`id_tag` int(10) unsigned NOT NULL AUTO_INCREMENT,
`tag` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id_tag`),
UNIQUE KEY `tag` (`tag`),
KEY `id_tag` (`id_tag`),
KEY `tag_2` (`tag`),
KEY `tag_3` (`tag`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3419 ;
CREATE TABLE IF NOT EXISTS `tutors_tag_relations` (
`id_tag` int(10) unsigned NOT NULL DEFAULT '0',
`id_tutor` int(10) DEFAULT NULL,
KEY `Tutors_Tag_Relations` (`id_tag`),
KEY `id_tutor` (`id_tutor`),
KEY `id_tag` (`id_tag`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `tutors_tag_relations`
ADD CONSTRAINT `Tutors_Tag_Relations_ibfk_1` FOREIGN KEY (`id_tag`) REFERENCES
`tags` (`id_tag`) ON DELETE NO ACTION ON UPDATE NO ACTION;
CREATE TABLE IF NOT EXISTS `learning_packs_tag_relations` (
`id_tag` int(10) unsigned NOT NULL DEFAULT '0',
`id_tutor` int(10) DEFAULT NULL,
`id_lp` int(10) unsigned DEFAULT NULL,
KEY `Learning_Packs_Tag_Relations_FKIndex1` (`id_tag`),
KEY `id_lp` (`id_lp`),
KEY `id_tag` (`id_tag`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `learning_packs_tag_relations`
ADD CONSTRAINT `Learning_Packs_Tag_Relations_ibfk_1` FOREIGN KEY (`id_tag`)
REFERENCES `tags` (`id_tag`) ON DELETE NO ACTION ON UPDATE NO ACTION;
===================================================================================
Following is the exact query (this includes classes also - tutors can create classes and search terms are matched with classes created by tutors):-
SELECT SUM(DISTINCT( t.tag LIKE "%Dictatorship%" )) AS key_1_total_matches,
SUM(DISTINCT( t.tag LIKE "%democracy%" )) AS key_2_total_matches,
COUNT(DISTINCT( od.id_od )) AS tutor_popularity,
CASE
WHEN ( IF(( wc.id_wc > 0 ), ( wc.wc_api_status = 1
AND wc.wc_type = 0
AND wc.class_date > '2010-06-01 22:00:56'
AND wccp.status = 1
AND ( wccp.country_code = 'IE'
OR wccp.country_code IN ( 'INT' )
) ), 0)
) THEN 1
ELSE 0
END AS 'classes_published',
CASE
WHEN ( IF(( lp.id_lp > 0 ), ( lp.id_status = 1
AND lp.published = 1
AND lpcp.status = 1
AND ( lpcp.country_code = 'IE'
OR lpcp.country_code IN ( 'INT' )
) ), 0)
) THEN 1
ELSE 0
END AS 'packs_published',
td . *,
u . *
FROM tutor_details AS td
JOIN users AS u
ON u.id_user = td.id_user
LEFT JOIN learning_packs_tag_relations AS lptagrels
ON td.id_tutor = lptagrels.id_tutor
LEFT JOIN learning_packs AS lp
ON lptagrels.id_lp = lp.id_lp
LEFT JOIN learning_packs_categories AS lpc
ON lpc.id_lp_cat = lp.id_lp_cat
LEFT JOIN learning_packs_categories AS lpcp
ON lpcp.id_lp_cat = lpc.id_parent
LEFT JOIN learning_pack_content AS lpct
ON ( lp.id_lp = lpct.id_lp )
LEFT JOIN webclasses_tag_relations AS wtagrels
ON td.id_tutor = wtagrels.id_tutor
LEFT JOIN webclasses AS wc
ON wtagrels.id_wc = wc.id_wc
LEFT JOIN learning_packs_categories AS wcc
ON wcc.id_lp_cat = wc.id_wp_cat
LEFT JOIN learning_packs_categories AS wccp
ON wccp.id_lp_cat = wcc.id_parent
LEFT JOIN order_details AS od
ON td.id_tutor = od.id_author
LEFT JOIN orders AS o
ON od.id_order = o.id_order
LEFT JOIN tutors_tag_relations AS ttagrels
ON td.id_tutor = ttagrels.id_tutor
JOIN tags AS t
ON ( t.id_tag = ttagrels.id_tag )
OR ( t.id_tag = lptagrels.id_tag )
OR ( t.id_tag = wtagrels.id_tag )
WHERE ( u.country = 'IE'
OR u.country IN ( 'INT' ) )
AND CASE
WHEN ( ( t.id_tag = lptagrels.id_tag )
AND ( lp.id_lp > 0 ) ) THEN lp.id_status = 1
AND lp.published = 1
AND lpcp.status = 1
AND ( lpcp.country_code = 'IE'
OR lpcp.country_code IN (
'INT'
) )
ELSE 1
END
AND CASE
WHEN ( ( t.id_tag = wtagrels.id_tag )
AND ( wc.id_wc > 0 ) ) THEN wc.wc_api_status = 1
AND wc.wc_type = 0
AND
wc.class_date > '2010-06-01 22:00:56'
AND wccp.status = 1
AND ( wccp.country_code = 'IE'
OR wccp.country_code IN (
'INT'
) )
ELSE 1
END
AND CASE
WHEN ( od.id_od > 0 ) THEN od.id_author = td.id_tutor
AND o.order_status = 'paid'
AND CASE
WHEN ( od.id_wc > 0 ) THEN od.can_attend_class = 1
ELSE 1
END
ELSE 1
END
GROUP BY td.id_tutor
HAVING key_1_total_matches = 1
AND key_2_total_matches = 1
ORDER BY tutor_popularity DESC,
u.surname ASC,
u.name ASC
LIMIT 0, 20
Please note - The provided database structure does not show all the fields and tables as in this query
=====================================================================================
The explain query output:-
Please see this screenshot
http://www.test.examvillage.com/Explain_query.jpg
Information on row counts, value distributions, indexes, size of the database, size of memory, disk layout - raid 0, 5, etc - how many users are hitting your database when queries are slow - what other queries are running. All these things factor into performance.
Also a print out of the explain plan output may shed some light on the cause if it's simply a query / index issue. The exact query would be needed as well.
You really should use some better formatting for the query.
Just add at least 4 spaces to the beginning of each row to get this nice code formatting.
SELECT * FROM sometable
INNER JOIN anothertable ON sometable.id = anothertable.sometable_id
Or have a look here: https://stackoverflow.com/editing-help
Could you provide the execution plan from mysql? You need to add "EXPLAIN" to the query and copy the result.
EXPLAIN SELECT * FROM ...complexquery...
will give you some useful hints (execution order, returned rows, available/used indexes)
Your question is, "how can I find tutors that match certain tags?" That's not a hard question, so the query to answer it shouldn't be hard either.
Something like:
SELECT *
FROM tutors
WHERE tags LIKE '%Dictator%' AND tags LIKE '%Democracy%'
That will work, if you modify your design to have a "tags" field in your "tutors" table, in which you put all the tags that apply to that tutor. It will eliminate layers of joins and tables.
Are all those layers of joins and tables providing real functionality, or just more programming headaches? Think about the functionality that your app REALLY needs, and then simplify your database design!!
Answering my own question.
The main problem with this approach was that too many tables were joined in a single query. Some of those tables like Tags (having large number of records - which can in future hold as many as all the English words in the vocabulary) when joined with so many tables cause this multiplication effect which can in no way be countered.
The solution is basically to make sure too many joins are not made in a single query. Breaking one large join query into steps, using the results of the one query (involving joins on some of the tables) for the next join query (involving joins on the other tables) reduces the multiplication effect.
I will try to provide better explanation to this later.