Query :
(
SELECT
upd.uid,
upd.update_id,
upd.update,
upd.date,
upd.type,
upd.total_likes,
upd.total_comments,
upd.deleted,
usr.username AS `username`,
usr.profile_picture AS `profile_picture`
,(
SELECT
COUNT(lik.id)
FROM
likes as lik
WHERE
upd.update_id = lik.item_id
AND
lik.uid = 118697835834
AND lik.type=0
) as liked_update,
(
SELECT
COUNT(fav.id)
FROM
favorites as fav
WHERE
upd.update_id = fav.item_id
AND
fav.uid = 118697835834
AND fav.type=0
) as favorited_update
FROM
updates AS upd
LEFT JOIN
users AS usr
ON upd.uid = usr.uid
WHERE
upd.deleted=0
AND
(
upd.uid=118697835834
OR EXISTS
(
SELECT *
FROM
subscribers AS sub
WHERE
upd.uid = sub.suid
AND sub.uid = 118697835834
)
)
)
UNION
(
SELECT
topic.uid,
topic.tid,
topic.title,
topic.body,
topic.total_likes,
topic.total_replies,
topic.views,
topic.date,
usr.username AS `username`,
usr.profile_picture AS `profile_picture`
,(
SELECT
COUNT(lik.id)
FROM
likes as lik
WHERE
topic.update_id = lik.item_id
AND
lik.uid = 118697835834
AND lik.type=1
) as liked_update,
(
SELECT
COUNT(fav.id)
FROM
favorites as fav
WHERE
topic.update_id = fav.item_id
AND
fav.uid = 118697835834
AND fav.type=1
) as favorited_update
FROM
topics AS topic
LEFT JOIN
users AS usr
ON topic.uid = usr.uid
WHERE
topic.deleted=0
AND
(
topic.uid=118697835834
OR EXISTS
(
SELECT *
FROM
subscribers AS sub
WHERE
topic.uid = sub.suid
AND sub.uid = 118697835834
)
)
)
ORDER BY date DESC
I have added the UNION ( SELECT ) and the query takes forever to load and finally it stops returning a blank page...
removing UNION ( SELECT ) works fine...
OK after all the comments can you please say what changes you made to get the script working.
In light of your comment about the data from two tables being in the same columns that is because you have told the engine to do that by using UNION. If you want them in seperate columns you will need to place Nulls in between:
SELECT
upd.uid,
upd.update_id,
upd.update,
upd.date,
upd.type,
upd.total_likes,
upd.total_comments,
upd.deleted,
null AS `topicuid`,
null AS `topictid`,
null AS `topictitle`,
null AS `topicbody`,
null AS `topictotal_likes`,
null AS `topictotal_replies`,
null AS `topicviews`,
null AS `topicdate`,
usr.username AS `username`,
usr.profile_picture AS `profile_picture`,
(SELECT COUNT(lik.id) FROM likes as lik WHERE upd.update_id = lik.item_id AND lik.uid = 118697835834 AND lik.type=0) as liked_update,
(SELECT COUNT(fav.id) FROM favorites as fav WHERE upd.update_id = fav.item_id AND fav.uid = 118697835834 AND fav.type=0) as favorited_update
FROM
......
UNION ALL
SELECT
null AS `upd.uid`,
null AS `upd.update_id`,
null AS `upd.update`,
null AS `upd.date`,
null AS `upd.type`,
null AS `upd.total_likes`,
null AS `upd.total_comments`,
null AS `upd.deleted`,
topic.uid,
topic.tid,
topic.title,
topic.body,
topic.total_likes,
topic.total_replies,
topic.views,
topic.date,
usr.username AS `username`,
usr.profile_picture AS `profile_picture`,
(SELECT COUNT(lik.id) FROM likes as lik WHERE topic.update_id = lik.item_id AND lik.uid = 118697835834 AND lik.type=1) as liked_update,
(SELECT COUNT(fav.id) FROM favorites as fav WHERE topic.update_id = fav.item_id AND fav.uid = 118697835834 AND fav.type=1) as favorited_update
You might need to do a bit more work on the column names but you should get there. You should read up on what the UNION operator actually does to two or more queries.
When you use UNION you have to have exact same results "column format, names,..." so you have at top of query upd.total_comments as total_comments, and bottom you have topic.total_replies as total_replies those are different names. hope you understand what i try to explain, if not visit here where it explains better.
Related
I've a SELECT which checks a status of active alarms (icinga).
This select joins different tables and until here all ok.
On the result I've as value/column an object_id as well. I would like to add a column to that select that could be empty or not, because, searching that 'object_id' on a different table, I could get a value or not. This accessory table is structured having: object_id, varname, varvalue.
So, i.e., my SELECT returns those values:
`name`, `object_id`, `status`
`Hello`, `123456`, `OK`
I would add the column City that should compared to a table having:
`object_id`, `varname`, `varvalue`
`123456`, `city`, `Rome`
`123456`, `lake`, `Garda`
`789789`, `city`, `Milano`
So that if the second table has object_id = 123456 AND city = Rome the result should be:
`name`, `object_id`, `status`, `city`
`Hello`, `123456`, `OK`, `Rome`
Otherwise the result should be:
`Hello`, `123456`, `OK`, `UNKNOWN`
How to do that?
Hope I've explained it well :-)
Thanks!
* EDIT *
It's better I explain with real example. My query actually is the following:
select icinga_objects.object_id, icinga_objects.name1 as host_name, icinga_objects.name2 as ServiceName, "service" as Type, icinga_servicestatus.last_check as LastCheckTime, icinga_servicestatus.last_hard_state_change as LastStateChange, TIMEDIFF(now(), icinga_servicestatus.last_hard_state_change) AS SinceTime,
CASE
WHEN icinga_servicestatus.current_state = 0 THEN '0'
WHEN icinga_servicestatus.current_state = 1 THEN '2'
WHEN icinga_servicestatus.current_state = 2 THEN '3'
ELSE '3'
END AS state
FROM icinga_objects, icinga_servicestatus, icinga_services WHERE icinga_servicestatus.service_object_id IN
(SELECT service_object_id FROM icinga_services WHERE icinga_services.host_object_id IN
(SELECT host_object_id FROM icinga_hostgroup_members WHERE hostgroup_id IN
(SELECT hostgroup_id FROM icinga_hostgroups WHERE alias = 'MY-HOSTGROUP-TO-FILTER')
)
)
AND icinga_servicestatus.service_object_id NOT IN
(SELECT service_object_id FROM icinga_services WHERE icinga_services.service_object_id IN (
SELECT object_id FROM icinga_objects WHERE icinga_objects.is_active = 1 AND icinga_objects.object_id IN
(SELECT object_id FROM icinga_customvariables WHERE varvalue = '8x5')
)
)
AND icinga_servicestatus.last_check > NOW() - INTERVAL 3 HOUR
AND icinga_servicestatus.state_type = 1
AND icinga_servicestatus.scheduled_downtime_depth = 0
AND icinga_objects.object_id = icinga_services.service_object_id
AND icinga_servicestatus.service_object_id = icinga_services.service_object_id
AND icinga_servicestatus.current_state = 2
AND icinga_servicestatus.problem_has_been_acknowledged = 0
This gives me as result, in example:
`object_id`, `host_name`, `ServiceName`, `Type`, `LastCheckTime`, `LastStateChange`, `SinceTime`, `State`
`123456`, `myHostName`, `myServiceName`, `service`, `2020-04-29 17:19:21`, `2020-04-28 14:50:27`, `26:32:51`, `3`
Here I would like to add the column.
So, now if I search object_id into icinga_customvariables I could find entries, or not. In Example, searching object_id = 123456 I have 4 records, but ONLY one having varname = NAME_IM_SEARCHING and so I need to add to the above result the corresponding of varvalue searching icinga_customvariables.object_id = '123456' AND varname = NAME_IM_SEARCHING. IF there are NO results, then the added cloumn should be UNKNOWN, otherwise the added column should be = icinga_customvariables.varvalue.
How to add it? :-)
You can place your query into a "table expression" so it becomes simpler to join it to the other_table. For example:
select
q.*,
coalesce(o.varvalue, 'UNKNOWN') as city
from (
-- your existing query here
) q
left join other_table o on o.object_id = q.object_id and o.varname = 'city'
EDIT: Joining multiple times
As requested if you need to extract more city names using another column, or if you want to extract against another table altogether, you can add an extra LEFT JOIN. For example:
select
q.*,
coalesce(o.varvalue, 'UNKNOWN') as city,
coalesce(o2.varvalue, 'UNKNOWN') as lake
from (
-- your existing query here
) q
left join other_table o on o.object_id = q.object_id and o.varname = 'city'
left join other_table o2 on o.object_id = q.object_id and o2.varname = 'lake'
I'm using a query various IN() clauses to get the price of product combinations. I am actually correctly getting the prices, so thats OK. But I also need some other ID's to identify to which ID's the prices belong.
As you can see IN's, they all start with a different bundle_variant_id's but the rest of the bundle_variant_id's are the same. I want to return that first (or all) of those bundle_variant_id's in the result too, but using this query, i always get '1620' as bundle_variant_id. How can i get the first bundle_variant_id in those IN's (1616, 1655, 1677, etc) or ALL of the bundle_variant_id's?
It's not an option to remove the GROUP and the HAVING or any of the WHERE clauses.
SELECT `HardwareProductSubscriptionInstanceLink`.`subscription_instance_id`
,`BundleVariantLink`.`bundle_variant_id`
,`HardwareProductSubscriptionInstanceLink`.`price`
FROM `wax`.`prd_providers` AS `Provider`
INNER JOIN `wax`.`prd_subscription_types` AS `SubscriptionType` ON (`SubscriptionType`.`provider_id` = `Provider`.`id`)
INNER JOIN `wax`.`prd_subscriptions` AS `Subscription` ON (`Subscription`.`subscription_type_id` = `SubscriptionType`.`id`)
INNER JOIN `wax`.`prd_subscription_instances` AS `SubscriptionInstance` ON (`SubscriptionInstance`.`subscription_id` = `Subscription`.`id`)
INNER JOIN `wax`.`prd_bundle_variants_subscription_instances` AS `BundleVariantLink` ON (`BundleVariantLink`.`subscription_instance_id` = `SubscriptionInstance`.`id`)
INNER JOIN `wax`.`prd_hardware_products_subscription_instances` AS `HardwareProductSubscriptionInstanceLink` ON (
`HardwareProductSubscriptionInstanceLink`.`subscription_instance_id` = `SubscriptionInstance`.`id`
AND `HardwareProductSubscriptionInstanceLink`.`hardware_product_id` = 317
)
WHERE (
(
`BundleVariantLink`.`bundle_variant_id` IN (
'1616'
,'1618'
,'1600'
,'1620'
)
)
OR (
`BundleVariantLink`.`bundle_variant_id` IN (
'1655'
,'1618'
,'1600'
,'1620'
)
)
OR (
`BundleVariantLink`.`bundle_variant_id` IN (
'1677'
,'1618'
,'1600'
,'1620'
)
)
OR (
`BundleVariantLink`.`bundle_variant_id` IN (
'1691'
,'1618'
,'1600'
,'1620'
)
)
AND `SubscriptionInstance`.`number_of_bundle_variants` = 4
AND (
(`Provider`.`enabled` = '1')
AND (`Provider`.`visible` = '1')
)
GROUP BY `SubscriptionInstance`.`id`
HAVING count(`BundleVariantLink`.`bundle_variant_id`) = 4
Current results:
(column 1 = subscription_instance_id, 2=bundle_variant_id,3=price)
9213 1620 331.405
9214 1620 311.57
9215 1620 291.736
9219 1620 390.909
Required results:
9213 1616 331.405
9214 1655 311.57
9215 1677 291.736
9219 1691 390.909
Or alternate required results:
9213 1616,1618,1600,1620 331.405
9214 1655,1618,1600,1620 311.57
9215 1677,1618,1600,1620 291.736
9219 1691,1618,1600,1620 390.909
I am confused here This first sql-
I have one entry in my cnt_content table with content_pk=5419441 and title=test wls-2
Now when I am running this sql 1-
SELECT
title,
content_pk
FROM
cnt_content c
WHERE
c.type not in (
'AMT', 'LPA', 'QUE'
)
AND c.private_flag = 'N'
AND c.searchable_flag = 'Y'
AND c.status in (
'PUB', 'NAP'
)
AND (
trim(c.title) LIKE 'test wls-2'
OR trim(c.special_keyword) LIKE 'test wls-2'
)
and not exists (
SELECT
1
FROM
cmd_content_metadata cmd
WHERE
cmd.content_pk=5419441
AND cmd.for_others='Y'
)
ORDER BY
c.last_modified_date desc;
it is giving result-content_pk=5419441 and title=test wls-2
But when I am running this sql 2-
SELECT
title,
content_pk
FROM
cnt_content c
WHERE
c.type not in (
'AMT', 'LPA', 'QUE'
)
AND c.private_flag = 'N'
AND c.searchable_flag = 'Y'
AND c.status in (
'PUB', 'NAP'
)
AND (
trim(c.title) LIKE 'test wls-2'
OR trim(c.special_keyword) LIKE 'test wls-2'
)
and not exists (
SELECT
1
FROM
cmd_content_metadata cmd
WHERE
cmd.content_pk=c.content_pk
AND cmd.for_others='Y'
)
ORDER BY
c.last_modified_date desc;
It is not giving any result. Anyone please explain.
Because in one you just giving specific value for content_pk as 5419441, while other one uses corelated query which means for each and every value of content_pk selected in outer query, you are checking for equality in your sub select query.
I have 4 tables:
Table talks
table talks_fan
table talks_follow
table talks_comments
What I'm trying to achieve is counting all comments, fans, followers for every single talk.
I came up with this so far.
All tables have talk_id and only in talks table is a primary key
SELECT
g. *,
COUNT( m.talk_id ) AS num_of_comments,
COUNT( f.talk_id ) AS num_of_followers
FROM
talks AS g
LEFT JOIN talks_comments AS m
USING ( talk_id )
LEFT JOIN talks_follow AS f
USING ( talk_id )
WHERE g.privacy = 'public'
GROUP BY g.talk_id
ORDER BY g.created_date DESC
LIMIT 30;
I also tried using this method
SELECT
t.*,
COUNT(b.talk_id) AS comments,
COUNT(bt.talk_id) AS followers
FROM
talks t
LEFT JOIN talks_follow bt
ON bt.talk_id = t.talk_id
LEFT JOIN talks_comments b
ON b.talk_id = t.talk_id
GROUP BY t.talk_id;
Both give me the same results ....?!
Update: Create Statements
CREATE TABLE IF NOT EXISTS `talks` (
`talk_id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` mediumint(9) NOT NULL,
`title` varchar(255) NOT NULL,
`content` text NOT NULL,
`created_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`privacy` enum('public','private') NOT NULL DEFAULT 'private',
PRIMARY KEY (`talk_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;
CREATE TABLE IF NOT EXISTS `talks_comments` (
`comment_id` bigint(20) NOT NULL AUTO_INCREMENT,
`talk_id` bigint(20) NOT NULL,
`user_id` mediumint(9) NOT NULL,
`comment` text NOT NULL,
`date_created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`status` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`comment_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=8 ;
CREATE TABLE IF NOT EXISTS `talks_fan` (
`fan_id` bigint(20) NOT NULL AUTO_INCREMENT,
`talk_id` bigint(20) NOT NULL,
`user_id` bigint(20) NOT NULL,
`created_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`status` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`fan_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;
CREATE TABLE IF NOT EXISTS `talks_follow` (
`follow_id` bigint(20) NOT NULL AUTO_INCREMENT,
`talk_id` bigint(20) NOT NULL,
`user_id` mediumint(9) NOT NULL,
`date_created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`follow_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;
The final query that works
SELECT t.* , COUNT( DISTINCT b.comment_id ) AS comments,
COUNT( DISTINCT bt.follow_id ) AS followers,
COUNT( DISTINCT c.fan_id ) AS fans
FROM talks t
LEFT JOIN talks_follow bt ON bt.talk_id = t.talk_id
LEFT JOIN talks_comments b ON b.talk_id = t.talk_id
LEFT JOIN talks_fan c ON c.talk_id = t.talk_id
WHERE t.privacy = 'public'
GROUP BY t.talk_id
ORDER BY t.created_date DESC
LIMIT 30
EDIT: Final answer to the whole issue...
I have modified the Query and created some code in PHP (Codeigniter) to solve my issue apone the reccomendation of #Bill Karwin
$sql="
SELECT t.*,
COUNT( DISTINCT b.comment_id ) AS comments,
COUNT( DISTINCT bt.follow_id ) AS followers,
COUNT( DISTINCT c.fan_id ) AS fans,
GROUP_CONCAT( DISTINCT c.user_id ) AS list_of_fans
FROM talks t
LEFT JOIN talks_follow bt ON bt.talk_id = t.talk_id
LEFT JOIN talks_comments b ON b.talk_id = t.talk_id
LEFT JOIN talks_fan c ON c.talk_id = t.talk_id
WHERE t.privacy = 'public'
GROUP BY t.talk_id
ORDER BY t.created_date DESC
LIMIT 30
";
$query = $this->db->query($sql);
if($query->num_rows() > 0)
{
$results = array();
foreach($query->result_array() AS $talk){
$fan_user_id = explode(",", $talk['list_of_fans']);
foreach($fan_user_id AS $user){
if($user == 1 /* this supposed to be user id or session*/){
$talk['list_of_fans'] = 'yes';
}
}
$follower_user_id = explode(",", $talk['list_of_follower']);
foreach($follower_user_id AS $user){
if($user == 1 /* this supposed to be user id or session*/){
$talk['list_of_follower'] = 'yes';
}
}
$results[] = array(
'talk_id' => $talk['talk_id'],
'user_id' => $talk['user_id'],
'title' => $talk['title'],
'created_date' => $talk['created_date'],
'comments' => $talk['comments'],
'followers' => $talk['followers'],
'fans' => $talk['fans'],
'list_of_fans' => $talk['list_of_fans'],
'list_of_follower' => $talk['list_of_follower']
);
}
}
I STILL BELIEVE IT COULD BE OPTIMIZED IN THE DB AND JUST USE THE RESULT...
Im thinking if there are 1000 follower and 2000 fans of every single TALK then the result will take much longer to load.. HOW IF YOUT MULTIPLY THE NO WITH 10. Or im mistaking hear...
EDIT: adding benchmark for the query test...
I have used codeigniter profiler to know how long it take for the query to finish excuting.
that been said i also start adding data in the tables gratually
the result as follows.
Testing the DB after answerting data into it
Query Results time
table Talks
---------------
table data 50 rows.
Time: 0.0173 seconds
Table Rows: 644 rows
Time: 0.0535 seconds
Table Rows: 1250 rows
Time: 0.0856 seconds
Adding data to other tables
--------------------------
Talks = 1250 rows
talks_follow = 4115
talks_fan = 10 rows
Time: 2.656 seconds
Adding data to other tables
--------------------------
Talks = 1250 rows
talks_follow = 4115
talks_fan = 10 rows
talks_comments = 3650 rows
Time: 10.156 seconds
After replacing LEFT JOIN with STRAIGHT_JOIN
Time: 6.675 seconds
It seems that its extremely heavy on the DB.....
NOW Im Going to another dilemma on how to enhance its performance
Edited: using #leonardo_assumpcao suggestion
After rebuilding the DB using #leonardo_assumpcao suggestion
for indexing few fields..........
Adding data to other tables
--------------------------
Talks = 6000 Rows
talks_follow = 10000 Rows
talks_fan = 10000 Rows
talks_comments = 10000 Rows
Time: 17.940 second
Is this normal for heavy data DB......?
I can say this is (at least) one of the coolest select statements I improved today.
SELECT STRAIGHT_JOIN
t.* ,
COUNT( DISTINCT b.comment_id ) AS comments,
COUNT( DISTINCT bt.follow_id ) AS followers,
COUNT( DISTINCT c.fan_id ) AS fans
FROM
(
SELECT * FROM talks
WHERE privacy = 'public'
ORDER BY created_date DESC
LIMIT 0, 30
) AS t
LEFT JOIN talks_follow bt ON (bt.talk_id = t.talk_id)
LEFT JOIN talks_comments b ON (b.talk_id = t.talk_id)
LEFT JOIN talks_fan c ON (c.talk_id = t.talk_id)
GROUP BY t.talk_id ;
But it seems to me that your problem resides on your tables; A first step to obtain efficient queries is to index every field involved on your desired joins.
I've made some modifications on the tables you shown above; You can see its code here (updated).
Quite interesting, isn't it? Since we're here, take also your ERR model:
First try it using MySQL test database. Hopefully it will solve your performance troubles.
(Forgive my english, it's my second language)
You can force this into one query like so:
SELECT COUNT(*) num, 'talks' item FROM talks
UNION
SELECT COUNT(*) num, 'talks_fan' item FROM talks_fan
UNION
SELECT COUNT(*) num, 'talks_follow' item FROM talks_follow
UNION
SELECT COUNT(*) num, 'talks_comment' item FROM talks_comment
This will give you a five row resultset with one row per table. Each row is the count in a particular table.
If you must get it all into a single row you can do a pivot like so.
SELECT
SUM( CASE item WHEN 'talks' THEN num ELSE 0 END ) AS 'talks',
SUM( CASE item WHEN 'talks_fan' THEN num ELSE 0 END ) AS 'talks_fan',
SUM( CASE item WHEN 'talks_follow' THEN num ELSE 0 END ) AS 'talks_follow',
SUM( CASE item WHEN 'talks_comment' THEN num ELSE 0 END ) AS 'talks_comment'
FROM
( SELECT COUNT(*) num, 'talks' item FROM talks
UNION
SELECT COUNT(*) num, 'talks_fan' item FROM talks_fan
UNION
SELECT COUNT(*) num, 'talks_follow' item FROM talks_follow
UNION
SELECT COUNT(*) num, 'talks_comment' item FROM talks_comment
) counts
(This doesn't take into account your WHERE g.privacy = clause because I don't understand that. But you could add a WHERE clause to one one of the four queries in the UNION item to handle that.)
Notice that this truly is four queries on four separate tables coerced into a single query.
And, by the way, there is no difference in value between COUNT(*) and COUNT(id) when id is the primary key of the table. COUNT(id) doesn't count the rows for which the id is NULL, but if id is the primary key, then it is NOT NULL. But COUNT(*) is faster, so use it.
Edit if you need the number of fan, follow, and comment rows for each distinct talk, do this. It's the same idea of doing a union and a pivot, but with an extra parameter.
SELECT
talk_id,
SUM( CASE item WHEN 'talks_fan' THEN num ELSE 0 END ) AS 'talks_fan',
SUM( CASE item WHEN 'talks_follow' THEN num ELSE 0 END ) AS 'talks_follow',
SUM( CASE item WHEN 'talks_comment' THEN num ELSE 0 END ) AS 'talks_comment'
FROM
(
SELECT talk_id, COUNT(*) num, 'talks_fan' item
FROM talks_fan
GROUP BY talk_id
UNION
SELECT talk_id, COUNT(*) num, 'talks_follow' item
FROM talks_follow
GROUP BY talk_id
UNION
SELECT talk_id, COUNT(*) num, 'talks_comment' item
FROM talks_comment
GROUP BY talk_id
) counts
GROUP BY talk_id
After doing this for (too) many years, I've discovered that the best way to describe a query you need is to say to yourself "I need a result set with one row for each xxx, with columns for yyy, zzz, and qqq."
The reason the counts are the same is that it's counting rows after the joins have combined the tables. By joining to multiple tables, you're creating a Cartesian product.
Basically, you're counting not only how many comments per talk, but how many comments * followers per talk. Then you count the followers as how many followers * comments per talk. Thus the counts are the same, and they're all way too high.
Here's a simpler way to write a query to count each distinct comment, follower, etc. only once:
SELECT t.*,
COUNT(DISTINCT b.comment_id) AS comments,
COUNT(DISTINCT bt.follow_id) AS followers
FROM talks t
LEFT JOIN talks_follow bt ON bt.talk_id = t.talk_id
LEFT JOIN talks_comments b ON b.talk_id = t.talk_id
GROUP BY t.talk_id;
Re your comment: I wouldn't fetch all the followers in the same query. You could do it this way:
SELECT t.*,
COUNT(DISTINCT b.comment_id) AS comments,
COUNT(DISTINCT bt.follow_id) AS followers,
GROUP_CONCAT(DISTINCT bt.follower_name) AS list_of_followers
FROM talks t
LEFT JOIN talks_follow bt ON bt.talk_id = t.talk_id
LEFT JOIN talks_comments b ON b.talk_id = t.talk_id
GROUP BY t.talk_id;
But what you'd get back is a single string with the follower names separated by commas. Now you have to write application code to split the string on commas, you have to worry if some follower names actually contain commas already, and so on.
I'd do a second query, fetching the followers for a given talk. It's likely you want to display the followers only for a specific talk anyway.
SELECT follower_name
FROM talks_follow
WHERE talk_id = ?
I'm trying to find the number of notifications for each user, I am having a little problem, I had the query working perfect for what I needed it for, then I changed my table around just a little bit.
Working query:
$numNotifications = mysql_num_rows(mysql_query("
SELECT
N.*,
P.*
FROM
notifications N,
posts P
WHERE
N.userID='$session'
AND
(
N.action='1' OR N.action='2'
)
AND
N.uniqueID=P.id AND P.state='0'"
));
However, uniqueID is now different for some rows. when N.aciton is "1" then N.uniqueID should be compared to P.id, however when N.action is "2" it should compare a row in that table with P.id.
Example query, (that SHOULD work, but doesn't)
$numNotifications = mysql_num_rows(mysql_query("
SELECT
N.*,
P.*,
C.*,
(CASE WHEN (
N.action = 2 AND N.state = 0
)
THEN
C.postID ELSE N.uniqueID END
) AS postId
FROM
notifications N,
posts P,
comments C
WHERE
N.userID='$session'
AND
(
N.action='1' OR N.action='2'
)
AND
postId=P.id AND P.state='0'"
));
diagram of my 3 table structures:
http://i41.tinypic.com/nyzolg.png
here you go :)
SELECT
COUNT(`notif_id`) AS `number_of_new_notifications`
FROM
(
SELECT
`notifications`.`id` AS `notif_id`
FROM
`notifications`
JOIN
`posts`
ON
`notifications`.`uniqueID`=`posts`.`id`
WHERE
`notifications`.`userID`='$session'
AND
`notifications`.`action`=1
AND
`notifications`.`state`=0
AND
`posts`.`state`=0
UNION ALL
SELECT
`notifications`.`id` AS `notif_id`
FROM
`notifications`
JOIN
`comments`
ON
`notifications`.`uniqueID`=`comments`.`id`
JOIN
`posts`
ON
`comments`.`postID`=`posts`.`id`
WHERE
`notifications`.`userID`='$session'
AND
`notifications`.`action`=2
AND
`notifications`.`state`=0
AND
`comments`.`state`=0
AND
`posts`.`state`=0
) AS notification_ids;