MySQL Many to Many select - php

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;

Related

Mysql multiple join query issue

I have three tables to be join please see this three tables
composite_inventories
composite_has_inventories
inventories
above three table i have join using below query
SELECT u.id, u.purchase_item_name,u.sales_item_name, us.type ,Group_concat(s.itemcode) as Items FROM composite_inventories as u LEFT JOIN composite_has_inventories as us ON u.id = us.composite_inventory_id LEFT JOIN inventories as s ON US.inventory_id = s.id GROUP BY us.composite_inventory_id,us.type
I got output of my above query as below
but instead of above output i need output should be like this
id | Purchase_item_name | sales_item_name | purchase_items | sales_items
-------------+-------+-----------+----------------------------
12 | golden | NULL | A123,Z523,QQ5252 | NULL
13 | test | demoabc | Z523,QQ5252 | Z523
please help me to convert this query to laravel query.
To achieve what you want it's necessary to join to the inventories table twice, once for purchases and once for sales. Then you can do a GROUP_CONCAT on the items from each of those JOINed tables to get purchase_items and sales_items.
This query should give you the desired result (SQLFiddle)
SELECT u.id, u.purchase_item_name,u.sales_item_name, us.type,
GROUP_CONCAT(p.itemcode) AS purchase_items,
GROUP_CONCAT(s.itemcode) as sales_items
FROM composite_inventories as u
LEFT JOIN composite_has_inventories as us ON u.id = us.composite_inventory_id
LEFT JOIN inventories as s ON US.inventory_id = s.id AND us.type='sale'
LEFT JOIN inventories as p ON US.inventory_id = p.id AND us.type='purchase'
GROUP BY u.id
Output:
id purchase_item_name sales_item_name type purchase_items sales_items
12 golden (null) purchase Z523,QQ5252,A123 (null)
13 test demoabc purchase Z523,QQ5252 Z523

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 joining three tables

I have a simple multiple school management system and I am trying to get total number of teachers, and total number of students for a specific school. My table structures are as follows:
teachers
--------------------------
id | schoolid | Name | etc...
--------------------------
1 | 1 | Bob |
2 | 1 | Sarah|
3 | 2 | John |
students
--------------------------
id | schoolid | Name | etc...
--------------------------
1 | 1 | Jack |
2 | 1 | David|
3 | 2 | Adam |
schools
--------------------------
id | Name | etc...
---------------------------
1 | River Park High |
2 | Stirling High |
I can count just all teachers with the following query:
SELECT COUNT(a.id) AS `totalteachers`
FROM teachers a
LEFT JOIN schools b ON a.schoolid = b.id WHERE b.id = '1'
and similarly I can count the number of teachers with the following query:
SELECT COUNT(a.id) AS `totalstudents`
FROM students a
LEFT JOIN schools b ON a.schoolid = b.id WHERE b.id = '1'
I am however struggling with trying to combine these two queries to get a simple result like this:
totalstudents | totalteachers
--------------------------------
2 | 2
I have tried the following:
SELECT COUNT(a.id) as `totalteachers`, COUNT(c.id) as `totalstudents`
FROM teachers a
LEFT JOIN schools b ON a.schoolid = b.id
LEFT JOIN students c ON c.schoolid=b.id WHERE b.id = '5'
You can do something like this
SELECT
id, name, s.total AS totalstudents, t.total AS totalteachers
FROM schools
JOIN (SELECT schoolid, COUNT(id) AS total FROM teachers GROUP BY schoolid)
AS t ON t.schoolid = id
JOIN (SELECT schoolid, COUNT(id) AS total FROM students GROUP BY schoolid)
AS s ON s.schoolid = id
then you can add where id = 2 or whatever to limit the school.
The problem with the multiple left joins is it generates additional records for each teacher to each student; artifically inflating your counts
There's four ways to solve this: (best imo is what Andrew bone did)
Simply select inline without the joins so the counts are not inflated. (most desirable in my mind as it's easy to maintain)
SELECT (SELECT COUNT(a.id) AS `totalteachers`
FROM teachers a
WHERE A.SchoolID = '1') as TotalTeachers
, (SELECT COUNT(a.id) AS `totalstudents`
FROM students a
WHERE a.SchoolID = '1') as TotalStudents
Use subqueries to get the counts first before the joins, then join. Since count will always be 1 a cross join works.
SELECT totalTeachers, totalStudents
FROM (SELECT COUNT(a.id) AS `totalteachers`
FROM teachers a
LEFT JOIN schools b
ON a.schoolid = b.id
WHERE b.id = '1')
CROSS JOIN (SELECT COUNT(a.id) AS `totalstudents`
FROM students a
LEFT JOIN schools b ON a.schoolid = b.id
WHERE b.id = '1')
Use key word distinct within the count so as not to replicate the counts and negate the artificial inflation (least desirable in my mind as this hides the artifical count increase)
SELECT COUNT(distinct a.id) as `totalteachers`, COUNT(distinct c.id) as `totalstudents`
FROM teachers a
LEFT JOIN schools b ON a.schoolid = b.id
LEFT JOIN students c ON c.schoolid=b.id WHERE b.id = '5'
Another way would be to use a window functions, however these are not available in mySQL.
SELECT COUNT(t.id) AS TotalTeachers, COUNT(st.id) AS TotalStudents
FROM schools s
INNER JOIN teachers t
ON s.id = t.schoolid
INNER JOIN students st
ON s.id = st.schoolid
Try this SQL. I havn't try it but it should work.

SQL query optimization for MySql database

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

mysql fetch all row based on join query and latest date time

Hello I have two tables:
table1 - eod_stock
company_code | open | high | low | ltp | close | ycp | total_volume | total_value | datetime
table 2 - company
ID | code | name
here company code = code so to get all name and other info i used this code:
but first one gives me error and 2nd one returns only one row, but i need all 200 companies with their associated info.
select
company.code,
company.name,
eod_stock.open,
eod_stock.high,
max(eod_stock.datetime)
from
company
right join company on company.code= eod_stock.company_code;
and
select
eod_stock.company_code,
max(eod_stock.close),
eod_stock.total_volume,
eod_stock.total_trade,
eod_stock.high,
eod_stock.low,
eod_stock.ltp,
max(eod_stock.datetime),
company.name
from
eod_stock
inner join company on (company.code = eod_stock.company_code);
but first one gives me error and 2nd one returns only one row, but i need all 200 companies with their associated info.
The trick here is to start with a list of the max datetime for each company_code, which you can do with this basic query:
SELECT company_code, MAX(datetime) AS maxdatetime
FROM eod_stock
GROUP BY company_code
Join this to a query that gets company code, company name, and end-of-day values, and you should be all set:
SELECT
company.code,
company.name,
eod_stock.open,
eod_stock.high
FROM eod_stock
INNER JOIN company ON company.code = eod_stock.company_code
INNER JOIN (
SELECT company_code, MAX(datetime) AS maxdatetime
FROM eod_stock
GROUP BY company_code) maxdt
ON maxdt.company_code = eod_stock.company_code AND
maxdt.maxdatetime = eod_stock.datetime
Your first error i guess that your have to write :
Table2 right join Table1
instead of company right join company
the 2nd one to get all company your full join !!
select
eod_stock.company_code,
max(eod_stock.close),
eod_stock.total_volume,
eod_stock.total_trade,
eod_stock.high,
eod_stock.low,
eod_stock.ltp,
max(eod_stock.datetime),
company.name
from
eod_stock
inner join company on (company.code = eod_stock.company_code) group by eod_stock.company_code;
dagfr was correct all i needed to add group by in the query.

Categories