Optimizing Small MySQL Join Query - php

I have a list of venues in the Venues table, and a list of a cities/states in the Locations table. The venue is associated with an area code unique to my organization, referred to as a SOYID. The SOYID is made up of a geographical area - each row in the Locations table has a City, State, and the corresponding SOYID. Some Venues rows have a SOYID, others do not; for those that do not, I need to find the SOYID for the city and state listed. I only want to select those Venues in a specific SOYID.
This query works, however, it takes a few seconds to load; I don't think I am writing the query correctly. Currently Venues has approx 140 rows, Locations has 40,000.
$sql = "SELECT DISTINCT a.VenueID, a.Name, a.PhotoID, a.City, a.StateAbbr
FROM Venues AS a LEFT JOIN Locations AS c ON a.City = c.city
WHERE a.SOYID = '" . mysql_real_escape_string($SOYID) . "'
OR ((c.city = a.City) AND (c.state = a.StateAbbr) AND (c.SOYID = '" . mysql_real_escape_string($SOYID) . "'))
ORDER BY a.Name ASC";

Any time you reference a column from a LEFT JOINed table (c.state and c.SOYID in your specific case) in the WHERE clause, you force that join to behave like an INNER JOIN. Instead, make those tests part of the join condition:
"SELECT DISTINCT a.VenueID, a.Name, a.PhotoID, a.City, a.StateAbbr
FROM Venues AS a
LEFT JOIN Locations AS c
ON a.City = c.city
AND a.StateAbbr = c.state
AND c.SOYID = '" . mysql_real_escape_string($SOYID) . "'
WHERE a.SOYID = '" . mysql_real_escape_string($SOYID) . "'
OR c.SOYID IS NOT NULL /* LEFT JOIN found a matching row */
ORDER BY a.Name ASC"
EDIT: Based on comments, this version should allow you do remove the DISTINCT requirement:
"SELECT a.VenueID, a.Name, a.PhotoID, a.City, a.StateAbbr
FROM Venues AS a
WHERE a.SOYID = '" . mysql_real_escape_string($SOYID) . "'
OR EXISTS(SELECT NULL
FROM Locations AS c
WHERE a.City = c.city
AND a.StateAbbr = c.state
AND c.SOYID = '" . mysql_real_escape_string($SOYID) . "')
ORDER BY a.Name ASC"

Related

Error on getting information in mysql

I have a public function with query but I don't get all the informations correctly.
public function getNextId($product_id,$product_category_id) {
$query2zz = $this->db->query("
SELECT *
FROM oc_product_to_category a
LEFT
JOIN oc_product b
ON a.product_id = b.product_id
LEFT
JOIN oc_product_description c
ON c.product_id = a.product_id
LEFT
JOIN oc_url_alias d
ON d.query LIKE CONCAT('%', 'a.product_id', '%')
WHERE a.product_id > '" . (int)$product_id . "'
AND a.category_id = '" . (int)$product_category_id . "'
AND b.status = '1'
ORDER
BY a.product_id ASC
LIMIT 1");
The idea is that I want to get next id for a next button. If you visit a product, you can click on next button, and you will go to next product on same category (clothes).
The problem is on oc_url_alias where I should get url for next id, as i mentioned here WHERE a.product_id > '" . (int)$product_id . "' but sometimes it get me wrong url (I mean get url for another product).
Thanks!

Join Statement for Multiple tables

I have a problem with displaying records once despite using inner join.
I have posted a question which have the code here
SELECT transaction_information . *, customer_information . * , property_information . *, borrowers . *, lenders . *, listing_agents . *, payoff . *, sellers . *, selling_agents .*
FROM transaction_information
JOIN customer_information ON transaction_information.entry_no = customer_information.entry_no
JOIN property_information ON transaction_information.entry_no = property_information.entry_no
JOIN borrowers ON transaction_information.entry_no = borrowers.entry_no
JOIN lenders ON transaction_information.entry_no = lenders.entry_no
JOIN listing_agents ON transaction_information.entry_no = listing_agents.entry_no
JOIN payoff ON transaction_information.entry_no = payoff.entry_no
JOIN sellers ON transaction_information.entry_no = sellers.entry_no
JOIN selling_agents ON transaction_information.entry_no = selling_agents.entry_no
";
I have a form at http://208.109.186.112/kushalmn/demo1/. It has two borrower sections and I am using borrowers in one single table. Also, Each section is stored in its own table. For example, Sellers have own table, lenders have own, customer information have own and so forth. If I fill two borrower entries and say customer info section only, I get multiple rows exceeding 100 maybe.
The view page for the form can be viewed here http://208.109.186.112/kushalmn/demo1/view.php.
It seems a problem with Joins . Common Column being Entry no which I am incrementing at each submit.

SQL Query JOIN syntax

I have the following tables and would like to query one more element from them.
categories table ->idcat(int), cat(varchar);
topics table ->idtopic(int), topic(varchar), idcat(int-fk), iduser(int-fk);
replies table ->idreply(int), reply(varchar) iduser(int-fk), idtopic(int-fk)
users table ->iduser(int), username(varchar).
My current query is;
$query = "SELECT t.topic, t.idtopic, u.username
FROM topics t
LEFT JOIN categories c ON t.idcat = c.idcat
LEFT JOIN users u ON t.iduser = u.iduser
WHERE c.idcat = '" . $idcat . "'";
Which presents 'Topic' and 'Username'. I'd like to show 'idReply' as well but don't know the proper JOIN syntax.
SELECT
t.topic,
t.idtopic,
u.username
FROM
topics t
LEFT JOIN
categories c ON t.idcat = c.idcat
LEFT JOIN
users u ON t.iduser = u.iduser
LEFT JOIN // new
replies r ON r.iduser = u.iduser AND r.idtopic = t.idtopic // new
WHERE c.idcat = '" . $idcat . "'";
This will generate a row for every reply to every topic in the specified category. Which could be a lot of records.
You may also want to experiment with the exact type of join on the replies table to get the result you want. LEFT JOIN is probably correct as you'll still get a result if there's no reply to a given topic. This may depend on your flavour of SQL.
OUTER JOIN & LEFT OUTER JOIN are possibilities.
Using INNER JOIN will ensure only topics with replies are returned.
You must have reply_id (foreign key) in your topics table. After that you can use this query.
$query = "SELECT t.topic, t.idtopic, u.username, r.id as reply_id
FROM topics t
LEFT JOIN categories c ON t.idcat = c.idcat
LEFT JOIN users u ON t.iduser = u.iduser
LEFT JOIN replies r ON t.reply_id = r.id
WHERE c.idcat = '" . $idcat . "'";

MySQL Sum with Join

I have been trying to do a sum with a join with no luck. What I need is to get a total from product_price (located in tbl_products) based on a product_id which is located in both tbl_basket and tbl_products.
So far I have:
$result = mysql_query("SELECT * FROM tbl_basket a INNER JOIN tbl_products b ON a.product_id = b.product_id WHERE a.customer_id = '" . $_SESSION['user'] . "'");
This works to get the product_name from tbl_products based on the product_id stored in tbl_basket. I know I need a COUNT which I have tried numerous ways but with no luck. I have chosen to show a query I know works for something as my syntax for the COUNT I tried was way off. Thank you.
Does this not work?
$result = mysql_query("SELECT sum(b.product_price) as total_price FROM tbl_basket a INNER JOIN tbl_products b ON a.product_id = b.product_id WHERE a.customer_id = '" . $_SESSION['user'] . "'")

MySQL: Show latest value when using GROUP BY

My table "orders" includes the date_purchased and my table "orders_products" includes the products_id for the specific order.
I want to list a specific client's all purchased products_id (not all orders!) showing the latest date_purchased for each products_id. The list should be ordered with the latest orders_id of these at the top.
The code below will show all unique products_id as I want, but the "group by" is resulting in not showing the latest orders_id or date_purchased for each products_id…
What am I missing here?
SELECT o.orders_id, o.date_purchased, op.products_id
FROM orders o, orders_products op
WHERE o.customers_id = '" . $client_id . "' and op.orders_id = o.orders_id
GROUP BY op.products_id
ORDER BY orders_id DESC
The not exists approach is often the most efficient approach for this type of query:
SELECT o.orders_id, o.date_purchased, op.products_id
FROM orders o join
orders_products op
on op.orders_id = o.orders_id
WHERE o.customers_id = '" . $client_id . "' and
not exists (select 1
from orders o2 join
orders_products op2
on op2.orders_id = o2.orders_id
where op2.products_id = op.products_id and
o.customers_id = '" . $client_id . "' and
o2.orders_id > o.orders_id
)
ORDER BY orders_id DESC;
The logic is: "Get me all rows from orders where there is no row with the same product and a larger id." This is equivalent to saying: "Get me the max row".
For best performance, you want an index on orders(products_id, orders_id).
EDIT:
There is another approach that uses subtring_index() and group_concat(). This might be the most efficient way, if the filter on customer_id is highly selective (that is, greatly reduces the number of rows).
SELECT max(o.orders_id) as orders_id,
substring_index(group_concat(o.date_purchased order by orders_id desc), ',', 1) as date_purchased,
op.products_id
FROM orders o join
orders_products op
on op.orders_id = o.orders_id
WHERE o.customers_id = '" . $client_id . "'
GROUP BY op.products_id;
Of course, if the date purchased and orders_id are both increasing, you can simplify this to using max() for both:
SELECT max(o.orders_id) as orders_id,
max(o.date_purchased) as date_purchased,
op.products_id
FROM orders o join
orders_products op
on op.orders_id = o.orders_id
WHERE o.customers_id = '" . $client_id . "'
GROUP BY op.products_id;
Using group by the result will be grouped in un-ordered way you cannot rely on that using group by will give you the latest result in each so for this you need to first get the maximum of purchase date and then join with your orders table with using additional condition in on clause
SELECT
o.orders_id,
o.date_purchased,
oo.products_id
FROM
orders o
INNER JOIN (
SELECT orders.orders_id, MAX(orders.date_purchased) date_purchased ,orders_products.products_id
FROM orders
INNER JOIN
orders_products
ON(orders_products.orders_id = orders.orders_id)
GROUP BY orders.orders_id ,orders_products.products_id
) oo
ON( oo.orders_id = o.orders_id AND oo.date_purchased=o.date_purchased)
WHERE o.customers_id = '" . $client_id . "'
ORDER BY o.orders_id DESC
This will give the latest orders per products of customer
You might try this? Cannot test it right now.
SELECT MAX(o.orders_id), MAX(o.date_purchased), op.products_id
FROM orders o, orders_products op
WHERE o.customers_id = 1 and op.orders_id = o.orders_id
GROUP BY op.products_id

Categories