I have this query below, and with just about 200k records in table, this query has started taking too long to execute. About 30 seconds or so.
I am not sure where or what is causing the problem.
I have other databases, with more than 2 million records, no issues of speed.
But somehow, for some reason, this query is causing problem on a site.
select p.pid, p.other_fields, c.user_name,
group_concat( t.tag ) as tags
from post_table as p, user_table as c, tag_table as t
where p.userID= c.userID
and p.stat=1
and p.mainID=0
and c.stat='y'
and t.pid=p.pid
group by p.pid
order by p.pid desc
limit 0, 20
This is the proper JOIN format of the same query, makes no difference, still slow.
This below is actually what I had earlier, but then changed it to the above older format, just to try if it makes any difference.
select p.pid, p.other_fields, c.user_name, group_concat( t.tag ) as tags
from post_table as p
LEFT JOIN user_table as c on p.userID = c.userID
LEFT JOIN tag_table as t on p.pid = t.pid
where p.stat=1
and p.mainID=0
and c.stat='y'
group by p.pid
order by p.pid desc
limit 0, 20
Structures and indexes on these tables:
post_table:
pid, userID, stat, mainID, title, other_fields...
index( userID, stat, mainID, title )
User_table:
userID, stat, user_name, pass_word, etc...
index( user_name, pass_word )
index( stat )
tag_table:
id, pid, tag
index( pid, tag )
I think I am following all indexes properly, but still the query takes lot of time to execute, and I don't know why.
Can someone please tell me what could be the reason?
Thanks
Below is the output of the EXPLAIN statement of this query above:
But I am not sure what this is doing, however, I think that for some reason its ignoring the "stat" index on both user_table and post_table.
3 in array
Array
(
[0] => Array
(
[id] => 1
[select_type] => SIMPLE
[table] => c
[type] => ALL
[possible_keys] => PRIMARY,id,id_2, userStat
[key] =>
[key_len] =>
[ref] =>
[rows] => 8
[Extra] => Using where; Using temporary; Using filesort
)
[1] => Array
(
[id] => 1
[select_type] => SIMPLE
[table] => p
[type] => ref
[possible_keys] => PRIMARY,id,id_2, userID, postmainID
[key] => userID
[key_len] => 27
[ref] =>
[rows] => 15091
[Extra] =>
)
[2] => Array
(
[id] => 1
[select_type] => SIMPLE
[table] => t
[type] => ref
[possible_keys] => pid
[key] => pid
[key_len] => 777
[ref] =>
[rows] => 1
[Extra] => Using where; Using index
)
)
select p.pid, p.other_fields, c.user_name,
( SELECT group_concat( t.tag ) FROM tag_table AS t
WHERE t.pid = p.pid ) as tags
FROM post_table as p
JOIN user_table as c ON p.userID = c.userID
where p.stat = 1
and p.mainID = 0
and c.stat = 'y'
order by p.pid desc
limit 0, 20
p: INDEX(stat, mainID, pid, userID, other_fields)
c: INDEX(userID, stat, user_name)
t: INDEX(pid, tag)
The GROUP BY p.pid is probably redundant now, put it back in if you need it.
There is no performance difference between the old comma-join and the new JOIN..ON. There is a possible semantic difference between JOIN/comma-join and LEFT JOIN. My reformulation for tags is equivalent to LEFT JOIN. The existence of c.stat = ... forces the other LEFT JOIN to turn into JOIN, so no semantic difference.
pid is key_len=777? Please provide SHOW CREATE TABLE so I can understand. Ditto for userID and 27.
There are a lot of possible reasons for the Optimizer to avoid a given index. It will probably use my indexes in preference to all others.
Related
Edit: Using MySQL.
In this example I have a Posts table, a Users table, a Tagged_Posts table, and a Tags table.
The Posts table has a foreign key for user_id that corresponded to the Users table and id column.
The Tagged_posts table has two foreign keys, one for the tag_id and one for the post_id
The Tags table just has an id and a slug.
I'm trying to query posts and have all data related to that post returned, i.e The Post data, the user's data whom the post, and what tags belong to that post.
A single post can have many tags.
This is the SQL query I'm using:
$sql = "SELECT posts.title, users.name, users.handle, users.email, tags.slug as tags
from posts
INNER JOIN users ON posts.user_id = users.id
INNER JOIN tagged_posts ON posts.id = tagged_posts.post_id
INNER JOIN tags ON tagged_posts.tag_id = tags.id";
Since this particular post has 3 tags associated with it, the query returns the same post 3 times with different values listed for tag. See below:
Array
(
[0] => Array
(
[title] => This is my first Post!
[name] => exampleuser
[handle] => example
[email] => example#gmail.com
[tags] => database-design
)
[1] => Array
(
[title] => This is my first Post!
[name] => exampleuser
[handle] => example
[email] => example#gmail.com
[tags] => sql
)
[2] => Array
(
[title] => This is my first Post!
[name] => exampleuser
[handle] => example
[email] => example#gmail.com
[tags] => php
)
)
Am I able to do a single query and get everything related to the post?
You want to concatenate the results together. For instance, in MySQL, you would do:
select p.title, u.name, u.handle, u.email, group_concat(t.slug) as tags
from posts p join
users u
on p.user_id = u.id join
tagged_posts tp
on p.id = tp.post_id join
tags t
on tp.tag_id = t.id
group by p.title, u.name, u.handle, u.email;
I am trying to get a count of apps for a user, using LEFT JOIN. It needs to return users regardless of whether they have apps, but the query is returning an empty record with 0 apps when no users are present.
SELECT u.*, COUNT(a.id) AS apps_working
FROM users u
LEFT JOIN apps a ON (u.id = a.user_id)
WHERE u.company_id = :company_id
AND u.account_type = 3
EDIT: When there are no users present, the results look like this:
Array
(
[0] => Array
(
[id] =>
[company_id] =>
[user_login] =>
[user_password] =>
[first_name] =>
[last_name] =>
[user_hash] =>
[user_slug] =>
[date_joined] =>
[last_login] =>
[account_type] =>
[status] =>
[apps_working] => 0
)
)
This is a query that is ALWAYS going to return a value because count(*) will always return something. If you don't want to see that, add a case statement to trap 0 and return null.
Try this code:
SELECT u.*, COUNT(IFNULL(a.id,0)) AS apps_working
FROM users u
LEFT JOIN apps a ON (u.id = a.user_id)
WHERE u.company_id = :company_id
AND u.account_type = 3
This is a left join query. So if there is no user satisfies conditions of company_id and account_type, there is no thing for joining and there is no thing in result.
how to join this in single query any help to combine these two queries as one without looping,
$today_date = mktime(0, 0, 0, $mon, $day-1, $year);
SELECT * FROM (`lead_follow_up`) LEFT JOIN `leads` ON `leads`.`id` = `lead_follow_up`.`lead_id` WHERE `date` <= $today_date GROUP BY `lead_follow_up`.`lead_id` ORDER BY `lead_follow_up`.`date` DESC
from the above query i get array $previou
$previou= Array
(
[0] => stdClass Object
(
[id] => 1
[lead_id] => 75943
[date] => 1438930800
[updated_on] => 1438884890
)
[1] => stdClass Object
(
[id] => 2
[lead_id] => 75943
[date] => 1416459600
[updated_on] => 1415901523
),
[2] => stdClass Object
(
[id] => 3
[lead_id] => 75943
[date] => 1416459600
[updated_on] => 1415901523
),....etc
);
foreach($previou as $key => $p):
$q = "SELECT `id` FROM (`lead_follow_up`) WHERE `lead_id` = '".$p->id."' AND `date` > '".$p->date."' ORDER BY `updated_on` DESC ";
if(!$this->db->query($q)){
$previouData[$key] = $p;
$pCount++;
}
endforeach;
how to join this in single query any help to combine these two queries as one without looping,
Your queries don't make much sense. For a start your first query has a GROUP BY lead_follow_up.lead_id but no aggregate functions. So in MySQL that will return one row for each value of lead_id (which row it returns is not defined).
Yet your array of sample data has multiple rows per lead_id so cannot have come from the query.
You are also LEFT OUTER JOINing the leads table, yet it doesn't seem to make sense to have a lead_follow_up which doesn't relate to a lead. As such you may as well use an INNER JOIN.
I am going to assume that what you want is a list of leads / lead_follow_ups and for each one a couple of all the follow ups after that particular follow up. That would give you something like this (making loads of assumptions as I do not know your table structure):-
SELECT leads.id AS lead_id,
lead_follow_up.id
lead_follow_up.`date`,
lead_follow_up.updated_on,
COUNT(lead_follow_up_future.id) AS future_lead_count
FROM leads
INNER JOIN lead_follow_up ON leads.id = lead_follow_up.lead_id
LEFT OUTER JOIN lead_follow_up AS lead_follow_up_future ON leads.id = lead_follow_up.lead_id AND lead_follow_up_future.`date` > lead_follow_up.`date`
WHERE lead_follow_up.`date` <= $today_date
GROUP BY leads.id AS lead_id,
lead_follow_up.id
lead_follow_up.`date`,
lead_follow_up.updated_on
ORDER BY lead_follow_up.date DESC
I have a problem, i am noob with cakePHP and i use cakePHP 1.3 and i have a query with a lot subquerys and try to convert this to queryBuilder of cake using find sentences or things like this:
My query is the next-one:
SELECT
`PbFeedback`.`id`,
`PbFeedback`.`createdby`,
`PbFeedback`.`created`,
`PbFeedback`.`msg`,
`Usuario`.`name`,
`PbFeedback`.`tipo`,
COUNT(IF(PbFeedback.msg = 0, 1, 0))AS totalMsg,
`Entidad`.`nombre`
,UltimoMensaje.* , #Subconsulta
MensajeNuevo.* #Subconsulta
FROM
# A A
`eon_feedback` AS `PbFeedback`
LEFT JOIN
# B B
`eon_sys_usuarios` AS `Usuario` ON(`PbFeedback`.`createdby` = `Usuario`.`id`)
LEFT JOIN `eon_entidades` AS `Entidad` ON(
`PbFeedback`.`entidad_id` = `Entidad`.`id`
)
LEFT JOIN (SELECT
`F`.`createdby`,
`F`.`created`,
`F`.`msg`,
`F`.`tipo`
FROM eon_feedback AS `F`
GROUP BY `F`.`createdby`
ORDER BY F.created DESC
) AS UltimoMensaje ON (UltimoMensaje.createdby = `PbFeedback`.`createdby`)
LEFT JOIN (
SELECT
F.createdby AS usuario_id,
COUNT(*) AS `count`
FROM
`eon_feedback` AS `F`
WHERE `F`.`status` = 0
GROUP BY F.createdby
) AS MensajeNuevo ON MensajeNuevo.usuario_id = PbFeedback.createdby
WHERE
`PbFeedback`.`usuario_id` = 0
GROUP BY
`PbFeedback`.`createdby`
ORDER BY
`PbFeedback`.`createdby` ASC
LIMIT 0,
30 ;
Thanks ;)
I would suggest you look into the ContainableBehavior:
http://book.cakephp.org/1.3/en/The-Manual/Core-Behaviors/Containable.html
Containable allows you to easily build complex queries, for example:
$this->PbFeedback->find('all', array(
'contain' => array (
'Usuario',
'UltimoMensaje ',
'eon_feedback' => array (
'fields' => array ('created','msg','tipo'),
'conditions' => array ('eon_feedback.status =' => '0')
)
),
'limit' => 30
);
I have a working SQL statement, but there is one issue within it I can't solve.
When I left join my table sites_photos there can be multiple matches on sp.sites_id = s.id, but I want the table to only return 1. Is this possible.
SELECT s.*, sp.photo
FROM sites s
LEFT JOIN sites_photos sp
ON sp.sites_id = s.id
My output: 2 times id 30, but with different photo paths, I only want 1 returned for that id, or both bundled in one array.
Array
(
[0] => Array
(
[id] => 30
[url] => www.test.nl
[name] => Aleve
[date] => 2014-08-16
[cms_active] => Y
[archive] => N
[photo] => 2014080812365920120214103601number_1.jpg
)
[1] => Array
(
[id] => 30
[url] => www.test.nl
[name] => Aleve
[date] => 2014-08-16
[cms_active] => Y
[archive] => N
[photo] => 20140811021102news.jpg
)
)
You can do so,by using GROUP_CONCAT which will concatenate all the photos per site by and produces comma separated list of photos then you can use SUBSTRING_INDEX over result of GROUP_CONCAT to pick one photo,you can also add the criteria of order by in GROUP_CONCAT as GROUP_CONCAT(sp.photo ORDER BY sp.id DESC)
SELECT s.*, SUBSTRING_INDEX(GROUP_CONCAT(sp.photo),',',1) photo
FROM sites s
LEFT JOIN sites_photos sp
ON sp.sites_id = s.id
GROUP BY s.id