MySQL joins - make sure one record is always found - php

I am trying to get a MySQL-query to return some activities on a city's page based on to parameters:
The city's shorttag
The chosem CategoryID's
MySQL Query:
SELECT
a.ActivityID as ActivityID,
a.Name as ActivityName,
a.Description as ActivityDescription,
a.Address as ActivityAddress,
a.Mail as ActivityMail,
a.Shorttag as ActivityShorttag,
a.Phone as ActivityPhone,
(SELECT ap.ThumbPath FROM ActivityPictures ap WHERE Acti_ActivityID = a.ActivityID ORDER BY MainPicture DESC, PictureID ASC LIMIT 1) as ActivityThumbPath,
c.CityID as CityID,
c.Name as CityName,
c.PostalCode as CityPostalCode,
c.Shorttag as CityShorttag,
c.ShortDescription as CategoryShortDescription,
c.LongDescription as CategoryLongDescription,
ca.CategoryID as CategoryID
FROM
Cities c
LEFT JOIN
Activities a
ON
c.CityID = a.City_CityID
LEFT JOIN
ActivityCategoryConn acc
ON
a.ActivityID = acc.Acti_ActivityID
LEFT JOIN
Categories ca
ON
acc.Cate_CategoryID = ca.CategoryID
AND
ca.CategoryID IN (2)
WHERE
c.Shorttag = 'city-shorttag'
ORDER BY
a.ActivityID desc
I want this to ALWAYS return at least one row with the info from "Cities c" - but if no activities are matching the CategoryID, the rest can just be NULL. If there is Activities these should of course return the number of rows matching.
How can I make this query ALWAYS return at least one record containing c.* - even if the array in the "IN"-statement doesnt contain a matched CategoryID?

You can fall back to a null by ORing with a IS NULL statement
AND
(
ca.CategoryID IN (2)
OR
ca.CategoryID IS NULL
)

Related

Left Join - Select ALL from left table but select only the latest from right table

I have 2 tables, borrowers and loans. I want to display on the main page the list of ALL borrowers with or without loans. If with loan, display the newest one.
I have the following sql query, basically it returns the above description except it displays the very first loan of the borrower instead of the latest one.
(Side note: I used GROUP BY to avoid duplicates. Without it the query returns duplicated borrower names if they have multiple loans. Just wanted to know if this is an efficient way of doing so.)
SELECT b.b_id,
b.isdeleted,
b.picture,
b.firstname,
b.middlename,
b.lastname,
b.address,
b.contactno,
b.birthday,
b.businessname,
b.occupation,
b.comaker,
b.comakerno,
b.remarks,
b.datecreated,
b.activeloan,
l.l_id,
l.amount,
l.payable,
l.balance,
l.mode,
l.term,
l.interestrate,
l.amortization,
l.releasedate,
l.duedate,
l.status,
l.c_id
FROM borrowers as b
LEFT JOIN loans as l ON b.b_id = l.b_id
WHERE b.isdeleted = 0
GROUP BY b.b_id
It seems the below query does exactly what i wanted.
I added the below subquery on the "ON" clause.
(SELECT MAX(l_id)
FROM jai_db.loans as l2
WHERE l2.b_id = b.b_id LIMIT 1)
SELECT b.b_id, b.isdeleted, b.picture, b.firstname, b.middlename, b.lastname, b.address, b.contactno,
b.birthday, b.businessname, b.occupation, b.comaker, b.comakerno, b.remarks, b.datecreated, b.activeloan,
l.l_id, l.amount, l.payable, l.balance, l.mode, l.term, l.interestrate, l.amortization,
l.releasedate, l.duedate, l.status, l.c_id
FROM jai_db.borrowers as b
LEFT JOIN jai_db.loans as l
ON l.l_id = (SELECT MAX(l_id)
FROM jai_db.loans as l2
WHERE l2.b_id = b.b_id LIMIT 1)
WHERE b.isdeleted = 0

Php, Mysql sort results based on number of records from another table

I'm joining two tables to display car brands. Here is the structure:
SELECT DISTINCT
b.title
FROM
brands as b
INNER JOIN items as i
ON i.brand_id = b.id
WHERE i.status = 1
ORDER BY COUNT(i.brand_id) DESC;
The above only produces one record. If I remove "ORDER BY COUNT(i.brand_id) DESC;" it displays all the records correctly.
I would like to sort result based on number of vehicles under each brand category. So for example if bmw category has the most car listed under, it should be the first one.
SELECT b.title
FROM brands as b
INNER JOIN items as i
ON i.brand_id = b.id
WHERE i.status = 1
GROUP BY b.title
ORDER BY COUNT(i.brand_id) DESC;
This should work for you.
I would use
SELECT b.title, count(i.brand_id)
FROM items i
LEFT JOIN brands as b
ON b.id = i.brand_id
WHERE i.status = 1
GROUP BY i.brand_id
ORDER BY COUNT(i.brand_id) DESC;
Your concern is the amount of cars in inventory. You want to break them down by how many of each brand you have. So, you're mainly concerned with the items tables and only need the brands table to get the information stored in the items table (brand name). Lastly, in order to get aggregate the number of brands of each, you must let MySQL know what you want in the GROUP BY.
I haven't used an EXPLAIN, but I would think the bottom query is more efficient.
You could join brands on an aggregate query:
SELECT b.title
FROM brands b
INNER JOIN (SELECT brand_id, COUNT(*) AS cnt
FROM items
WHERE status = 1
GROUP BY brand_id) i ON i.brand_id = b.id
ORDER BY cnt DESC;

MySQL - Having problems with COUNT() in query with GROUP BY

I have a pretty big query which is used by an ajax call to return and also sort active items. From my understanding sub queries should be avoided where possible and since this query will be called very often, I would like to do just that.
At the moment everything is fine except for the COUNT(b.bic) AS bids. If there are two(2) bids the query returns four(4), if there are 4, it returns 8, and so on. I've tried grouping by other columns ... but no luck.
Some of the tables. I hope each of the column names are pretty self explanatory:
countries_ship - each item can be shipped to multiple countries so item_id can be duplicate.
id item_id country_id ship_cost
countries
id country_code country_name
item_expire - not sure if item_expire should have it's own table.
id item_id exp_date
bids - Just as countries_ship, item_id can be duplicate. This is where the bids are stored.
id item_id user_id bid previous_bid bid_date
The query:
$q = $this->db->mysqli->prepare("
SELECT c.ship_cost,
c.item_id,
co.country_name,
co.id AS co_id,
i.id,
i.user_id,
i.item_start,
i.item_title,
i.item_number,
i.item_year,
i.item_publisher,
i.item_condition,
i.item_description,
i.item_location,
e.exp_date AS exp_date,
i.active,
CAST(u.fb_id AS CHAR(50)) AS fb_id,
u.user_pic,
MAX(b.bid) AS maxbid,
COUNT(b.bid) AS bids,
p.publisher_name,
t.tag_name
FROM countries_ship c
JOIN items i
ON c.item_id = i.id
JOIN item_expire e
ON c.item_id = e.item_id
JOIN users u
ON i.user_id = u.id
LEFT JOIN bids b
ON i.id = b.item_id
LEFT JOIN publishers p
ON i.item_publisher = p.id
LEFT JOIN tags_rel tr
ON c.item_id = tr.item_id
JOIN tags t
ON t.id = tr.tag_id
LEFT JOIN countries co
ON i.item_location = co.id
WHERE ".$where."
GROUP BY c.item_id ORDER BY ".$order." ".$limit."");
You may try
COUNT(distinct b.bic) AS bids
This will ignore duplicates due to joins

mysql query become slow when data reached upto 100000

I build a query a month ago on a website. It was working fine. But after a month I was informed that the website become very slow to load the page.
When I search for the problem, I found that my query is executing very slow to fetch the data from mysql database. Then I check for the database and found that the 4 tables which I was using by joins, have around 216850, 167634, 64000, 931 rows respectively.
I have already have indexed that tables. So, where I'm lacking. Please help guys.
[Edit]
Table1: user_alert
Records: 216850
DB Type: InnoDB
Indexes: id(primary)
Table2: orders
Records: 167634
DB Type: InnoDB
Indexes: id(primary), order_id, customer_id
Table3: user_registration
Records: 64000 around
DB Type: InnoDB
Indexes: id(primary), email_address
Table4: cities
Records: 931
DB Type: InnoDB
Indexes: id(primary)
Query:
SELECT uas.alert_id, uas.user_id, uas.status, ur.first_name, ur.last_name, ur.email_address, o.order_id,
CASE WHEN ct.city_name IS NULL THEN uas.city_name ELSE ct.city_name END AS city_name
FROM `user_alert` uas
LEFT JOIN orders o ON o.customer_id = uas.user_id
LEFT JOIN user_registration ur ON ur.id = uas.user_id
LEFT JOIN `cities` ct ON ct.city_id = uas.city_id
WHERE uas.status = '1'
GROUP BY uas.user_id
ORDER BY uas.create_date DESC
GROUP BY is used to aggregate values up. For example if you wanted the count of orders by a user you could use COUNT(o.order_id).....GROUP BY uas.user_id. There are multiple orders for each user, but the aggregate function is just counting them here. However if you just select o.order_id when you have a GROUP BY uas.user_id it doesn't know which of the possibly many order_id values to return for that user id.
In this case it possibly doesn't matter as it looks like the order table is the only one where there is multiple rows per use. If you want the latest one you could just use MAX(o.order_id) (assuming that the order_id is assigned is order). But if you wanted the order value it becomes more difficult.
SELECT uas.alert_id, uas.user_id, uas.status, ur.first_name, ur.last_name, ur.email_address, MAX(o.order_id) AS LatestOrderId,
IFNULL(ct.city_name, uas.city_name) AS city_name
FROM `user_alert` uas
LEFT JOIN orders o ON o.customer_id = uas.user_id
LEFT JOIN user_registration ur ON ur.id = uas.user_id
LEFT JOIN `cities` ct ON ct.city_id = uas.city_id
WHERE uas.status = '1'
GROUP BY uas.user_id
ORDER BY uas.create_date DESC
If you wanted the (say) value of the latest order then it becomes more difficult.
SELECT uas.alert_id, uas.user_id, uas.status, ur.first_name, ur.last_name, ur.email_address, Sub1.MaxOrderId AS LatestOrderId, o.order_value
IFNULL(ct.city_name, uas.city_name) AS city_name
FROM `user_alert` uas
LEFT JOIN (SELECT customer_id, MAX(order_id) AS MaxOrderId FROM orders GROUP BY customer_id) Sub1 ON Sub1.customer_id = uas.user_id
LEFT OUTER JOIN orders o ON o.customer_id = Sub1.user_id AND o.order_id = Sub1.MaxOrderId
LEFT JOIN user_registration ur ON ur.id = uas.user_id
LEFT JOIN `cities` ct ON ct.city_id = uas.city_id
WHERE uas.status = '1'
ORDER BY uas.create_date DESC
Or doing a bit of a fiddle based on GROUP_CONCAT
SELECT uas.alert_id, uas.user_id, uas.status, ur.first_name, ur.last_name, ur.email_address,
SUBSTRING_INDEX(GROUP_CONCAT(o.order_id ORDER BY o.order_id DESC), ',', 1) AS LatestOrderId,
SUBSTRING_INDEX(GROUP_CONCAT(o.order_value ORDER BY o.order_id DESC), ',', 1) AS LatestOrderValue,
IFNULL(ct.city_name, uas.city_name) AS city_name
FROM `user_alert` uas
LEFT OUTER JOIN orders o ON o.customer_id = uas.user_id AND o.order_id = Sub1.MaxOrderId
LEFT JOIN user_registration ur ON ur.id = uas.user_id
LEFT JOIN `cities` ct ON ct.city_id = uas.city_id
WHERE uas.status = '1'
GROUP BY uas.user_id
ORDER BY uas.create_date DESC

Last occurrence of foreign key in reference table mysql

I have two tables: return, and return_details
Return is setup like so -
+ Return
- id
- orderNum
- startDate
- endDate
+ Return_Details
- id
- rid (Return.id)
- stage [this is essentially location]
- lastSeen [timeDate last seen)
I am attempting to find all returns that are "open" (where Return.endDate == null) and then the stage and lastSeen for each open Return.id.
The problem is I can't figure out how to find the last occurrence of Return.id in Return_Details. Currently I am able to find the correct lastSeen time using MAX but how do I grab the corresponding stage.
Here's the query I am using now -
SELECT r.so, rd.lastSeen, rd.stage, r.sotype, MAX(rd.lastSeen) as last
FROM repairs r
JOIN repair_details rd ON r.id = rd.rid
WHERE `enddate` IS NULL
GROUP BY r.so
ORDER BY lastSeen asc
Any assistance as to how this could be done with one query would be much appreciated. Thanks in advance!
This should work.
SELECT a.id, b.stage, b.lastSeen
FROM Return a
LEFT JOIN (SELECT rid, stage, MAX(lastSeen) AS lastSeen FROM Return_Details GROUP BY rid, stage) b ON b.rid = a.id
WHERE a.endDate IS NULL
Edit 1:
This method will find the MAX(id) for each rid, then get the details based off that MAX(id).
SELECT a.orderNum, c.stage, c.lastSeen
FROM repairs a
LEFT JOIN
(SELECT rid, MAX(id) AS id FROM repair_details
GROUP BY rid) b ON b.rid = a.id
LEFT JOIN
(SELECT id, stage, lastSeen FROM repair_details) c ON c.id = b.id
WHERE a.endDate IS NULL
SQL Fiddle

Categories