optimizing query (LEFT JOIN) - mysql

My goal is to show search results of companies both: with categories and without (not added yet). My companies table has more or less 12 000 records. Companies with categories are only more or less 200.
There are two search inputs:
$name -> name of company or category.
$id_country -> id of the country
I want to display:
1) how many results is in all database. (that's why i use: SQL_CALC_FOUND_ROWS)
2) i use LIMIT to show 10 results per page (with pagination).
My query:
SELECT SQL_CALC_FOUND_ROWS
c.*,
lc.name as langName,
lc.shortDesc,
lc.longDesc
FROM companies c
JOIN lang_companies lc USING(id_company)
LEFT JOIN categories_companies cc USING(id_company)
LEFT JOIN lang_categories lang_cat USING (id_category)
WHERE
lc.id_lang = '2' AND c.status = 1 AND c.active = 1 AND c.id_country = ".$id_country." AND
(lc.name = LCASE('".$name."') OR (lang_cat.name = LCASE('".$name."') AND lang_cat.id_lang = '2')
OR c.city = '".$name."')
GROUP BY c.id_company
ORDER BY c.id_hierarchi asc
LIMIT 0, 10
This query executes more or less 6 seconds and I want to optimize it. Could you help me?
I will be grateful for any suggestions.

Out of the FROM part of your query, you do not seem to actually use the tables which are joined with these two lines:
LEFT JOIN categories_companies cc USING(id_company)
LEFT JOIN categories cat USING (id_category)
I presume you can simply exclude them from the query, if they are not relevant through something more sublte like the join suppressing rows.

Related

Multiple Inner join blank result without error

Hi dev's i'm new with "advanced" SQL I've try alone but I dont understand how to have the good result.
I'll try to take information from 4 tables in the same DB.
The first table items only have id and name.
2 others tables take the id from items to extract data.
The last tables takes one data from items_buy for print another data.
Lastly I concat 2 column from 2 DB for having a full information.
SELECT items.id, items.name, items_buy.item_cost AS item_cost, items_sales.item_price AS item_price, CONCAT(trader.name, planet.name) AS name_point
FROM ((((items
INNER JOIN items_buy ON items_buy.id = items.id)
INNER JOIN trader ON trader.id = items_buy.name_point)
INNER JOIN items_sales ON items_sales.id = items.id)
INNER JOIN planet ON planet.id = trader.planet)
WHERE items.id = 1;
I dont know how to make it work, she doesnt return an error in SQLyog or on my server.
In order:
ID / NAMEITEM / PRICE / SELLINGPRICE / NAME from concat
If you need more, some test data:
https://pastebin.com/6Bs4kbN9
I've run your test data and run your script against it. As I suggested in my commment, the problem is with the INNER JOIN you are using.
I am not sure whether you are aware, but when using an INNER JOIN, if the joined table is NULL for the current row, then nothing at all will be returned.
If you modify your query to use a LEFT JOIN, you will see the results that are available regardless of whether the joined tables are NULL or otherwise:
SELECT items.id, items.name, items_buy.item_cost AS item_cost, items_sales.item_price AS item_price, CONCAT(trader.name, planet.name) AS name_point
FROM ((((items
LEFT JOIN items_buy ON items_buy.id = items.id)
LEFT JOIN trader ON trader.id = items_buy.name_point)
LEFT JOIN items_sales ON items_sales.id = items.id)
LEFT JOIN planet ON planet.id = trader.planet)
WHERE items.id = 1;
This produces:
1 Agricium 24.45 25.6 NULL
1 Agricium 24.6 25.6 NULL
The problem in the case of your example is that the join to trader or planet has no result and therefore produces no output.

Prevent mysql query from giving multiple rows of same id

At first, it's a bit complicated to create an SQL fiddle with the problem because the data amount is huge. By this I try to drop an screenshot with the result I have.
The problem seems to be small, my query runs perfectly but because different images are linked to different locations in the same city (or cities in range of the distance) I get back two times or more the same image at this time. Because this query is running on 'city' niveau, I just need to get back one image as I need the first one out of the results (supposed to be the closest one to my lan/lon).
The query:
SELECT c.slug
,l.location_id
,fl.flyer_id
,f.salt
,f.NAME
,f.online
,f.cover
,fc.cat_id
,ca.NAME AS cat_name
,lan
,lon
,(6371 * acos(cos(radians(1.5000759112888)) * cos(radians(lan)) * cos(radians(lon) - radians(2.6118285879120)) + sin(radians(1.5000759112888)) * sin(radians(lan)))) AS distance
FROM cities c
INNER JOIN locations l
ON c.slug = l.city_slug
INNER JOIN flyers_locations fl
ON l.location_id = fl.location_id
INNER JOIN flyers_categories fc
ON fl.flyer_id = fc.flyer_id
INNER JOIN categories ca
ON fc.cat_id = ca.id
INNER JOIN flyers f
ON fl.flyer_id = f.id
HAVING distance < 25
AND l.location_id != ''
AND fc.cat_id = '41'
AND f.online = '1'
ORDER BY distance ASC
LIMIT 100
The result
Many thanks
If you donĀ“t want an image to appear twice you could try using distinct in front of the column name.
But if you realy only want to fetch the first one why not just set the limit to 1? This way you would only get the first (closest) row.

Searching a keyword from multiple tables using my query ,

I want to search "law" from three tables tbl_books, tbl_books_author and tbl_books_subject. I am running following query.
SELECT *
FROM tbl_books p, tbl_books_author d, tbl_books_subject m
WHERE p.title = 'law'
OR d.author = 'law'
OR m.subject = 'law'
LIMIT 0,30;
When I run this query it shows on top "Showing rows 0 - 29 (1759260 total, Query took 5.1206 sec)". can you please explain why it is showing this.
What is the reason for this?
As already pointed out, you are doing a Cartesian product i.e. matching all records from first table with all records from 2nd tables and with 3d table.
You probably have some foreign keys relating tables, so your query should look like:
SELECT *
FROM tbl_books p
INNER JOIN tbl_books_author d ON inner_join_condition1
INNER JOIN tbl_books_subject m ON inner_join_condition2
WHERE p.title = 'law'
OR d.author = 'law'
OR m.subject = 'law'
LIMIT 0,30;
fill in with you inner_join_conditions between tables

Comparing two values from the same select query

I have a select query which selects all products from my inventory table and joins them with two other tables (tables l_products and a_products)
SELECT
i.*,
b.title,
ROUND((i.price/100*80) - l.price,2) AS margin,
l.price AS l_price,
a.price AS a_price,
ROUND((a.price/100*80) - l.price, 2) AS l_margin
FROM inventory i
LEFT JOIN products b ON i.id = b.id
LEFT JOIN a_products a ON i.id = a.id
LEFT JOIN l_products l ON i.id = l.id
WHERE
a.condition LIKE IF(i.condition = 'New', 'New%', 'Used%')
AND l.condition LIKE IF(i.condition = 'New', 'New%', 'Used%')
This select query will normally give me a table such as...
id, title, condition, margin, l_price, a_price ...
001-new ... new 10 20 10
001-used ... used 10 25 20
002....
Now I need a condition in the query which will ignore all used products that are more expensive (have a higher a_price) than their 'new' counterparts, such as in the example above you can see that 001-used has a higher a_price than 001-new.
How can I achieve this with out having to resolve to using php
FULL JOIN this query with it self on a column which has a uniquely same value for each id prefix.
You may achieve this effect by adding another field to your SELECT call which produces same unique value for 001-new and 001-used, 002-new and 002-used...
Such value generation can be done by defining your own SQL Routine to extract first 3 characters from a column.

php mysql join 3 tables

Im looping through a feedback type comment system on a users page, finding the latest 6 posts.
To display the post how I want, I have to grab data from 3 tables.
I figure it'd be less db intensive if it was all pulled in one query, but I'm not sure how to write it, being suck at JOINs and things.
This is my existing code
$sql_result = mysql_query("SELECT * FROM feedback WHERE user='1' ORDER BY date desc LIMIT 6", $db);
while($rs = mysql_fetch_array($sql_result)) {
$sql_result2 = mysql_query("SELECT * FROM members WHERE id= '$rs[author]'", $db);
$rs2 = mysql_fetch_array($sql_result2);
if ($rs2[clan] != 0) {
$sql_result3 = mysql_query("SELECT * FROM clans WHERE id= '$rs2[clan]' LIMIT 1", $db);
$rs3 = mysql_fetch_array($sql_result3);
// write comment
Can someone give me a clue?
This should do it:
select * from feedback
left join members on feedback.author = members.id
left join clans on members.clan = clans.id
where feedback.user = 1
left join means if the table on the right has no matching row, a record with all null values is returned, so you still get the data from the table on the left.
I am no expert in Sql myself, but I have picked up a few tricks here and there :-)
A typical LEFT JOIN that works in Firebird is :
select A.*,B.*,C.*
from FEEDBACK A left join MEMBERS B
on A.USER = B.ID left join CLANS C
ON C.ID = A.USER
where A.USER=1
The logic behind the join is that All rows that now share the same value,
A.USER = B.ID = C.ID will now be visible.
The letters A B and C is just used for simplicity.
F, M and C will work the same way.
This Left Join will pick out all and every column in tables. This is done with A.*,B.*,C.*
Maybe you want only a few columns in each table.
That can be accomplished by naming the columns in the same manner.
Example:
A.USER,A.FIRSTNAME,A.SURNAME,B.COLNAME1,B.COLNAME2,C.COLNAME1,C.COLNAME2
When you need to adress the columns later, remember the Prefix of A. or B. or C. before the actual column-name you address.
Good luck and best regards.
Morten, Norway