SQL FIDDLE HERE
I am making a simple blog system where posts can have more than one category, so I have this query:
SELECT *, GROUP_CONCAT(item_category) AS item_categories
FROM (`dev_pages`)
LEFT JOIN `dev_items_to_categories` ON `dev_items_to_categories`.`item_id` = `dev_pages`.`page_id`
WHERE deleted_time IS NULL
AND `page_type` = 'blog'
AND `item_category` = '16'
ORDER BY `page_title` ASC
It works fine but if there are no results instead of coming back as nothing it returns NULL or the default value (See SQL Fiddle)
I managed to make a around but I was wondering if anyone had a better solution to this:
SELECT a.* FROM (
SELECT *, GROUP_CONCAT(item_category) AS item_categories
FROM (`dev_pages`)
LEFT JOIN `dev_items_to_categories` ON `dev_items_to_categories`.`item_id` = `dev_pages`.`page_id`
WHERE deleted_time IS NULL
AND `page_type` = 'blog'
AND `item_category` = '16'
ORDER BY `page_title` ASC
) AS a
WHERE page_id > 0
You need a GROUP BY clause:
SELECT *, GROUP_CONCAT(item_category) AS item_categories
FROM (`dev_pages`)
LEFT JOIN `dev_items_to_categories` ON `dev_items_to_categories`.`item_id` = `dev_pages`.`page_id` AND `item_category` = '16'
WHERE deleted_time IS NULL
AND `page_type` = 'blog'
GROUP BY dev_pages.page_id
ORDER BY `page_title` ASC
You also need to put item_category = '16' into the ON clause. Otherwise, you will filter out rows with no matches in dev_items_to_categories; since you're using LEFT JOIN rather than INNER JOIN, I assume you want those rows with null matches.
FIDDLE
you can also try coalesce function.
SELECT *, GROUP_CONCAT(coalece(item_category,'')) AS item_categories
FROM (`dev_pages`)
LEFT JOIN `dev_items_to_categories` ON `dev_items_to_categories`.`item_id` = `dev_pages`.`page_id`
WHERE deleted_time IS NULL
AND `page_type` = 'blog'
AND `item_category` = '16'
ORDER BY `page_title` ASC
Related
I want to use the default search and pagination in yii2. But the query is complex and I don't know how can I add it to the search model! This is the query:
SELECT p.*,po_sum,rpo_sum,so_sum
FROMproduct p
LEFT JOIN (
SELECT id,product_id , IF(sum(quantity) IS NULL, 0, sum(quantity)) AS po_sum
FROM purchase_order_products inner join purchase_order on purchase_order.id = purchase_order_products.purchase_order_id
Where purchase_order.status = 'Approved'
GROUP BY product_id )
subcount ON p.id = subcount.product_id
LEFT JOIN (
SELECT id,product_id , sum(quantity) AS rpo_sum
FROM return_purchase_order_products inner join return_purchase_order on return_purchase_order.id = return_purchase_order_products.purchase_order_id Where return_purchase_order.status = 'Approved'
GROUP BY product_id )
subcount2 ON p.id = subcount2.product_id
LEFT JOIN (
SELECT product_id , sum(quantity_ordered) AS so_sum
FROM sales_order_item inner join sales_order on sales_order.id = sales_order_item.sales_order_id Where sales_order.order_status = 'complete'
GROUP BY product_id )
subcount3 ON p.id = subcount3.product_id
order by po_sum DESC,rpo_sum DESC
Any help?
If you use MySql >= 5.7.7 the easiest way is to create a view with that query and use it in the tableName method.
You need that version of MySql because you cant use subquery in from clause during view creation in previous versions.
Here is my query:
SELECT DISTINCT `post_data`. * , pv.`seller_id` , pv.`islimited` , pv.`isquantity` , pv.`isslider`, `price`.`original_price` , `price`.`discount_percentage` , `timelimit`.`start_date` , `timelimit`.`expire_date` , `quantity`.`in_stock`, `currency`.`currency_symbol`, `seller`.`directory`, `post_to_cat`.`cat_id`, count(`sales`.`sales_id`) as sale FROM `post_view` AS pv
INNER JOIN `post_data` ON pv.`post_id` = `post_data`.`post_id` AND pv.`status` = 1
INNER JOIN `price` ON pv.`post_id` = `price`.`post_id`
INNER JOIN `currency` ON `price`.`currency_id` = `currency`.`currency_id`
INNER JOIN `seller` ON pv.`seller_id` = `seller`.`seller_id`
INNER JOIN `post_to_cat` ON `post_to_cat`.`cat_id` = 1 AND `post_to_cat`.`post_id` = `post_data`.`post_id`
LEFT JOIN `timelimit` ON ( CASE WHEN pv.`islimited` = 1 THEN `timelimit`.`post_id` ELSE -1 END ) = pv.`post_id`
LEFT JOIN `quantity` ON ( CASE WHEN pv.`isquantity` = 1 THEN `quantity`.`post_id` ELSE -1 END ) = pv.`post_id`
LEFT JOIN `sales` ON `sales`.`post_id` = pv.`post_id` AND `sales`.`status` = 1
WHERE pv.`status` = 1
ORDER BY pv.`post_id` DESC LIMIT 1
The ORDER BY DESC is not working, it just returns the first row from the table, but I want to get the highest post_id value row. What is the mistake I am making?
AS #Alex said in the comments you've got a LIMIT 1 at the end, you should probably bracket the last LEFT JOIN also for readability.
As #McAdam331 said we need data sample and sql fiddle to investigate what is wrong with you query. But at the moment I have some suggestions how to improve and debug your query.
First off all, what do I see the main and very left table in your query is post_view so all other tables should be LEFT JOIN if you want to get the max id. You should use INNER JOIN only if you think that other table could filter your main table somehow and order or result could be other table dependend. But in your case I see no reason to use INNER JOIN.
Second point is your very weird ON conditions:
LEFT JOIN `timelimit` ON ( CASE WHEN pv.`islimited` = 1 THEN `timelimit`.`post_id` ELSE -1 END ) = pv.`post_id`
LEFT JOIN `quantity` ON ( CASE WHEN pv.`isquantity` = 1 THEN `quantity`.`post_id` ELSE -1 END ) = pv.`post_id`
I have converted them into another one
CASE WHEN pv.`islimited`=1 THEN `timelimit`.`start_date` ELSE NULL END as start_date ,
CASE WHEN pv.`islimited`=1 THEN `timelimit`.`expire_date` ELSE NULL END as expire_date,
CASE WHEN pv.`isquantity`=1 THEN `quantity`.`in_stock` ELSE NULL END as in_stock,
But I still don't like it. It seems very useless to me. And has no sense when I read CASE WHEN pv.islimited=1 THEN timelimit.start_date ELSE NULL END as start_date so if flag pv.islimited=0 you don't need start_date? Are you sure?
And the last thing I can suggest: try to use my or even your query. But add every table by step while debugging. So First query just:
SELECT
pv.`post_id`, pv.`seller_id` , pv.`islimited` , pv.`isquantity` ,
pv.`isslider`
FROM `post_view` AS pv
WHERE pv.`status` = 1
ORDER BY pv.`post_id` DESC
LIMIT 1
If it returns correct post_id add next table:
SELECT
pv.`post_id`, pv.`seller_id` , pv.`islimited` , pv.`isquantity` ,
pv.`isslider`,
`post_data`. *
FROM `post_view` AS pv
LEFT JOIN `post_data`
ON pv.`post_id` = `post_data`.`post_id`
WHERE pv.`status` = 1
AND `post_data`.`slug` = 'abc'
ORDER BY pv.`post_id` DESC
LIMIT 1
Check the result. And continue step by step.
Yes it takes time. But that is debugging process. It could be the fastest way to get that query done. :-)
SELECT `post_data`. * ,
pv.`post_id`, pv.`seller_id` , pv.`islimited` , pv.`isquantity` ,
pv.`isslider`, `price`.`original_price` , `price`.`discount_percentage` ,
CASE WHEN pv.`islimited`=1 THEN `timelimit`.`start_date` ELSE NULL END as start_date ,
CASE WHEN pv.`islimited`=1 THEN `timelimit`.`expire_date` ELSE NULL END as expire_date,
CASE WHEN pv.`isquantity`=1 THEN `quantity`.`in_stock` ELSE NULL END as in_stock,
`currency`.`currency_symbol`, `seller`.`directory`, `post_to_cat`.`cat_id`, count(`sales`.`sales_id`) as sale
FROM `post_view` AS pv
LEFT JOIN `post_data`
ON pv.`post_id` = `post_data`.`post_id`
LEFT JOIN `price`
ON pv.`post_id` = `price`.`post_id`
LEFT JOIN `currency`
ON `price`.`currency_id` = `currency`.`currency_id`
LEFT JOIN `seller`
ON pv.`seller_id` = `seller`.`seller_id`
LEFT JOIN `post_to_cat`
ON `post_to_cat`.`cat_id` = 1
AND `post_to_cat`.`post_id` = pv.`post_id`
LEFT JOIN `timelimit`
ON `timelimit`.`post_id` = pv.`post_id`
LEFT JOIN `quantity`
ON quantity`.`post_id` = pv.`post_id`
LEFT JOIN `sales`
ON `sales`.`post_id` = pv.`post_id`
AND `sales`.`status` = 1
WHERE pv.`status` = 1
AND `post_data`.`slug` = 'abc'
GROUP BY pv.`post_id`
ORDER BY pv.`post_id` DESC
LIMIT 1
EDIT 1 - last GROUP BY pv.post_id was added as per #McAdam331 notice about count() function without GROUP BY
I believe the issue here is mostly as a result of preforming aggregation (using the COUNT()) function, without any group by. Although, it seems like you don't necessarily need it because you want that count only for the post in question.
If you're trying to gather all of that information for a single post, I would adjust your WHERE clause to have a condition to only gather that information for the post with the largest ID.
Instead of ordering by ID and limiting by 1, use a subquery to get the largest id, like this:
...
WHERE pv.status = 1 AND post_data.slug = 'abc' AND pv.post_id = (SELECT MAX(post_id) FROM post_view);
I a developing in zend and have a rather large mysql query. The query works fine and i get the list I expect. I am doing this using Select->Where.... below is the query.
SELECT DISTINCT `d`.* FROM `deliverable` AS `d` INNER JOIN `groups` AS `g1` ON d.id = g1.deliverable_id INNER JOIN `groupmembers` AS `gm1` ON g1.id = gm1.group_id LEFT JOIN `connection` AS `c` ON d.id = c.downstreamnode_id LEFT JOIN `deliverable` AS `d1` ON c.upstreamnode_id = d1.id INNER JOIN `deliverable` AS `d2` ON CASE WHEN d1.id IS NULL THEN d.id ELSE d1.id END = d2.id INNER JOIN `groups` AS `g` ON d2.id = g.deliverable_id INNER JOIN `groupmembers` AS `gm` ON g.id = gm.group_id WHERE (g1.group_type = 100) AND (gm1.member_id = 1) AND (c.downstreamnode_id IS NULL OR d.restrict_access = 1) AND (g.group_type = 100 OR g.group_type = 110) AND (gm.member_id = 1) AND (d.deliverable_type = 110 OR d.deliverable_type = 100) GROUP BY CASE WHEN c.downstreamnode_id IS NULL THEN d.id ELSE c.downstreamnode_id END
Only problem is when I try to count the rows in a mysql query I only get 1 returned. below is the query
SELECT DISTINCT count(*) AS `rowCount` FROM `deliverable` AS `d` INNER JOIN `groups` AS `g1` ON d.id = g1.deliverable_id INNER JOIN `groupmembers` AS `gm1` ON g1.id = gm1.group_id LEFT JOIN `connection` AS `c` ON d.id = c.downstreamnode_id LEFT JOIN `deliverable` AS `d1` ON c.upstreamnode_id = d1.id INNER JOIN `deliverable` AS `d2` ON CASE WHEN d1.id IS NULL THEN d.id ELSE d1.id END = d2.id INNER JOIN `groups` AS `g` ON d2.id = g.deliverable_id INNER JOIN `groupmembers` AS `gm` ON g.id = gm.group_id WHERE (g1.group_type = 100) AND (gm1.member_id = 1) AND (c.downstreamnode_id IS NULL OR d.restrict_access = 1) AND (g.group_type = 100 OR g.group_type = 110) AND (gm.member_id = 1) AND (d.deliverable_type = 110 OR d.deliverable_type = 100) GROUP BY CASE WHEN c.downstreamnode_id IS NULL THEN d.id ELSE c.downstreamnode_id END
i generate this from by using the same 'select' that generated the first query but I reset the columns and add count in.
$this->getAdapter()->setFetchMode(Zend_Db::FETCH_ASSOC);
$select
->reset( Zend_Db_Select::COLUMNS)
->columns(array('count('.$column.') as rowCount'));
$rowCount = $this->getAdapter()->fetchOne($select);
This method works fine for all my other queries only this one i am having trouble with. I suspect it has something to do the 'CASE' I have in there but it is strange because I am getting the correct rows the the first query. Any ideas. Thanks.
FYI below are two queries that I have working successfully.
SELECT DISTINCT `po`.* FROM `post` AS `po` INNER JOIN `postinfo` AS `p` ON po.postinfo_id = p.id WHERE (p.creator_id = 1) ORDER BY `p`.`date_created` DESC
SELECT DISTINCT count(*) AS `rowCount` FROM `post` AS `po` INNER JOIN `postinfo` AS `p` ON po.postinfo_id = p.id WHERE (p.creator_id = 1) ORDER BY `p`.`date_created` DESC
In this one I have 4 rows returned in the first query and 'int 4' returned for the second one. Does anyone know why it doesnt work for the big query?
Move your DISTINCT.
SELECT COUNT(DISTINCT `po`.*) AS `rowCount` ...
Ok figured it out It was the GROUP BY that was causing only 1 result to be returned. Thanks Interrobang for you help I am sure that using DISTINCT incorrectly will have caused me a headache in the future.
Try using SQL_CALC_FOUND_ROWS in your query?
http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_found-rows
Using SQL_CALC_FOUND_ROWS is mysql-specific, but it's pretty nice for getting a full record count even when your initial query contains a limit. Once you get the count, don't include SQL_CALC_FOUND_ROWS in subsequent queries for extra records since that will cause extra load on your query.
Your initial query would be:
SELECT SQL_CALC_FOUND_ROWS DISTINCT `d`.* FROM `deliverable` AS `d` INNER JOIN `groups` ...
You'll have to do a subsequent call after your initial query executes to get the count by doing a SELECT FOUND_ROWS().
If you do a little searching, you'll find someone who extended Zend_Db_Select to include this ability.
Looking for some help with optimising the query below. Seems to be two bottlenecks at the moment which cause it to take around 90s to complete the query. There's only 5000 products so it's not exactly a massive database/table. The bottlenecks are SQL_CALC_FOUND_ROWS and the ORDER BY statement - If I remove both of these it takes around a second to run the query.
I've tried removing SQL_CALC_FOUND_ROWS and running a count() statement, but that takes a long time as well..
Is the best thing going to be to use INNER JOIN's (which I'm not too familiar with) as per the following Stackoverflow post? Slow query when using ORDER BY
SELECT SQL_CALC_FOUND_ROWS *
FROM tbl_products
LEFT JOIN tbl_link_products_categories ON lpc_p_id = p_id
LEFT JOIN tbl_link_products_brands ON lpb_p_id = p_id
LEFT JOIN tbl_link_products_authors ON lpa_p_id = p_id
LEFT JOIN tbl_link_products_narrators ON lpn_p_id = p_id
LEFT JOIN tbl_linkfiles ON lf_id = p_id
AND (
lf_table = 'tbl_products'
OR lf_table IS NULL
)
LEFT JOIN tbl_files ON lf_file_id = file_id
AND (
file_nameid = 'p_main_image_'
OR file_nameid IS NULL
)
WHERE p_live = 'y'
ORDER BY p_title_clean ASC, p_title ASC
LIMIT 0 , 10
You could try reducing the size of the joins by using a derived table to retrieve the filtered and ordered products before joining. This assumes that p_live, p_title_clean and p_title are fields in your tbl_products table -
SELECT *
FROM (SELECT *
FROM tbl_products
WHERE p_live = 'y'
ORDER BY p_title_clean ASC, p_title ASC
LIMIT 0 , 10
) AS tbl_products
LEFT JOIN tbl_link_products_categories
ON lpc_p_id = p_id
LEFT JOIN tbl_link_products_brands
ON lpb_p_id = p_id
LEFT JOIN tbl_link_products_authors
ON lpa_p_id = p_id
LEFT JOIN tbl_link_products_narrators
ON lpn_p_id = p_id
LEFT JOIN tbl_linkfiles
ON lf_id = p_id
AND (
lf_table = 'tbl_products'
OR lf_table IS NULL
)
LEFT JOIN tbl_files
ON lf_file_id = file_id
AND (
file_nameid = 'p_main_image_'
OR file_nameid IS NULL
)
This is a "stab in the dark" as there is not enough detail in your question.
I try to avoid subqueries due to the fact they usually have much lower performance than a proper join.
This is my current NOT working query:
SELECT
a.`email_list_id`, a.`category_id`, a.`name`
FROM
`email_lists`AS a
LEFT JOIN `email_autoresponders` AS b
ON ( a.`website_id` = b.`website_id` )
WHERE
a.`website_id` = [...]
AND a.`category_id` <> 0
AND a.`email_list_id` <> b.`email_list_id`
GROUP BY
a.`email_list_id`
ORDER BY a.`name`
This query works:
SELECT
`email_list_id`, `category_id`, `name`
FROM
`email_lists`
WHERE
`website_id` = [...]
AND `category_id` <> 0
AND `email_list_id` NOT IN (
SELECT
`email_list_id`
FROM
`email_autoresponders`
WHERE `website_id` = [...]
)
GROUP BY
`email_list_id`
ORDER BY
`name`
Is there any way to do this with a left join? I've tried a number of different options.
After rethinking it a bit, this might work i believe:
SELECT
a.`email_list_id`, a.`category_id`, a.`name`
FROM
`email_lists`AS a
LEFT JOIN `email_autoresponders` AS b
ON ( a.`website_id` = b.`website_id` and a.`email_list_id` = b.`email_list_id` )
WHERE
a.`website_id` = [...]
AND a.`category_id` <> 0
AND b.`email_list_id` is NULL
GROUP BY
a.`email_list_id`
ORDER BY a.`name`
For starters, add the inequality check on email_list_id to the join criteria, instead of having it in your WHERE clause:
LEFT JOIN `email_autoresponders` AS b
ON ( a.`website_id` = b.`website_id`
AND a.`email_list_id` <> b.`email_list_id` )
Though I'm not sure the scenario you mention calls for vast optimizations, this is a way to use a join rather than a subquery...