Need to show SQL partial empty results - php

I need to show empty rows for BRANDS too. I mean, there is a third brand not shown in this query, look:
SELECT
da_brands.name AS brand_name,
COUNT(DISTINCT da_deals.id) AS total_deals,
0 AS total_downloaded_coupons,
0 AS total_validated_coupons,
COUNT(da_logs.id) AS total_likes
FROM
da_brands,
da_deals
LEFT JOIN da_logs
ON da_logs.fk_deal_id = da_deals.id
AND da_logs.fk_deal_id = da_deals.id
AND da_logs.type = 'deal_like'
WHERE da_brands.fk_club_id = 6
AND da_deals.fk_brand_id = da_brands.id
AND da_brands.date <= NOW()
GROUP BY da_brands.name
ORDER BY da_brands.name ASC
RESULTS:
brand_name total_deals total_downloaded_coupons total_validated_coupons total_likes
Marca2 2 0 0 1
Marca1 9 0 0 4
This conditional is showing only brands within deals but i want all brands...:
AND da_deals.fk_brand_id = da_brands.id
Any idea what statement should i use?
Thank you so much.!!!

This following line in the WHERE predicate is the problem...
AND da_deals.fk_brand_id = da_brands.id
You need to LEFT JOIN to da_deals, the same way you did to da_logs, and move that line above into the ON statement for the join.
See below...
SELECT
da_brands.name AS brand_name,
COUNT(DISTINCT da_deals.id) AS total_deals,
0 AS total_downloaded_coupons,
0 AS total_validated_coupons,
COUNT(da_logs.id) AS total_likes
FROM da_brands
LEFT JOIN da_deals
ON da_brands.id = da_deals.fk_brand_id
LEFT JOIN da_logs
ON da_logs.fk_deal_id = da_deals.id
AND da_logs.fk_deal_id = da_deals.id
AND da_logs.type = 'deal_like'
WHERE da_brands.fk_club_id = 6
AND da_brands.date <= NOW()
GROUP BY da_brands.name
ORDER BY da_brands.name ASC

Related

How to use PDO::FETCH_GROUP with a table join and only returning 3 records in a joined table ordered by date

I am using the following code to get a grouped list of voyage types and their respective voyages.
public function getVoyageTypesWithTrips() {
//query
$this->db->query('
SELECT voyagetype_name
, voyagetype_description
, voyagetype_image
, voyage_id
, voyage_name
, voyage_startDate
FROM voyagetypes
LEFT
JOIN voyages
ON voyagetypes.voyagetype_id = voyages.voyage_type
WHERE voyagetype_deleted != 1
');
//get the results
$results = $this->db->resultSetGrouped();
return $results;
}
//get results set as array of objects - grouped
public function resultSetGrouped() {
$this->execute();
return $this->statement->fetchAll(PDO::FETCH_GROUP);
}
What I want to do is limit the voyages table to only show the 3 most closest records to today instead of returning all the voyages for that type.
So returning
Category 1 (Voyage next week, voyage week after, voyage week after that, no more but loads in table)
Category 2 (voyage tomorrow, no more in this category)
Category 3 (no voyages)
I initially tried ORDER BY and LIMIT but this doesn't work with the PDO::FETCH_GROUP I think.
I believe I need to have my SQL order & limit the joined table prior to sending to the fetch_group??? So something like
$this->db->query('
SELECT voyagetype_name
, voyagetype_description
, voyagetype_image
, voyage_id
, voyage_name
, voyage_startDate
FROM voyagetypes
LEFT
JOIN voyages
ON voyagetypes.voyagetype_id = voyages.voyage_type
APPLY THE SORT AND LIMIT TO THE JOINED TABLE
WHERE voyagetype_deleted != 1
');
One option is to filter with a subquery:
select vt.voyagetype_name, vt.voyagetype_description, vt.voyagetype_image, v.voyage_id, v.voyage_name, v.voyage_startdate
from voyagetypes vt
left join voyages v
on v.voyagetype_id = vt.voyage_type
and (
select count(*)
from voyages v1
where
v1.voyage_type = vt.voyage_type
and v1.voyage_startdate > v.voyage_startdate
) < 3
where vt.voyagetype_deleted <> 1
Or, if you are running MYSQL 8.0, you can just use rank():
select *
from (
select
vt.voyagetype_name,
vt.voyagetype_description,
vt.voyagetype_image,
v.voyage_id,
v.voyage_name,
v.voyage_startdate,
rank() over(partition by vt.voyage_type order by v.voyage_startdate desc) rn
from voyagetypes vt
left join voyages v on v.voyagetype_id = vt.voyage_type
where vt.voyagetype_deleted <> 1
) t
where rn <= 3

Single query that allows alias with it's own limit

I would like to better optimize my code. I'd like to have a single query that allows an alias name to have it's own limit and also include a result with no limit.
Currently I'm using two queries like this:
// ALL TIME //
$mikep = mysqli_query($link, "SELECT tasks.EID, reports.how_did_gig_go FROM tasks INNER JOIN reports ON tasks.EID=reports.eid WHERE `priority` IS NOT NULL AND `partners_name` IS NOT NULL AND mike IS NOT NULL GROUP BY EID ORDER BY tasks.show_date DESC;");
$num_rows_mikep = mysqli_num_rows($mikep);
$rating_sum_mikep = 0;
while ($row = mysqli_fetch_assoc($mikep)) {
$rating_mikep = $row['how_did_gig_go'];
$rating_sum_mikep += $rating_mikep;
}
$average_mikep = $rating_sum_mikep/$num_rows_mikep;
// AND NOW WITH A LIMIT 10 //
$mikep_limit = mysqli_query($link, "SELECT tasks.EID, reports.how_did_gig_go FROM tasks INNER JOIN reports ON tasks.EID=reports.eid WHERE `priority` IS NOT NULL AND `partners_name` IS NOT NULL AND mike IS NOT NULL GROUP BY EID ORDER BY tasks.show_date DESC LIMIT 10;");
$num_rows_mikep_limit = mysqli_num_rows($mikep_limit);
$rating_sum_mikep_limit = 0;
while ($row = mysqli_fetch_assoc($mikep_limit)) {
$rating_mikep_limit = $row['how_did_gig_go'];
$rating_sum_mikep_limit += $rating_mikep_limit;
}
$average_mikep_limit = $rating_sum_mikep_limit/$num_rows_mikep_limit;
This allows me to show an all-time average and also an average over the last 10 reviews. Is it really necessary for me to set up two queries?
Also, I understand I could get the sum in the query, but not all the values are numbers, so I've actually converted them in PHP, but left out that code in order to try and simplify what is displayed in the code.
All-time average and average over the last 10 reviews
In the best case scenario, where your column how_did_gig_go was 100% numeric, a single query like this could work like so:
SELECT
AVG(how_did_gig_go) AS avg_how_did_gig_go
, SUM(CASE
WHEN rn <= 10 THEN how_did_gig_go
ELSE 0
END) / 10 AS latest10_avg
FROM (
SELECT
#num + 1 AS rn
, tasks.show_date
, reports.how_did_gig_go
FROM tasks
INNER JOIN reports ON tasks.EID = reports.eid
CROSS JOIN ( SELECT #num := 0 AS n ) AS v
WHERE priority IS NOT NULL
AND partners_name IS NOT NULL
AND mike IS NOT NULL
ORDER BY tasks.show_date DESC
) AS d
But; Unless all the "numbers" are in fact numeric you are doomed to sending every row back from the server for php to process unless you can clean-up the data in MySQL somehow.
You might avoid sending all that data twice if you establish a way for your php to use only the top 10 from the whole list. There are probably way of doing that in PHP.
If you wanted assistance in SQL to do that, then maybe having 2 columns would help, it would reduce the number of table scans.
SELECT
EID
, how_did_gig_go
, CASE
WHEN rn <= 10 THEN how_did_gig_go
ELSE 0
END AS latest10_how_did_gig_go
FROM (
SELECT
#num + 1 AS rn
, tasks.EID
, reports.how_did_gig_go
FROM tasks
INNER JOIN reports ON tasks.EID = reports.eid
CROSS JOIN ( SELECT #num := 0 AS n ) AS v
WHERE priority IS NOT NULL
AND partners_name IS NOT NULL
AND mike IS NOT NULL
ORDER BY tasks.show_date DESC
) AS d
In future (MySQL 8.x) ROW_NUMBER() OVER(order by tasks.show_date DESC) would be a better method than the "roll your own" row numbering (using #num+1) shown before.

SQL search Result, Group by ID number

I'm having some issues with trying to fix this SQL Query
This is a custom search query which is searching for the word 'weddings' on all pages on this CMS system.
At the moment I am getting the same page appear on the first 5 rows because the word 'weddings' appears 5 times. What I want to do is combine the rows with the same ID number into 1 row so it doesn't appear multiple times.
I thought doing a group by at the end of this statement would do this but I keep getting an SQL syntax error
GROUP BY `documents`.`id`
I have attached the full SQL bellow with an image of the output i currently get.... Any idea?
SELECT `documents`.*,
`documenttypes`.`name` as `doctype`,
`articles`.`id` as `article_id`,
`articles`.`language_id`,
`articles`.`title`,
`articles`.`template`,
`articles`.`slug`,
`articles`.`path`,
`articles`.`slug_title`,
MATCH ( elements.textvalue )AGAINST ( 'weddings' ) AS score,
elements.textvalue AS matching,
LOWER(`articles`.`title`)
LIKE '%weddings%' as 'like_title',
( MATCH ( elements.textvalue )
AGAINST ( 'weddings' ) ) + IF(( LOWER(`articles`.`title`)
LIKE '%weddings%'),1, 0) + IF((LOWER(`elements`.`textvalue`)
LIKE '%weddings%'),1, 0) as total FROM (`documents`)
LEFT JOIN `articles` ON `articles`.`document_id` = `documents`.`id`
LEFT JOIN `documenttypes` ON `documents`.`documenttype_id` = `documenttypes`.`id`
LEFT JOIN `documents_users` AS du ON `documents`.`id` = du.`document_id`
LEFT JOIN `documents_usergroups` AS dug ON `documents`.`id` = dug.`document_id`
LEFT JOIN elements ON `elements`.`article_id` = `articles`.`id`
WHERE `documents`.`trashed` = 0
AND `documents`.`published` = 1
AND `articles`.`status_id` = 1
AND `articles`.`language_id` = 1
AND (`documents`.`no_search` = '0'
OR `documents`.`no_search` IS NULL)
AND ( (dug.usergroup_id IS NULL)
AND (du.user_id IS NULL) )
AND (`documents`.`startdate` < NOW()
OR `documents`.`startdate` = '0000-00-00 00:00:00' OR `documents`.`startdate` IS NULL)
AND (`documents`.`enddate` > NOW()
OR `documents`.`enddate` = '0000-00-00 00:00:00'
OR `documents`.`enddate` IS NULL)
HAVING (total > 0)
ORDER BY label ASC,
total DESC LIMIT 0,10
You can try to use the statement DISTINCT:
SELECT DISTINCT 'documents'.*,
'documenttypes'.'name' as 'doctype',
'articles'.'id' as 'article_id',
...
GROUP BY lets you use aggregate functions, like AVG, MAX, MIN, SUM, and COUNT which apparently you don't use.

Returning Just count of records found in MYSQL query

I have the following MYSQL query which returns the number of photos found for each record where the number of photos is greater than 0.
SELECT advert_id, (SELECT COUNT( * ) FROM advert_images b WHERE b.advert_id = adverts.advert_id) AS num_photos
FROM adverts
WHERE adverts.approve = '1'
HAVING num_photos > 0
The query works fine, but I want to just return a count of the records found. i.e. the number of records which have at least one photo. I've tried to wrap the whole query in a COUNT, but it gives an error. I want to do this in the query, and not a separate count of records found in php.
SELECT COUNT(*) AS TotalRecords
FROM
(
SELECT a.advert_id, COUNT(*) AS num_photos
FROM adverts AS a
JOIN advert_images AS i
ON i.advert_id = a.advert_id
WHERE a.approve = '1'
GROUP BY a.advert_id
HAVING num_photos > 0
) AS mq
SELECT COUNT(*) FROM (SELECT advert_id, (SELECT COUNT( * ) FROM advert_images b WHERE b.advert_id = adverts.advert_id) AS num_photos
FROM adverts
WHERE adverts.approve = '1'
HAVING num_photos > 0) AS c
This should do the trick

SQL: Forum Order by behaviour (in Codeigniter)

Here's the situation:
In my database i have a table with threads, and a table with replies. Both have a Timestamp field.
Now i am developing a forum and wish to order threads in the following manner:
If the thread has replies, then: ORDER BY tblReply.Timestamp DESC
Else, the thread has no replies: ORDER BY tblThread.Timestamp DESC
I do not know how to combine these 2 in one statement.
My query as it is now:
SELECT `PK_ThreadID`, `Title`, `tblUsers`.`Username`, `tblThread`.`Date`, count(tblReply.FK_ThreadID) AS number_replies FROM (`tblThread`)
JOIN `tblUsers` ON `tblUsers`.`PK_UserID` = `tblThread`.`FK_UserID`
LEFT JOIN `tblReply` ON `tblReply`.`FK_ThreadID` = `tblThread`.`PK_ThreadID`
WHERE `isExpertQuestion` = 0 AND `isPublic` = 1
GROUP BY `PK_ThreadID`
ORDER BY max(tblReply.Date)` desc
//Here it only orders by reply date, so threads with no replies appear at the bottom
How do i achieve the ordering i want in this query?
Like this probably:
SELECT `PK_ThreadID`, `Title`,
`tblUsers`.`Username`,
`tblThread`.`Date`,
count(tblReply.FK_ThreadID) AS number_replies
FROM (`tblThread`)
JOIN `tblUsers` ON `tblUsers`.`PK_UserID` = `tblThread`.`FK_UserID`
LEFT JOIN `tblReply` ON `tblReply`.`FK_ThreadID` = `tblThread`.`PK_ThreadID`
WHERE `isExpertQuestion` = 0 AND `isPublic` = 1
GROUP BY `PK_ThreadID`
ORDER BY
CASE WHEN COUNT(tblReply.FK_ThreadID) > 0 THEN tblReply.Timestamp
WHEN COUNT(tblReply.FK_ThreadID) = 0 OR tblReply.FK_ThreadID IS NULL
THEN tblThread.Timestamp
END DESC

Categories