I need to get a list of Articles sorted by the latest Comment from a related table joined on article.id = message.article_id using Kohana ORM. I managed to build a query that GROUPS and only then ORDERS:
SELECT *
FROM `articles`
LEFT JOIN `comments` ON ( `articles`.`id` = `comments`.`article_id` )
GROUP BY `comments`.`item_id`
ORDER BY `datetime` DESC
The query I am trying to build is:
SELECT * FROM `articles` LEFT JOIN
(SELECT article_id, MAX(datetime) as datetime FROM comments GROUP BY (article_id))
AS b ON `articles`.`id` = b.`article_id`
ORDER BY datetime
I have no idea how to rewrite it into Kohana ORM... (and I can't avoid ORM because there is a ton of code that depends on it)
$subquery = DB::select('article_id', array('MAX("datetime")','datetime'))
->from('comments')
->group_by('article_id');
$s = ORM::factory('article')
->join(array($subquery, 'b'), 'LEFT')
->on('article.id','=','b.article_id')
->order_by('datetime')
->find_all();
This is the translation of your query, I'm not really sure if it will work
ORM::factory('article')->join('comments', 'LEFT')->on('article.id', '=', 'comments.article_id')->group_by('comments.id')->order_by('date', 'DESC')->find_all()->as_array();
This generates sql as :
SELECT article.* FROM articles AS article LEFT JOIN comments
ON (article.id = comments.article_id) GROUP BY comments.id
ORDER BY date DESC
which matches your first query.
I'm not sure how to use nested query in ORM but there is Query builder in kohana which should do the trick for you.
Related
I'm current facing a issue in my project. When i search a product, it take too much time to fetch data.
This is sql code.
SELECT * FROM product_info
LEFT JOIN (
SELECT bulk_stock_info.bulk_unit_buy_price,bulk_stock_info.general_unit_sale_price,bulk_stock_info.bulk_unit_sale_price,bulk_stock_info.product_id as pid
FROM bulk_stock_info
ORDER BY bulk_id DESC
) bulk_stock_info
ON bulk_stock_info.pid = product_info.product_id
WHERE (`product_name` RLIKE ' +$query') OR `product_name` LIKE '$query%';"
There is no reason to use a subquery or outer join:
SELECT pi.*
bsi.bulk_unit_buy_price, bsi.general_unit_sale_price, bsi.bulk_unit_sale_price
FROM product_info pi JOIN
bulk_stock_info bsi
ON bsi.product_id = pi.product_id
WHERE pi.product_name RLIKE ' +$query' OR pi.product_name LIKE '$query%'
Initially I need to build a query fetching sites from one table ordered by date of newest article (articles placed in the separate table).
I build the following query:
SELECT *
FROM `sites`
INNER JOIN `articles` ON `articles`.`site_id` = `sites`.`id`
ORDER BY `articles`.`date` DESC
GROUP BY `sites`.`id`
I supposed that SELECT and INNER JOIN will fetch all posts and associate a site to each one, than ORDER BY will order the result by descending of post date than GROUP BY will take the very first post for each site and I will get the needed result.
But I'm receiving MySQL error #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'GROUP BYsites.idLIMIT 0, 30' at line 7
If I place GROUP BY before ORDER BY statement the query is working but it will not give me the newest post for each site. Instead the result will be sorted after the grouping which is not the thing I need (actually I could prefer to order in another way after grouping).
I read several pretty similar questions but they all related to the data stored in a single table making it possible to use MAX and MIN functions.
What should I do to implement what I need?
You can use either a subquery / derived-table / inline-view or a self-exclusion join, e.g.:
SELECT s.*, a1.*
FROM `sites` s
INNER JOIN `articles` a1 ON a1.`site_id` = s.`id`
LEFT OUTER JOIN `articles` a2 ON a2.`site_id` = a1.`site_id`
AND a2.`date` > a1.`date`
WHERE
a2.`site_id` IS NULL
ORDER BY
a1.`date` DESC
The principle is that you select the sites for which there is no article date greater than any other article date.
rewrite the sql to the following syntax -
SELECT `articles`.`article_name`,'sites'.'id','articles'.'site_id'
FROM `sites`,'articles'
WHERE `articles`.`site_id` = `sites`.`id`
ORDER BY 'sites'.'id', `articles`.`date` DESC;
Do something like this in the select statement. Group by function demands that all fields to be grouped. Hence usage of * is not possible.
SELECT * FROM ( SELECT `S.<col1>`, `S.<col2>`, `A.<col1>`,`A.<col2>`,
ROW_NUMBER ()
OVER (PARTITION BY `SITES`.`ID`
ORDER BY `SITES`.`ID` DESC)
RID
FROM `SITES` `S`,`ARTICLES` `A`
WHERE `ARTICLES`.`SITE_ID` = `SITES`.`ID`
)
WHERE RID = 1;
Can you try this?
Finally I came to the solution.
First of all I changed the main query from queering from sites table to queering from articles. Next I added the MAX(date) column to the result.
So the resulting query implementing the thing I need is the following:
SELECT `sites`.`url`,MAX(`articles`.`date`) AS `last_article_date`
FROM `articles`
INNER JOIN `sites` ON `sites`.`id` = `article`.`site_id`
GROUP BY `site_id`
ORDER BY `last_article_date` ASC
Thanks to all of you for giving me hints and right search directions!
SELECT * FROM
(
SELECT messages.from AS `from`, messages.creation AS `creation`, account_images.profile AS `profile`, bio.user_full_name AS `user_full_name`
FROM messages
INNER JOIN account_images ON account_images.uuid=messages.from
INNER JOIN bio ON bio.uuid=messages.from
WHERE messages.to='{$user}'
ORDER BY `creation` DESC
)
GROUP BY `from`
Why this query not working? Is there something wrong with nested select statements? Please help me how to solve this...
FROM is a mysql reserved keyword using from as column name should be in backtics otherwise you will get error messages.from
SELECT * FROM
(
SELECT messages.`from` AS `from`, messages.creation AS `creation`, account_images.profile AS `profile`, bio.user_full_name AS `user_full_name`
FROM messages
INNER JOIN account_images ON account_images.uuid=messages.`from`
INNER JOIN bio ON bio.uuid=messages.`from`
WHERE messages.to='{$user}'
ORDER BY `creation` DESC
)
GROUP BY `from`
Reserved Words
In addition to the unquoted from problem, the joins are in the wrong order. You cannot refer to a table alias in an on clause before it is defined and you need a table alias on the subquery:
SELECT *
FROM (SELECT messages.from AS `from`, messages.creation AS `creation`,
account_images.profile AS `profile`, bio.user_full_name AS `user_full_name`
FROM account_images INNER JOIN
bio
ON bio.uuid = messages.`from` INNER JOIN
messages
ON account_images.uuid = messages.`from`
WHERE messages.to='{$user}'
ORDER BY `creation` DESC
) t
GROUP BY `from`;
Finally, you are attempting to get the last message for a group using a technique explicitly described as unreliable in the MySQL documentation on group by extensions (see here).
Hi I have the following query from a previous question and need to convert it to DQL for Doctrine 1.2. However it turns out that DQL does not support subqueries in joins.
SELECT * FROM contact c
LEFT JOIN
(SELECT a1.contact_id, a1.date, a1.activity_type_id FROM activity a1
JOIN (SELECT contact_id, MAX(DATE) DATE FROM activity GROUP BY contact_id) a2
ON a1.contact_id = a2.contact_id AND a1.date = a2.date
) a
ON c.id = a.contact_id
WHERE a.activity_type_id = 2;
I'm trying to figure out how to do this another way without resorting to multiple queries.
Thanks.
Using doctrine, you should not nest subqueries into where condition using raw sql. You will get into trouble in more complex scenarios. Instead use createSubquery to explicitly tell doctrine about the subquery, let doctrine compile it to DQL and then nest it into your where condition. So your query should look something like this:
$q->from('Contact c')
->leftJoin('c.Activity a')
;
$subquery = $q->createSubquery()
->select("a1.contact_id, MAX(a1.date) a1.date")
->from("Activity a1")
->groupby("a1.contact_id")
;
$q->where('ROW (c.id, date) IN ('.$subquery->getDql().')')
->andWhere('a.activity_type_id = ?', $filterActivityTypeId)
;
Another example can be found here:
https://www.philipphoffmann.de/2012/08/29/a-bulletproof-pattern-for-creating-doctrine-subqueries-of-any-complexity/
Final query:
SELECT * FROM contact c
LEFT JOIN activity ON c.id = contact_id
WHERE ROW (c.id,DATE) IN (SELECT contact_id, MAX(date) date FROM activity GROUP BY contact_id)
AND activity_type_id = 2
Final DQL:
$q->from('Contact c')
->leftJoin('c.Activity a')
->where('ROW (c.id, date) IN (SELECT a1.contact_id, MAX(a1.date) a1.date FROM Activity a1 GROUP BY a1.contact_id)')
->andWhere('a.activity_type_id = ?', $filterActivityTypeId);
im trying to generate a report using CodeIgniter and Datatables.net .
Now i'm trying to the amount of closed jobs (its a human resources system). I used to query all jobs and in PHP do a foreach and then doing the calcs.
Because im want to use all the features of Datatables (sorting specifically) im trying to do all the calcs in mySQL.
The problem is: the second subquery is very very very slow.
SELECT
jobs.jobs_id, clients.nome_fantasia, concat_ws(' ', user_profiles.first_name, user_profiles.last_name) as fullname,
jobs.titulo_vaga, jobs.qtd_vagas, company.name as nome_company, jobs_status.name as status_name, DATEDIFF(NOW(), jobs.data_abertura) as date_idade,
(select count(job_cv.jobs_id) from job_cv where job_cv.jobs_id = jobs.jobs_id) as qtd_int,
(select count(distinct job_cv.user_id) from job_cv_history join job_cv on job_cv.job_cv_id = job_cv_history.job_cv_id where job_cv_history.status = '11' and job_cv.jobs_id = jobs.jobs_id ) as fechadas
FROM (jobs)
JOIN clients ON lients.clients_id=jobs.clients_idJOIN user_profiles ON jobs.consultor_id=user_profiles.user_id
JOIN jobs_status ON jobs.status=jobs_status.jobs_status_id
JOIN company ON jobs.company_id=company.company_id
LIMIT 50
Some one can help me? I can provide more information if its needed.
UPDATE
The idea to use JOIN instead SELECT work with the first subquery but with the second one not, there a way to pass a 'variable' to use inside the subquery? Like the current jobs_id?
UPDATE AGAIN
This line works fine by itself. But inside the subquery take about a minute with worng values
SELECT job_cv.jobs_id,count(distinct job_cv.user_id) AS fechadas
FROM job_cv_history
JOIN job_cv
ON job_cv.job_cv_id = job_cv_history.job_cv_id
WHERE job_cv_history.status = '11'
GROUP BY job_cv.jobs_id
It is not subquery that is slow. It's the fact, that you're executing these subqueries for each row returned from outer query. Move these to joins instead, and you should observe increase in performance.
SELECT
jobs.jobs_id, clients.nome_fantasia, concat_ws(' ', user_profiles.first_name, user_profiles.last_name) as fullname,
jobs.titulo_vaga, jobs.qtd_vagas, company.name as nome_company, jobs_status.name as status_name, DATEDIFF(NOW(), jobs.data_abertura) as date_idade,
qtd.qtd_int,
fechadas.fechadas
FROM (jobs)
JOIN clients ON lients.clients_id=jobs.clients_idJOIN user_profiles ON jobs.consultor_id=user_profiles.user_id
JOIN jobs_status ON jobs.status=jobs_status.jobs_status_id
JOIN company ON jobs.company_id=company.company_id
JOIN (
SELECT jobs_id, count(jobs_id) AS qtd_int FROM job_cv GROUP BY jobs_id
) AS qtd ON qtd.jobs_id = jobs.jobs_id
JOIN (
SELECT job_cv.user_id, count(distinct job_cv.user_id) AS fechadas
FROM job_cv_history
JOIN job_cv
ON job_cv.job_cv_id = job_cv_history.job_cv_id
WHERE job_cv_history.status = '11'
GROUP BY job_cv.user_id
) AS fechadas ON job_cv.jobs_id = jobs.jobs_id
LIMIT 50
You may try to create these indexes:
ALTER TABLE `job_cv` ADD INDEX `job_cv_cindex` (`job_cv_id` ASC, `jobs_id` ASC, `user_id` ASC);
ALTER TABLE `job_cv_history` ADD INDEX `job_cv_history_cindex` (`job_cv_id` ASC, `status` ASC);
use Joins instead of sub queries. It significantly improves the performance in MySql.
try to use Left join on your case and see if performance improves or not