SELECT B.name
FROM
(
SELECT BB.listing_id id,COUNT(1) taxon_count
FROM
(
SELECT id taxon_id FROM taxons
WHERE name IN ('Ford','Exhaust')
) AA
INNER JOIN listings_taxons BB
USING (taxon_id)
GROUP BY listing_id HAVING COUNT(1) = 2
) A
INNER JOIN listings B USING (id);
Subquery A will bring back all listing_ids that have Ford, Exhaust, or both. Doing the GROUP BY count within Subquery A gives any listing id that has a COUNT(1) of 2 has both Ford and Exhaust taxon ids becasue BB.listing_id would appears twice thus HAVING COUNT(1) = 2. Then Subquery A has an INNER JOIN with listings.
Make sure you have the following indexes
ALTER TABLE listings_taxons ADD INDEX taxon_listing_ndx (taxon_id,listing_id);
ALTER TABLE taxons ADD INDEX name_id_ndx (name,id);
Here is some sample data
drop database if exists nwwatson;
create database nwwatson;
use nwwatson
create table listings
(id int not null auto_increment,
name varchar(25),
primary key (id),
key (name));
create table taxons like listings;
create table listings_taxons
(
listing_id int,
taxon_id int,
primary key (listing_id,taxon_id),
unique key (taxon_id,listing_id)
);
insert into listings (name) values ('SteeringWheel'),('WindShield'),('Muffler'),('AC');
insert into taxons (name) values ('Ford'),('Escort'),('Buick'),('Exhaust'),('Mustard');
insert into listings_taxons values
(1,1),(1,3),(1,5),(2,1),(2,2),(2,3),(2,5),
(3,1),(3,4),(4,2),(4,3),(4,4),(5,1),(5,5);
SELECT * FROM listings;
SELECT * FROM taxons;
SELECT * FROM listings_taxons;
SELECT B.name
FROM
(
SELECT BB.listing_id id,COUNT(1) taxon_count
FROM
(
SELECT id taxon_id FROM taxons
WHERE name IN ('Ford','Exhaust')
) AA
INNER JOIN listings_taxons BB
USING (taxon_id)
GROUP BY listing_id HAVING COUNT(1) = 2
) A
INNER JOIN listings B USING (id);
Here is it executed
mysql> drop database if exists nwwatson;
Query OK, 3 rows affected (0.09 sec)
mysql> create database nwwatson;
Query OK, 1 row affected (0.00 sec)
mysql> use nwwatson
Database changed
mysql> create table listings
-> (
-> id int not null auto_increment,
-> name varchar(25),
-> primary key (id),
-> key (name)
-> );
Query OK, 0 rows affected (0.08 sec)
mysql> create table taxons like listings;
Query OK, 0 rows affected (0.05 sec)
mysql> create table listings_taxons
-> (
-> listing_id int,
-> taxon_id int,
-> primary key (listing_id,taxon_id),
-> unique key (taxon_id,listing_id)
-> );
Query OK, 0 rows affected (0.08 sec)
mysql> insert into listings (name) values ('SteeringWheel'),('WindShield'),('Muffler'),('AC');
Query OK, 4 rows affected (0.06 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> insert into taxons (name) values ('Ford'),('Escort'),('Buick'),('Exhaust'),('Mustard');
Query OK, 5 rows affected (0.06 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> insert into listings_taxons values
-> (1,1),(1,3),(1,5),(2,1),(2,2),(2,3),(2,5),
-> (3,1),(3,4),(4,2),(4,3),(4,4),(5,1),(5,5);
Query OK, 14 rows affected (0.11 sec)
Records: 14 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM listings;
+----+---------------+
| id | name |
+----+---------------+
| 4 | AC |
| 3 | Muffler |
| 1 | SteeringWheel |
| 2 | WindShield |
+----+---------------+
4 rows in set (0.00 sec)
mysql> SELECT * FROM taxons;
+----+---------+
| id | name |
+----+---------+
| 3 | Buick |
| 2 | Escort |
| 4 | Exhaust |
| 1 | Ford |
| 5 | Mustard |
+----+---------+
5 rows in set (0.00 sec)
mysql> SELECT * FROM listings_taxons;
+------------+----------+
| listing_id | taxon_id |
+------------+----------+
| 1 | 1 |
| 1 | 3 |
| 1 | 5 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 2 | 5 |
| 3 | 1 |
| 3 | 4 |
| 4 | 2 |
| 4 | 3 |
| 4 | 4 |
| 5 | 1 |
| 5 | 5 |
+------------+----------+
14 rows in set (0.00 sec)
mysql> SELECT B.name
-> FROM
-> (
-> SELECT BB.listing_id id,COUNT(1) taxon_count
-> FROM
-> (
-> SELECT id taxon_id FROM taxons
-> WHERE name IN ('Ford','Exhaust')
-> ) AA
-> INNER JOIN listings_taxons BB
-> USING (taxon_id)
-> GROUP BY listing_id HAVING COUNT(1) = 2
-> ) A
-> INNER JOIN listings B USING (id);
+---------+
| name |
+---------+
| Muffler |
+---------+
1 row in set (0.00 sec)
mysql>
Give it a Try !!!
Ok, so based on our conversation if you make sure that when a friend's request is created you also create an entry in the other direction: (i.e. Friend_A asks Friend_B, but you also need a relationship between Friend_B and Friend_A) then you can run the following query to obtain the results that you need:
select distinct
s.user_id,
u.username,
p.display_name,
p.first_name,
p.last_name,
s.message
from default_status as s
join default_friend as f on f.user_id = s.user_id
join default_users as u on u.id = s.user_id
join default_profiles as p on p.user_id = u.id
where (f.user_id = 1 or f.friend_id = 1)
and f.approved = 1
order by s.created_at desc;
This will also make it easier to obtain a list of all of a persons friend's:
select *
from default_users as u
join default_friends as f on f.friend_id = u.id
where f.user_id = 1;
Just remember to put an index over user_id and friend_id in the default friends_table otherwise you will find your query may take a while:
create unique index idx_friend on default_friend(user_id,friend_id);
I hope this helps you. ;-)
Best Answer
B-tree Index on Status won't help - it has 3 distinct values, and full table scan will be much faster. Bitmap index should work better in such case, but
TRANSACTION
table name implies lots of DML , so it shouldn't be used.Some improvement may be gained by partitioning transaction table by status, and then create local index on created_time. In a sense it's similar to (3), but don't require application changes, and moving rows to proper partition is done by the engine itself. However, it is quite an expensive option, and should be used for really huge databases .
In my opinion, index on
created_time
will work just fine for most of the cases.