Using LEFT OUTER JOIN to build query results - php

There are three tables like this:
store_stock:
ID ItemWeaveType ItemModel Cost Price
7 3 4 10.00 15.00
store_item_weaves:
ID WeaveID
3 MC
store_item_models:
ID ModelID
4 HV
I am trying to do a query to gather all of the data for item with the stock ID of 7. As a finished result, I would like an array like:
Array ( [ID] => 7 [ItemWeaveType] => MC [ItemModel] => HV [Cost] => 10.00 [Price] => 15.00)
So, I need to join the data from the tables store_item_weaves and store_item_models.
Here is what I have so far:
$query = $db->query("SELECT * FROM `store_stock` s
left outer join `store_item_weaves` w on w.`ID`=s.`ItemWeaveType`
left outer join `store_item_models` m on m.`ID`=s.`ItemModel`
where s.`ID`=7");
This returns an array like:
Array ( [ID] => 7 [ItemWeaveType] => 3 [ItemModel] => 4 [Cost] => 10.00 [Price] => 15.00 [WeaveID] => MC [ModelID] => HV )
So, I'm almost there. Instead of using the values of WeaveID and ModelID for ItemWeaveType and ItemModel, it is adding it onto the array.
Any ideas?

Use a columns list instead of just * to make sure you get the values you want:
$query = $db->query("SELECT s.ID, w.WeaveId, m.ModelId, s.Cost, s.Price FROM `store_stock` s
left outer join `store_item_weaves` w on w.`ID`=s.`ItemWeaveType`
left outer join `store_item_models` m on m.`ID`=s.`ItemModel`
where s.`ID`=7");

Related

How to get all categories of a product in PHP/SQL

I have an sql request that retrieves all the products in the database with it's categories using mysqli_fetch_all, problem arises when a product has two categories I have twice the product coming back in the result or n times as many categories the product has and I cain't get it to output properly in the html template if someone explained to me in php or sql how to do what i wish thanks.
products
--------
id
name
etc ...
categories
----------
id
name
category_product
----------------
category_id
product_id
$sql = "SELECT
p.id,
p.name,
p.price,
categories.name as category_name
FROM products as p
JOIN category_product
ON p.id = category_product.product_id
JOIN categories
ON category_product.category_id = categories.id";
output => [0] => Array (
[id] => 3
[name] => product 3
[price] => 1.00
[category_name] => cat 4 )
[1] => Array (
[id] => 3
[name] => product 3
[price] => 1.00
[category_name] => cat 3 )
expected => [0] => Array (
[id] => 3
[name] => product 3
[price] => 1.00
[category_name] => Array(
[0] => cat 3
[1] => cat 4 ]))
Change your query to generate a GROUP_CONCAT of category names, then after fetching you can split that value using explode in PHP. For example (assuming you are using MySQLi with a connection $conn):
$sql = "SELECT
p.id,
p.name,
p.price,
GROUP_CONCAT(categories.name SEPARATOR '|') as category_name
FROM products as p
JOIN category_product ON p.id = category_product.product_id
JOIN categories ON category_product.category_id = categories.id
GROUP BY p.id, p.name, p.price";
$result = $conn->query($sql) or die($conn->error);
while ($row = $result->fetch_assoc()) {
// split the category names into an array
$row['category_name'] = explode('|', $row['category_name']);
// do something with the row value
}
Note that you need to specify a separator to GROUP_CONCAT (in this case I've used |) which will not occur in a category name.

Issue with mysql joins

I have two tables
Meetings:
m_id ProjectName
1 Test
2 Test2
Meeting_next:
id fk_m_id Meetingdate status
1 1 9-1-2018 0
1 1 10-1-2018 0
1 1 13-1-2018 1
I want to join this two tables when I left join it I will get duplicate value
Expected output
Array
(
[0] => Array
(
[m_id] => 1
[ProjectName] => test
[meetingDate] =>13-1-2018
)
[1] => Array
(
[m_id] => 2
[ProjectName] => test2
[meetingDate] =>
)
)
I tried -
select * from meetings left join meeting_next on meetings.m_id= meeting_next.fk_m_id where meeting_next.status=1 order by m_id desc
myOutput:
Array
(
[0] => Array
(
[m_id] => 1
[ProjectName] => test
[meetingDate] =>13-1-2018
) )
Bad luck I got only first Project name. I need second too. Please help me. Any help would be appreciated.
Your WHERE condition filters the number of rows to only the row of the first project.
If you want to show both projects, even if there are no meetings with status 1, you need to move the condition to the join condition:
select *
from meetings
left join meeting_next
on meetings.m_id= meeting_next.fk_m_id
and meeting_next.status=1
order by m_id desc
Now you will get all rows from meetings with only the matching entries from meeting_next.

sql join from 3 tables

I have 3 tables
user,
wink,
messages
Im going to clarify what i want because no one seems to understand
SELECT wink.*,user.id,user.points,user.gender,user.firstname,user.email
FROM wink
INNER JOIN user ON
(wink.id_user_from_wink=user.id OR wink.id_user_to_wink=user.id)
WHERE user.id!=?
AND (wink.id_user_from_wink=? OR wink.id_user_to_wink=?)
AND wink.wink_confirmed='1'
This works exactly as i want i to. I get the desired resultset, however now i need to get the last row matching from messages table.
I have tried using LEFT JOIN but it returns to many rows.
I need to return the last matching row for each result i get from the query above
As i said, i have tried:
SELECT wink.*,user.id,user.points,user.gender,user.firstname,user.email
FROM wink
INNER JOIN user ON
(wink.id_user_from_wink=user.id OR wink.id_user_to_wink=user.id)
LEFT JOIN messages ON (messages.id_user_from_message=user.id OR messages.id_user_to_message=user.id)
WHERE user.id!=?
AND (wink.id_user_from_wink=? OR wink.id_user_to_wink=?)
AND wink.wink_confirmed='1'
results:
[2] => Array
(
[id_wink_table] => 18
[id_user_from_wink] => 8
[id_user_to_wink] => 6
[wink_confirmed] => 1
[wink_date] => 2016-07-07 07:19:09
[id] => 8
[points] => 4
[gender] => 0
[firstname] =>
[email] => testuser1#hotmail.com
[id_messages_table] => 5
[id_user_from_message] => 6
[id_user_to_message] => 8
[message_text] => hejsan ss
[message_confirmed] => 0
[message_date] => 2016-07-09 02:43:59
)
[3] => Array
(
[id_wink_table] => 18
[id_user_from_wink] => 8
[id_user_to_wink] => 6
[wink_confirmed] => 1
[wink_date] => 2016-07-07 07:19:09
[id] => 8
[points] => 4
[gender] => 0
[firstname] =>
[email] => testuser1#hotmail.com
[id_messages_table] => 4
[id_user_from_message] => 6
[id_user_to_message] => 8
[message_text] => halluy
[message_confirmed] => 0
[message_date] => 2016-07-09 02:38:13
)
You can see that id_user_to_message and id_user_from_message are dublicate the only thing that differs is the message_text. So i want only the latest matching record from the messages table
EDIT:
SELECT wink.*,user.id,user.points,user.gender,user.firstname,user.email
FROM wink
INNER JOIN user
ON (wink.id_user_from_wink=user.id OR wink.id_user_to_wink=user.id)
**LEFT JOIN HERE returning latest message from messages table and join all rows that is returned when this left join is not used**
WHERE
user.id!=?
AND
(wink.id_user_from_wink=? OR wink.id_user_to_wink=?)
AND
wink.wink_confirmed='1'
/*
create table wink (id_wink_table int, id_user_from_wink int, id_user_to_wink int,wink_confirmed int,wink_date datetime);
insert into wink values
(18, 8,6,1,'2016-07-07 07:19:09'),(18,8,6,1,'2016-07-07 07:19:09');
create table user (id int,points int,gender int,firstaname varchar(20), email varchar(40));
truncate table user;
insert into user values
(8,4,0,null,'testuser1hotmail.com'), (8,4,0,null,'testuser1hotmail.com');
create table messages (id_messages_table int,id_user_from_message int,id_user_to_message int,message_text varchar(20),message_confirmed int,message_date datetime);
truncate table messages;
insert into messages values (5,6,8,'hejsan ss',0,'2016-07-09 02:43:59')
,(4,6,8,'halluy',0,'2016-07-09 02:38:13');
*/
select * from wink;
select * from user;
select * from messages;
SELECT wink.*,user.id,user.points,user.gender,user.firstaname,user.email, m.*
FROM wink
left outer JOIN user ON wink.id_user_from_wink = user.id OR wink.id_user_to_wink = user.id
left outer join messages m on m.id_user_from_message=user.id OR m.id_user_to_message=user.id
where m.id_messages_table =
(select max(m1.id_messages_table) from messages m1 where m.id_user_from_message=m1.id_user_from_message OR m.id_user_to_message = m1.id_user_to_message)

MySQL secondary query is too performance intensive

I have a query where I want to pull in ID's from another table based on the ID of the item selected in another table. I'm currently doing this with an additional query based on the results that I get from the main query. It's resulting in many many additional queries. Is there a way to condense this into 1 query?
SELECT music.id,
SUM(linked_tags.weight) AS total_weight
FROM (music)
INNER JOIN linked_tags ON linked_tags.track_id = music.id
AND linked_tags.tag_id IN (7,56,59)
GROUP BY music.id
ORDER BY total_weight DESC
Then the additional query comes from running the results from the main query through a foreach loop, where 2713 is the ID of an item in the music table.
SELECT tag_id,
weight
FROM (linked_tags)
JOIN tags_en ON tags_en.id = linked_tags.tag_id
WHERE track_id = '2713'
This results in this object, where all_tags is the data that comes from the 2nd query:
[id] => 1500
[name] => Some Track Name
[total_weight] => 10
[all_tags] => Array
(
[0] => 20
[1] => 28
[2] => 4
[3] => 13
[4] => 16
[5] => 7
[6] => 42
[7] => 56
[8] => 61
)
Is there a way to pull this all into 1 query?
You can combine them directly using join:
select tag_id, weight
from (SELECT music.id,
SUM(linked_tags.weight) AS total_weight
FROM music join
linked_tags
ON linked_tags.track_id = music.id AND linked_tags.tag_id IN (7,56,59)
GROUP BY music.id
) m join
linked_tags
on m.id = linked_tags.track_id join
tags_en
ON tags_en.id = linked_tags.tag_id;
EDIT:
If I understand the query correctly, you are trying to get all tags on "tracks" (or "music") that have one or more tags in the set of (7,56,59). And, you want to get the sum of the weights of those three tags.
You can do this in one pass, if you don't mind have the tags in a comma-delimited list:
SELECT m.id,
SUM(case when lt.tag_id IN (7,56,59) then lt.weight end) AS total_weight,
sum(lt.tag_id IN (7, 56, 59)) as NumSpecialTags,
group_concat(lt.tag_id) as AllTags
FROM music m join
linked_tags lt
ON lt.track_id = m.id
GROUP BY m.id
having NumSpecialTags > 0
order by total_weight desc;
You then have to parse the AllTags list at the application layer.

Gathering information in 1 row using related tables

I am trying to build my database using related tables. I am getting the right output from the database, however - Since the user is the same, and the only change in data is the courses, I would like to gather the values "Engelsk" and "Matematik" in the same row, instead of having two outputs which are virtually the same, except the courses.
Is this even possible without having this in the same row in the database? And if so, I'd very much like to know how :)
Array
(
[0] => Array
(
[Type] => Elev
[Username] => test
[Name] => Test Testsen
[Grade] => 9. Klasse
[Course] => Engelsk
)
[1] => Array
(
[Type] => Elev
[Username] => test
[Name] => Test Testsen
[Grade] => 9. Klasse
[Course] => Matematik
)
)
So basically what I would like to achieve is something like this:
Array
(
[0] => Array
(
[Type] => Elev
[Username] => test
[Name] => Test Testsen
[Grade] => 9. Klasse
[Course] => Engelsk, Matematik
)
My query looks like this:
SELECT
*
FROM
lek_Essentials
LEFT JOIN
lek_Type
ON
lek_Essentials.TypeId = lek_Type.TypeId
LEFT JOIN
lek_Grades
ON
lek_Essentials.GradeId = lek_Grades.GradeId
LEFT JOIN
lek_GradeCourses
ON
lek_Grades.GradeId = lek_GradeCourses.GradeId
LEFT JOIN
lek_Courses
ON
lek_GradeCourses.CourseId = lek_Courses.CourseId
LEFT JOIN
lek_Request
ON
lek_Courses.CourseId = lek_Request.CourseId
WHERE
lek_Essentials.UserId = lek_Request.UserId
It's not exactly clear what tables each of the columns in your array are coming from but in MySQL you can use GROUP_CONCAT to aggregate the two rows into one:
SELECT t.type,
e.username,
e.name,
g.grade,
group_concat(c.course) course
FROM lek_Essentials e
LEFT JOIN lek_Type t
ON e.TypeId = t.TypeId
LEFT JOIN lek_Grades g
ON e.GradeId = g.GradeId
LEFT JOIN lek_GradeCourses gc
ON g.GradeId = gc.GradeId
LEFT JOIN lek_Courses c
ON gc.CourseId = c.CourseId
LEFT JOIN lek_Request r
ON c.CourseId = r.CourseId
WHERE e.UserId = r.UserId
group by t.type, e.username, e.name, g.grade

Categories