SQL query optimization for MySql database - php

I have 3 table: user , company and deal.
One user may own several companies. Deal is made between the 2 companies. I need a list of deals, which involved my company.
Deals must contain the following fields: partner_company_id,my_company_id,partner_photo,partner_name,deal_about.
Language code: PHP.
Database: Mysql.
1.List of my company I can get by user ID.
user_id = 22;
companyList = query('SELECT company_id FROM company WHERE user_id = ?', user_id);
2. Then i get deal list where my_company_id is company_first_id
list1 = query('SELECT u.name AS partner_name, u.photo AS partner_photo, d.first_company_id AS
my_company_id , d.second_company_id AS partner_company_id,d.about AS deal_about FROM deal AS d
INNER JOIN company AS c ON c.company_id = d.second_company_id
INNER JOIN user AS u ON u.user_ud = c.user_id
WHERE d.company_first_id IN (?)', companyList);
3. Then i get deal list where my_company_id is company_second_id
list2 = query('SELECT u.name AS partner_name, u.photo AS partner_photo, d.first_company_id AS
partner_company_id , d.second_company_id AS my_company_id,d.about AS deal_about FROM deal AS d
INNER JOIN company AS c ON c.company_id = d.first_company_id
INNER JOIN user AS u ON u.user_ud = c.user_id
WHERE d.company_second_id IN (?)', companyList);
4. then i marge to array and set limit list
list = array_marge(list1,list2);
result = array_slice (list ,0 , 10);
HELP please optimize this queries.
THANKS.
DATABASE SCHEME
user | company | deal |
--------------------------------------------------
user_d | company_id | deal_id
photo | user_id |first_company_id
name | about |second_company_id
| |description

Are your queries so slow? They don't look slow (provided you have indexes on all IDs of course).
However, you can save one database access by combining the two deal queries. Either you simply select query1 UNION ALL query1 or you do it in one pass:
select
u.name AS partner_name,
u.photo AS partner_photo,
d.my_company_id,
d.partner_company_id,
d.about AS deal_about
from
(
select
about,
case when company_first_id in (?) then
company_first_id
else
company_second_id
end as my_company_id,
case when company_first_id in (?) then
company_second_id
else
company_first_id
end as partner_company_id
from deal
where company_first_id in (?) OR d.company_second_id in (?)
) as d
inner join company as c on c.company_id = d.partner_company_id
inner join user as u on u.user_ud = c.user_id

Related

join 3 tables with where clause

This query performs three JOIN operations with 3 tables. But is not ok i see..i'm trying to output all the rows in echo, but i have bad luck.
Mysql table columns:
tours
------
titlu_slider | desc_slider | poza_slider | poza_articol | pret
tours_review
----------
name | time_added | review_text
tours_overview
------------
descriere | titlu_box1 | desc_box1 | titlu_box2 | desc_box2 | titlu_box3 | desc_box3 | titlu_box4 | desc_box4
Php code:
<?php
$db = mysqli_connect("localhost", "root", "fidodido", "antonytravel");
$q = mysqli_query($db,"SELECT * FROM tours INNER JOIN tours_review INNER JOIN tours_overview WHERE id = ".$_GET['id']."");
while ($row = mysqli_fetch_assoc($q)) {
$titlu_slider=$row['titlu_slider'];
$desc_slider=$row['desc_slider'];
$poza_slider=$row['poza_slider'];
$poza_articol=$row['poza_articol'];
$pret=$row['pret'];
## Review table
$name_review=$row['name'];
$time_added=$row['time_added'];
$review_text=$row['review_text'];
## Overview table
$descriere=$row['descriere'];
$titlu_box1=$row['titlu_box1'];
$desc_box1=$row['desc_box1'];
$titlu_box2=$row['titlu_box2'];
$desc_box2=$row['desc_box2'];
$titlu_box3=$row['titlu_box3'];
$desc_box3=$row['desc_box3'];
$titlu_box4=$row['titlu_box4'];
$desc_box4=$row['desc_box4'];
echo '<section class="parallax_window_in" data-parallax="scroll" data-image-src="'.$poza_slider.'" data-natural-width="1400" data-natural-height="470">
<div id="sub_content_in">
<div id="animate_intro">
<h1>'.$titlu_slider.'</h1>
<p>"'.$desc_slider.'"</p>
</div>
</div>';
Some help needed..thanx.
You need to specify how how the tables relate to each other which might look something like the on conditions shown below (which are just guesses)
SELECT *
FROM tours t
INNER JOIN tours_review trev ON t.id = trev.tour_id
INNER JOIN tours_overview tovr ON = t.id = tovr.tour_id
WHERE t.id = $whatever
You then face the issue of what type of join because if you have a tour with no reviews then you probably still want to list it. For that type of relationship you need an "outer join".
SELECT *
FROM tours t
LEFT OUTER JOIN tours_review trev ON t.id = trev.tour_id
INNER JOIN tours_overview tovr ON = t.id = tovr.tour_id
WHERE t.id = $whatever
If every every tour has an "overview" then that can remain an "inner join"
EDIT: Please note that you need to prefix EVERY column reference with a table name or table alias (I have used table aliases to make the query shorter). If you don't do this your query may fail, e.g. if every table has a column id and you just ask for where id = 123 the query will not know which table to use and the query would error.
INNER join shows the records if there are matching record. Use OUTER join to show all records if it does not exists on other tables.
You are missing a few things in your query. Specifically related to the fields that link the tables. To do these joins the best practice is to name each table and then use that name to in an ON statement to JOIN the tables
So
SELECT * FROM tours
INNER JOIN tours_review
INNER JOIN tours_overview
WHERE id = ".$_GET['id'].""
Should be:
SELECT * FROM tours AS t
INNER JOIN tours_review AS r ON r.somefield = t.somefield
INNER JOIN tours_overview AS o ON o.somefield = t.somefield
WHERE id = ".$_GET['id'].""
MySQL can't join tables if it doesn't know what is connecting them.

MySQL Multi-Join Query

I have multiple tables I am trying to grab data from in a single query. I seem to be close to a solution but can not seem to get the data result I am expecting.
Examples of my tables are as follows (fields have been truncated):
Table c
id
name
abbreviation
Table mr (relationship table tat ties tables c and m together by ID)
id
c.id
m.id
Table m
id
Table cnt
id
c.id
Table cmp
id
cnt.id
active
What I WANT is all fields from C, all fields from M where m.id = c.id, all active (active = 1) id's from CMP that match on cnt.id.
My most recent query (after dozens of iterations) is:
SELECT c.id AS id
, c.name AS name
, c.abbreviation AS abbr
, c.active AS active
, c.last_modified AS last_modified
, c.modified_by AS modified_by
, mr.media_id
, mr.related_object_table
, mr.related_object_id
, m.orig_name AS img_name
, m.unique_name AS img_slug
, m.file_type AS confed_file_type
, m.file_size AS file_size
, COUNT('cmp.id') AS comps
FROM confederations AS c
LEFT JOIN media_relationships AS mr
ON mr.related_object_id = c.id
AND mr.related_object_table = 'confederations'
LEFT JOIN media AS m
ON m.id = mr.media_id
INNER JOIN countries AS cnt
ON cnt.confederations_id = c.id
INNER JOIN competitions AS cmp
ON cmp.countries_id = cnt.id
AND cmp.active = 1;
I am not proficient with Joins.
Basically, the result i am expecting is: For each Confederation (table C) I want that confederations name, abbreviation, active status (active), last modified date, modified by; from the Media Relationship table (table MR) I want the image id associated with that confederation so I can use that id to grab the image name and image slug for the confederations primary image from the Media table (M).
Now I also want the total number of Competitions (table CMP) for a given Confederation. Competitions are stored with a Country ID that is tied to the primary key ID of a country in the Countries Table (table CNT). Each Country in table CNT has a Confederations ID. So to get the total number of Competitions per Confederation I am 'trying' to get all Countries within their respective Confederation by CONFEDERATIONS_ID in table CNT, then foreach confederation I want select all the competitions from table CMP with matching COUNTRIES_ID from the group of country id's for that given confederation. (At this point i think i am confusing myself with how to get what i want)
Somehow I am getting the CORRECT NUMBER of competitions, but I am getting duplicate Confederations as results. For Example I am getting something similar to this (assume I have 3 different confederations with 2, 1, and 3 competitions respectively):
Competitions 1 : name 1 | abbreviation 1 | image 1 | total competitions = 2;
Competitions 1 : name 1 | abbreviation 1 | image 1 | total competitions = 1;
Competitions 1 : name 1 | abbreviation 1 | image 1 | total competitions = 3
What am i doing wrong?
Through trial and error, I actually solved this on my own. I came back to post my answer and see Degan's answer, and though it is written differently than mine I think its very close to what I ended up with:
SELECT
cnf.id AS confed_id, cnf.name AS confed_name, cnf.abbreviation AS
confed_abbr, cnf.active AS confed_active, cnf.modified_by AS
confed_mod_by, cnf.last_modified AS confed_last_mod,
COUNT(cnt.id) AS total_countries,
COUNT(cmp.id) AS total_comps,
mr.media_id, mr.related_object_table, mr.related_object_id,
mr.primary_img,
m.orig_name AS img_name, m.unique_name AS img_slug, m.file_type AS file_type
FROM confederations AS cnf
LEFT JOIN media_relationships AS mr
ON mr.related_object_id = cnf.id AND mr.related_object_table = 'confederations'
LEFT JOIN media AS m
ON m.id = mr.media_id
LEFT JOIN countries AS cnt
ON cnt.confederations_id = cnf.id AND cnt.active = 1
LEFT JOIN competitions AS cmp
ON cmp.countries_id = cnt.id AND cmp.active = 1
GROUP BY cnf.id
So farthis seems to be giving me results i can use. I am not certain if my choice of Left Join for all my joins is in fact giving me everything i need (it SEEMS to be) and whether this will omit/add records once the tables get larger. If anyone can point out a problem in my query and my choice of using Left Join as opposed to a combination of LEFT and INNER JOINs as Degan did, that would be helpful.
When aggregating you need to group by.
Perhaps this is close to what you are looking for:
SELECT c.id AS id
, c.name AS name
, c.abbreviation AS abbr
, m.orig_name AS img_name
, SUM('cmp.id') AS comps
FROM confederations AS c
LEFT JOIN media_relationships AS mr
ON mr.related_object_id = c.id
AND mr.related_object_table = 'confederations'
LEFT JOIN media AS m
ON m.id = mr.media_id
INNER JOIN countries AS cnt
ON cnt.confederations_id = c.id
INNER JOIN competitions AS cmp
ON cmp.countries_id = cnt.id
AND cmp.active = 1
GROUP BY c.id AS id
, c.name AS name
, c.abbreviation AS abbr
, m.orig_name AS img_name

MySQL Many to Many select

I'm trying to create a MySQL Many to Many select query. I have three tables:
Profiles
Skills
Profile_skills (Profile ID and Skills ID)
I want to be able to search for a skill (PHP for example) and all profiles with that skill show up in a list, but including all their other skills. Like most freelance sites, so I can also see that they know MySQL etc.
I haven't found or figured out the correct way to get all info about a profile including all their skills. This is my best result so far:
SELECT * FROM profiles INNER JOIN profile_skills ON
profile_skills.profil_id = profiles.profil_id INNER JOIN skills ON
skills.skill_id = profile_skills.skill_id WHERE skill = 'PHP'
But this only gives me the profile with that one skill and not the rest.
Result:
http://imgur.com/E9PEiaU
Expected:
To have the id, Skill "PHP", and the rest of skills, for example "C#" if user id 1 possesses that skill.
http://imgur.com/5bCG4qo
So like this when I search for a profile with a skill, it also shows the rest of that profiles acquired skills.
You can re-join the profile_skills and skills table to get the result you want.
SELECT p.*, s2.*
FROM skills s1
INNER JOIN profile_skills ps1 ON s.skill_id = ps1.skill_id
INNER JOIN profiles p ON ps1.profil_id = p.profil_id
INNER JOIN profile_skills ps2 ON p.profil_id = ps2.profil_id
INNER JOIN skills s2 ON ps2.skill_id = s2.skill_id
WHERE s1.skill = 'PHP'
This will get you a result like this:
| p.id | p.name | s.id | s.skill |
---------------------------------------------
| 1 | rasmus | 1 | PHP |
| 1 | rasmus | 2 | Linux |
| 2 | thomas | 1 | PHP |
| 2 | thomas | 3 | Eating Sandwiches |
There are various different ways to get each profile in one row with a list of skills. One way is with GROUP_CONCAT in MySQL.
SELECT p.profil_id, p.profile_name, GROUP_CONCAT(s2.skill) AS skills
FROM skills s1
INNER JOIN profile_skills ps1 ON s.skill_id = ps1.skill_id
INNER JOIN profiles p ON ps1.profil_id = p.profil_id
INNER JOIN profile_skills ps2 ON p.profil_id = ps2.profil_id
INNER JOIN skills s2 ON ps2.skill_id = s2.skill_id
WHERE s1.skill = 'PHP'
GROUP BY p.profil_id
I added the profile_name column for example, since I don't know what your columns actually are, and you can add other ones, or even still use p.*. Technically, the other columns in profile aren't defined with GROUP BY p.profil_id, and you generally shouldn't select columns that aren't either included in the grouping or some aggregate function, but MySQL will take care of that for you in this case, since they'll all have the same values for each row with the same p.profil_id.
Another way is to just use the first query and do the grouping in PHP:
$previous_id = null;
while ($row = some_fetch_method()) {
if ($row['profil_id'] != $previous_id) {
echo "<h3>$row[profile_name]</h3>";
}
echo "<div>$row[skill]</div>";
}
obviously the HTML won't be quite what you need, but it should be enough to show the general idea.
You probably want a sub query to identify the accounts, and then load details about them with the super query (and GROUP_CONCAT the skills if you want). Something like:
SELECT profiles.*, GROUP_CONCAT(skills.skill SEPARATOR ', ') AS the_skillz
FROM profiles INNER JOIN profile_skills (...) INNER JOIN skills (...)
WHERE profiles.profile_id IN (
SELECT DISTINCT profile_id
FROM profile_skills INNER JOIN skills (...)
WHERE skills.skill = 'PHP'
)
GROUP BY profiles.profile_id;

Select same field on 2 table with same alias

Suppose this:
users
id | name | address
partners
id | name | company | address
Even though the tables are distinct sometimes happens having to associate the users or partners id to the same function ..
For example, the access table
acl
uri | uid | group | operations
Here "uid" can be both user and partner.
How can I read the data of 2 tables with the same alias?
something like:
$selectQuery = <<<QUERY
SELECT A. *,
U.name P.name AS username,
G.name AS groupname
FROM [acl] A
LEFT JOIN [users] U ON A.uid = U.id
LEFT JOIN [partner] P ON A.uid = P.id
LEFT JOIN [groups] G ON A.gid = G.id
WHERE A.id =: id
LIMIT 0.1
QUERY;
You probably want to use COALESCE or IFNULL
IFNULL(U.name, P.name) AS username,
COALESCE(U.name, P.name) AS username,
Both of those will do the exact same thing in this situation. If U.name is not NULL then username will be U.name, otherwise it'll be P.name.
USE UNION ALL with the condition......it should help hopefully

mysql joining two table

table user:
id_u* f_name l_name
----------------------
1 andi mitchel
2 sarah bench
3 kirsty larx
table voucher:
id_v* id_user id_target
1 1 2
2 2 3
quite confused how to join those table with two foreign keys
$db->query("SELECT * FROM voucher v
LEFT JOIN user u ON u.id_u = v.id_user
LEFT JOIN user u1 ON u1.id_u = v.id_target
WHERE .... ")
echoing while loop... and returns nothing??
while($r = $q->fetch_array(MYSQLI_ASSOC)) :
echo $r['u.f_name'];
echo $r['u1.f_name'];
endwhile;
Your JOIN seems absolutely correct. The only issue is that you have joined table user twice, therefore you have columns with same name (like f_name). The database will assign different (but arbitrary) names to these columns. You can override this behaviour with the AS keyword:
$db->query("SELECT v.*
, u.f_name AS user_f_name
, u.l_name AS user_l_name
, ta.f_name AS target_f_name
, ta.l_name AS target_l_name
FROM voucher v
LEFT JOIN user u ON u.id_u = v.id_user
LEFT JOIN user ta ON ta.id_u = v.id_target
WHERE .... ")
Then:
while($r = $q->fetch_array(MYSQLI_ASSOC)) :
echo $r['user_f_name'];
echo $r['target_f_name'];
endwhile;
And I think you can replace the LEFT JOINs with (inner) JOINs. Unless you have id_user or id_target values referencing non-existing userids (id_u).
It looks like you are asking for all people who are in the voucher table regardless of them being in position 1 (user) or position 2 (target)... Then, showing that person's name.
This query does a pre-query of each possible person and their position basis (via WhichPosition).
SELECT STRAIGHT_JOIN
AllVoucherUsers.WhatPosition,
u.*
FROM
( select distinct
v.id_user,
'1' as WhatPosition
from voucher v
union select distinct
v.id_target as id_user,
'2' as WhatPosition
from voucher v
) AllVoucherUsers
join users u
on AllVoucherUsres.id_user = u.id_u
If you only want ONE instance of a given person -- REGARDLESS of their position, just strip out all instances of the "WhatPosition" reference...
SELECT STRAIGHT_JOIN
u.*
FROM
( select distinct
v.id_user
from voucher v
union select distinct
v.id_target as id_user
from voucher v
) AllVoucherUsers
join users u
on AllVoucherUsres.id_user = u.id_u
SELECT * FROM voucher v
LEFT JOIN user u ON u.id_u = v.id_user OR u.id_u = v.id_target
WHERE ....
how about:
SELECT * FROM voucher JOIN user ON id_user = id_u
Simpler still:
SELECT * FROM voucher, user WHERE id_user = id_u

Categories