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'
Related
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
I have a problem with a search post on my forum.
1) I split search string using explode(" ", $string);
2) I search all tag_id where tag_value contains word - SELECT tag_id WHERE tag_value like '%{word}%'; all results are added to array $tags_array
3) I created INNER JOIN
foreach ($tags_array as $k => $v) {
if (!empty($v)) {
$short_name = "frt_".$i++;
$inner_array[] = " INNER JOIN forum_rel_tags as ".$short_name." ON (".$short_name.".tag_id IN (".implode(", ", $v).")) ";
$more[] = " ".$short_name.".post_id = fp.post_id ";
}
}
4) and finally I have something like:
SELECT COUNT(fp.post_id) AS total
FROM forum_categories c
JOIN forum_thread t USE INDEX (thread_id)
ON t.category_id = c.category_id
AND t.posts_limit <= 1
AND t.contest_limit <= 3
AND (t.register_limit*86400) <= 5841372
JOIN forum_auth a
ON a.auth_id = c.category_id
AND a.auth_group_id = 1
AND a.auth_type = 1
AND a.auth_visible = 1
JOIN forum_posts fp
ON fp.thread_id = t.thread_id
AND fp.post_deleted = 0
JOIN forum_rel_tags frt_0
ON frt_0.post_id = fp.post_id
AND frt_0.tag_id IN (1000 tag_id)
JOIN forum_rel_tags frt_1
ON frt_1.post_id = fp.post_id
AND frt_1.tag_id IN (200 tag_id)
JOIN forum_rel_tags frt_2
ON frt_2.post_id = fp.post_id
AND frt_2.tag_id IN (432 tag_id)
JOIN forum_rel_tags frt_3
ON frt_3.post_id = fp.post_id
AND frt_3.tag_id IN (50 tag_id)
But this query is very slow.
What can I change to make it faster?
EXPLAIN SELECT
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t ref thread_id thread_id 4 const 1 Using where; Using index
1 SIMPLE c eq_ref PRIMARY PRIMARY 4 pionas.t.category_id 1 Using index
1 SIMPLE a eq_ref auth_type auth_type 10 const,pionas.c.category_id,const 1 Using where
1 SIMPLE frt_0 range post_id,tag_id tag_id 4 NULL 372226 Using where; Using join buffer
1 SIMPLE frt_1 range post_id,tag_id tag_id 4 NULL 37787 Using where; Using join buffer
1 SIMPLE fp eq_ref PRIMARY,thread_id,post_deleted PRIMARY 4 pionas.frt_1.post_id 1 Using where
1 SIMPLE u eq_ref PRIMARY PRIMARY 4 pionas.fp.user_id 1 Using index
1 SIMPLE frt_3 range post_id,tag_id tag_id 4 NULL 23608 Using where; Using join buffer
1 SIMPLE frt_2 ref post_id,tag_id post_id 4 pionas.frt_3.post_id 296144 Using where; Using index
Tables
CREATE TABLE `forum_auth` (
`auth_type` smallint(1) unsigned NOT NULL DEFAULT '0',
`auth_id` int(10) unsigned NOT NULL,
`auth_visible` int(10) unsigned NOT NULL,
`auth_group_id` int(10) unsigned NOT NULL,
`auth_last_post_id` int(11) unsigned NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `forum_auth`
ADD UNIQUE KEY `auth_type` (`auth_type`,`auth_id`,`auth_group_id`);
CREATE TABLE `forum_categories` (
`category_id` int(10) unsigned NOT NULL,
`category_name` varchar(200) NOT NULL,
`category_desc` text NOT NULL,
`category_order` tinyint(2) NOT NULL,
`category_parent` int(10) unsigned NOT NULL DEFAULT '0',
`last_post_id` int(11) NOT NULL,
`total_thread` int(11) NOT NULL,
`total_posts` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `forum_categories`
ADD PRIMARY KEY (`category_id`);
CREATE TABLE `forum_posts` (
`post_id` int(10) unsigned NOT NULL,
`post_subject` varchar(250) NOT NULL,
`post_message` text NOT NULL,
`post_create_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`post_last_modify` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`post_count_modify` int(11) NOT NULL,
`post_deleted` tinyint(1) NOT NULL DEFAULT '0',
`post_ip` int(11) NOT NULL,
`user_id` int(10) NOT NULL,
`thread_id` int(10) unsigned NOT NULL,
`guest_name` varchar(100) NOT NULL,
`guest_mail` varchar(150) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `forum_posts`
ADD PRIMARY KEY (`post_id`),
ADD KEY `thread_id` (`thread_id`),
ADD KEY `user_id` (`user_id`),
ADD KEY `post_deleted` (`thread_id`,`post_deleted`),
ADD KEY `post_create_date` (`post_create_date`),
ADD FULLTEXT KEY `post_message` (`post_message`);
CREATE TABLE `forum_rel_tags` (
`post_id` int(10) unsigned NOT NULL,
`tag_id` int(10) unsigned NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `forum_rel_tags`
ADD UNIQUE KEY `post_id` (`post_id`,`tag_id`),
ADD KEY `tag_id` (`tag_id`);
CREATE TABLE `forum_tags` (
`tag_id` int(10) unsigned NOT NULL,
`name` varchar(150) NOT NULL,
`tag_url` varchar(150) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `forum_tags`
ADD PRIMARY KEY (`tag_id`),
ADD UNIQUE KEY `tag_url` (`tag_url`);
CREATE TABLE `forum_thread` (
`thread_id` int(10) unsigned NOT NULL,
`thread_subject` varchar(250) CHARACTER SET utf8 NOT NULL,
`thread_desc` text CHARACTER SET utf8 NOT NULL,
`thread_create_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`thread_view` int(10) unsigned NOT NULL DEFAULT '0',
`thread_reply` int(10) unsigned NOT NULL DEFAULT '0',
`thread_ip` int(10) unsigned NOT NULL,
`last_post_id` int(11) NOT NULL,
`user_id` varchar(100) CHARACTER SET utf8 NOT NULL,
`category_id` int(10) unsigned NOT NULL,
`guest_name` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
`guest_mail` varchar(150) COLLATE utf8_unicode_ci DEFAULT NULL,
`guest_can_reply` smallint(1) NOT NULL DEFAULT '0',
`thread_block` tinyint(1) NOT NULL,
`thread_sticky` tinyint(1) unsigned NOT NULL DEFAULT '0',
`posts_limit` int(2) NOT NULL DEFAULT '0',
`register_limit` int(3) NOT NULL DEFAULT '0',
`contest_limit` int(2) NOT NULL DEFAULT '0'
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
ALTER TABLE `forum_thread`
ADD PRIMARY KEY (`thread_id`),
ADD KEY `category_id` (`category_id`),
ADD KEY `thread_id` (`thread_id`,`category_id`,`posts_limit`,`register_limit`,`contest_limit`);
Second query:
SELECT fp.*, u.username, u.rang, u.post, u.date_register, u.avatar, u.pkt, f.city
FROM forum_categories c
JOIN forum_thread t USE INDEX (thread_id)
ON t.category_id = c.category_id
AND t.posts_limit <= 1
AND t.contest_limit <= 3
AND (t.register_limit*86400) <= 5841372
JOIN forum_auth a
ON a.auth_id = c.category_id
AND a.auth_group_id = 1
AND a.auth_type = 1
AND a.auth_visible = 1
JOIN forum_posts fp
ON fp.thread_id = t.thread_id
AND fp.post_deleted = 0
JOIN forum_rel_tags frt_0
ON frt_0.post_id = fp.post_id
AND frt_0.tag_id IN (1000 tag_id)
JOIN forum_rel_tags frt_1
ON frt_1.post_id = fp.post_id
AND frt_1.tag_id IN (200 tag_id)
JOIN forum_rel_tags frt_2
ON frt_2.post_id = fp.post_id
AND frt_2.tag_id IN (432 tag_id)
JOIN forum_rel_tags frt_3
ON frt_3.post_id = fp.post_id
AND frt_3.tag_id IN (50 tag_id)
LEFT JOIN users u
ON fp.user_id=u.user_id
LEFT JOIN users_field f
ON u.user_id=f.user_id
GROUP BY fp.post_id
ORDER BY fp.post_id DESC
LIMIT 0,30;
[EDIT]
What you think about it:
EXPLAIN SELECT fp.*, u.username, u.rang, u.post, u.date_register, u.avatar, u.pkt, f.city
FROM forum_categories c
JOIN forum_thread t USE INDEX (thread_id)
ON t.category_id = c.category_id
AND t.posts_limit <= 1
AND t.contest_limit <= 3
AND t.register_limit <= (5841372/86400)
JOIN forum_auth a
ON a.auth_id = c.category_id
AND a.auth_group_id = 1
AND a.auth_type = 1
AND a.auth_visible = 1
JOIN forum_posts fp
ON fp.thread_id = t.thread_id
AND fp.post_deleted = 0
JOIN forum_rel_tags frt_0
ON frt_0.post_id = fp.post_id
JOIN forum_tags ft_0
ON ft_0.tag_id = frt_0.tag_id
AND ft_0.tag_url like '%play%'
JOIN forum_rel_tags frt_1
ON frt_0.post_id = frt_1.post_id
JOIN forum_tags ft_1
ON ft_1.tag_id = frt_1.tag_id
AND ft_1.tag_url like '%how%'
JOIN forum_rel_tags frt_2
ON frt_1.post_id = frt_2.post_id
JOIN forum_tags ft_2
ON ft_2.tag_id = frt_2.tag_id
AND ft_2.tag_url like '%win%'
JOIN forum_rel_tags frt_3
ON frt_2.post_id = frt_3.post_id
JOIN forum_tags ft_3
ON ft_3.tag_id = frt_3.tag_id
AND ft_3.tag_url like '%to%'
LEFT JOIN users u
ON fp.user_id=u.user_id
LEFT JOIN users_field f
ON u.user_id=f.user_id
GROUP BY fp.post_id
ORDER BY fp.post_id DESC
Assuming that user_id is unique in the users table, the LEFT JOIN to users table is unnecessary.
There are four joins to the forum_rel_tags table; there seems to be a potential there for multiple rows to be be returned from each join; and each of those rows is going to get "matched" to the rows returned from the other joins... which is a a partial cross product i.e. the same fp.post_id can be "counted" multiple times.
The most likely explanation for poor performance is the absence of suitable indexes.
Some predicates e.g. (t.register_limit*86400)<=5841372 cannot make use of an range scan operation on an index... the expression on t.register_limit*86400 has to be evaluated for every row (that isn't excluded by some other predicate) before the comparison to the literal can be made. We typically prefer to reference a bare column in the predicate, where it's possible to do that to return an equivalent result... e.g.
t.register_limit <= (584137/86400)
We can re-write the query, removing the unnecessary join to the users table, to something like this:
SELECT COUNT(fp.post_id) AS total
FROM forum_categories c
JOIN forum_thread t
ON t.category_id = c.category_id
AND t.posts_limit <= 1
AND t.contest_limit <= 3
AND (t.register_limit*86400) <= 5841372
JOIN forum_auth a
ON a.auth_id = c.category_id
AND a.auth_group_id = 1
AND a.auth_type = 1
AND a.auth_visible = 1
JOIN forum_posts fp
ON fp.thread_id = t.thread_id
AND fp.post_deleted = 0
JOIN forum_rel_tags frt_0
ON frt_0.post_id = fp.post_id
AND frt_0.tag_id IN (1000 tag_id)
JOIN forum_rel_tags frt_1
ON frt_1.post_id = fp.post_id
AND frt_1.tag_id IN (200 tag_id)
JOIN forum_rel_tags frt_2
ON frt_2.post_id = fp.post_id
AND frt_2.tag_id IN (432 tag_id)
JOIN forum_rel_tags frt_3
ON frt_3.post_id = fp.post_id
AND frt_3.tag_id IN (50 tag_id)
This makes it much easier to decipher the query, especially in terms of figuring out what indexes are likely to be the most appropriate for this query.
... ON forum_thread (category_id, posts_limit, contest_limit, register_limit)
... ON forum_auth (auth_id, auth_group, auth_type, auth_visible)
... ON forum_posts (thread_id, post_deleted, post_id)
... ON forum_rel_tags (post_id, tag_id)
Since these are covering indexes for the query, we expect the EXPLAIN output to include "Using index" for these tables. This is just a first cut at some indexes; these may already exist, or there may be more appropriate ordering of the columns in the indexes.
But absent table definitions, including indexes and estimated cardinalities, it's not really possible to give a more definitive answer.
Again...
I'm very suspicious of the partial cross product between the references to forum_rel_tags table. I suspect that this query can return a value for "total" that is higher than is expected. This is just a suspicion, because I don't see any specification (beyond the query) as to what result is supposed to be returned.
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
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
);
I have two tables that looks like this;
Table: pictures
`picture_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`picture_title` varchar(255) NOT NULL,
`picture_description` text NOT NULL,
`picture_src` varchar(50) NOT NULL,
`picture_filetype` varchar(10) NOT NULL,
`picture_width` int(11) NOT NULL,
`picture_height` int(11) NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`upload_date` datetime NOT NULL,
--
Table: picture_votes
`vote_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`picture_id` int(10) unsigned NOT NULL,
`vote` tinyint(4) NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`timestamp` datetime NOT NULL,
And what I want to do is to select every field from pictures table and then a count of all the records in picture_votes where pictures.picture_id = picture_votes.picture_id, like for example;
picture_id => 1
picture_title => 'Pic title'
picture_description => 'Pic description'
picture_src => 'b8b3f2c3a85f1a46fbf2ee132d81f783'
picture_filetype => 'jpg'
picture_width => 612
picture_height => 612
user_id => 1
upload_date => '2013-10-12 12:00:00'
vote_count => 3 // Amount of records in `picture_votes` that has `picture_id` = 1
I've come up with (where $limit is the amount of pictures to select);
SELECT pictures.*, count(picture_votes.vote) as vote_count
FROM pictures, picture_votes
WHERE pictures.picture_id = picture_votes.picture_id
ORDER BY upload_date DESC
LIMIT $limit
This selects only 1 picture and a count of all records in picture_votes.
You really want to use LEFT join, because that will return all the pictures, not just the ones with a vote. You also should do sum(pv.vote) vs COUNT() incase your votes ever are more than 1 (hey, it can happen! Just think: Premium account == x2 votes ;-)
SELECT p.*, SUM(pv.vote) votes FROM pictures p
LEFT JOIN picture_votes pv
ON pv.picture_id=p.picture_id
GROUP BY pv.picture_id
If you ever wanted to sort by, say, top 10 vote count:
SELECT * FROM (
SELECT p.*, SUM(pv.vote) votes FROM pictures p
LEFT JOIN picture_votes pv
ON pv.picture_id=p.picture_id
GROUP BY pv.picture_id
) AS aggregate
ORDER BY votes DESC
LIMIT 10;
If picture_id is indexed on your picture_votes table, then the following may be even faster than a join:
SELECT *
, ( SELECT COUNT(*) FROM picture_votes WHERE picture_id = pictures.picture_id )
FROM pictures
This may skip the contents of the table entirely and allow you to simply count the records in the hash table, which should be faster.