Selecting from different tables based on condition - mysql

I Have a table comments ->
id | comment | type | userid |
1 Hello human 9
2 Hi robot 4
3 Gnaw! animal 1
4 Boo ghost 2
Also i have four more tables human,robot,ghost and animal
These tables contains some basic details about themselves...
Now I have a know value of comment say : $id = 3
if i do
$data = mysqli_query($con,"SELECT type FROM comments WHERE id = $id");
while($row = mysqli_fetch_assoc($data)){
$table = $row['type'];
$table_data = mysqli_fetch_assoc(mysqli_query($con,"SELECT * FROM $table"));
}
this will fetch me all the data about the one who commented but this will prove to be too slow....is there any way i can combine this in one single query ?

One way to do this is with left joins.
SELECT c.type, COALESCE(h.detail,r.detail,a.detail,g.detail)
FROM comments
LEFT JOIN human h ON c.type = 'human' AND c.id = h.id
LEFT JOIN robot r ON c.type = 'robot' AND c.id = r.id
LEFT JOIN animal a ON c.type = 'animal' AND c.id = a.id
LEFT JOIN ghost g ON c.type = 'ghost' AND c.id = g.id
Another way would be to do a UNION on the four tables and then join those:
SELECT c.type, q1.detail
FROM comments c
LEFT JOIN
(
SELECT 'human' AS type, detail FROM human
UNION
SELECT 'robot', detail FROM robot
(etc.)
) q1 ON c.type = q1.type AND q1.id = c.id
I would prefer the second option, because this one makes it easier to join lots of detail-columns. I don't think there's much of a difference perfomance-wise.

Related

Joining to table with multiple FKs and Rows

I have the following query:
SELECT p.id,
p.firstname,
**p.address1id,
p.address2id,**
r.invoice_id,
i.authcode
FROM membershiprenewals r,
Profile p,
Invoice i
WHERE r.orgID = 1
and r.period_id = 3
and r.status = 0
and r.profile_id = p.id
and r.invoice_id = i.id;
This table selects a Users Profile and a few related details.
A Profiles Addresses are stored in another table (profileaddress). And a Profile can have 2 addresses. These addresses are referenced using p.address1 and p.address2.
I need to extend this query to join on the profileaddress table to get BOTH addresses and combined into the single record.
So the results I would need would be the following columns
p.id | p.firstname | .. etc .. | profileaddress1.address | profileaddress1.town | profileaddress2.address | profileaddress2.town | .. etc
I've been playing around with JOIN statements for hours, but just can't seem to crack it.
Any help hugely Appreciated !!
Jason
First, never use commas in the FROM clause. Always use proper, explicit JOIN syntax. So, your query should be:
SELECT p.id, p.firstname, **p.address1id, p.address2id,**
r.invoice_id, i.authcode
FROM membershiprenewals r JOIN
Profile p
ON r.profile_id = p.id JOIN
Invoice i
ON r.invoice_id = i.id
WHERE r.orgID = 1 AND r.period_id = 3 AND r.status = 0;
Then you want two joins to the address table:
SELECT p.id, p.firstname, p.address1id, p.address2id,
pa1.address, pa1.town,
pa2.address, pa2.town,
r.invoice_id, i.authcode
FROM membershiprenewals r JOIN
Profile p
ON r.profile_id = p.id JOIN
Invoice i
ON r.invoice_id = i.id LEFT JOIN
profileaddress pa1
ON p.address1id = pa1.id LEFT JOIN
profileaddress pa2
ON p.address2id = pa2.id
WHERE r.orgID = 1 AND r.period_id = 3 AND r.status = 0;
This uses LEFT JOIN in case one of the addresses is missing.

MySQL Joins where one value can be optional

Situation
I have a database which heavily makes use of joins due to the various situations in which each entity is used. Here is a simplified diagram:
Goal
I would like to be able to get details of all modules and the "name" fields regardless of whether the "fk_chapter_id" within user_has_module is set or not.
In the case where "user_has_module.fk_chapter_id" is null, the system can return details of the module and then null chapter.
In the case where there is a user_has_module, I would like to get the status
Issue
Whenever I perform SQL statements, I get the results only partially returned. I.E. If I have 4 module records in total, two of which where the user has an entry in "user_has_module" returns the two records in full and then 2 null records for the other modules.
Update based on feedback, almost there
Now, the only problem is I get duplicates. Using some test data
SELECT DISTINCT
chapter_id,
chapter_name,
module_id,
module_name,
(null ) AS user_module_progress,
(SELECT COUNT(fk_chapter_id) FROM module_has_chapter WHERE fk_module_id = m.module_id) AS chapter_count
FROM
module as m
LEFT JOIN
module_has_chapter as mhc ON m.module_id = mhc.fk_module_id
LEFT JOIN
chapter as c ON mhc.fk_chapter_id = c.chapter_id
group by m.module_id
UNION
SELECT DISTINCT
chapter_id,
chapter_name,
module_id,
module_name,
user_module_progress,
(SELECT COUNT(fk_chapter_id) FROM module_has_chapter WHERE fk_module_id = m.module_id) AS chapter_count
FROM
module as m
LEFT JOIN
user_has_module as uhm ON m.module_id = uhm.fk_module_id
LEFT JOIN
user as u ON uhm.fk_user_id = u.user_id
LEFT JOIN
chapter as c ON uhm.fk_latest_chapter_id = c.chapter_id
WHERE u.user_id = 2
group by m.module_id;
I got there in the end but, not particularly happy about it. This works but, it's a bloody mess...Does anyone have a better solution please?
SELECT DISTINCT
(null) AS chapter_id,
(null) AS chapter_name,
module_id,
module_name,
(null ) AS user_module_progress,
(SELECT COUNT(fk_chapter_id) FROM module_has_chapter WHERE fk_module_id = m.module_id) AS chapter_count
FROM
module as m
LEFT JOIN
user_has_module as uhm ON m.module_id = uhm.fk_module_id
WHERE
uhm.fk_user_id IS NULL
UNION ALL
SELECT DISTINCT
chapter_id,
chapter_name,
module_id,
module_name,
user_module_progress,
(SELECT COUNT(fk_chapter_id) FROM module_has_chapter WHERE fk_module_id = m.module_id) AS chapter_count
FROM
module as m
LEFT JOIN
user_has_module as uhm ON m.module_id = uhm.fk_module_id
INNER JOIN
user as u ON uhm.fk_user_id = u.user_id
INNER JOIN
chapter as c ON uhm.fk_latest_chapter_id = c.chapter_id
WHERE
u.user_id = 2;

SQL JOIN Query - linking four tables

I have the SQL to display ALL the activities and relative Admin permissions (if any) for that activity.
Current SQL Code:
SELECT `activities`.*, `admins`.`admin_role_id`
FROM (`activities`)
LEFT JOIN `admins` ON `admins`.`activity_id`=`activities`.`id` AND admins.member_id=27500
WHERE `activities`.`active` = 1
Returning:
id | name | description | active | admin_role_id (or null)
I then need to detect whether they are an active member within that Activity.
I have the following SQL code:
SELECT DISTINCT `products`.`activity_ID` as joinedID
FROM (`transactions_items`)
JOIN `transactions` ON `transactions`.`id` = `transactions_items`.`id`
JOIN `products` ON `products`.`id` = `transactions_items`.`product_id`
JOIN `activities` ON `activities`.`id` = `products`.`activity_ID`
WHERE `transactions`.`member_id` = 27500
AND `activities`.`active` = 1
Is there any way to merge this into one SQL query. I can't figure out how to use the correct JOIN queries, because of the complexity of the JOINs.
Help please, thanks! :)
Try like this
SELECT `activities`.*, `admins`.`admin_role_id`
FROM (`activities`)
LEFT JOIN `admins` ON `admins`.`activity_id`=`activities`.`id` AND admins.member_id=27500
JOIN (`transactions_items`
JOIN `transactions` ON `transactions`.`id` = `transactions_items`.`id`
JOIN `products` ON `products`.`id` = `transactions_items`.`product_id`)
ON `activities`.`id`=`products`.`activity_ID`
WHERE `transactions`.`member_id` = 27500
AND `activities`.`active` = 1
Seems to me that a query like this would be marginally more comprehensible and (I think) adhere more closely to the spec...
SELECT c.*
, d.admin_role_id
FROM activities c
LEFT
JOIN admins d
ON d.activity_id = c.id
AND d.member_id = 27500
LEFT
JOIN products p
ON p.activity_ID = c.id
LEFT
JOIN transactions_items ti
ON ti.product_id = p.id
LEFT
JOIN transactions t
ON t.id = ti.id
AND t.member_id = 27500
WHERE c.active = 1

SQL one to many?

So this is the problem I am trying to solve.
Category(CatID(PK), CatName,....)
Writer(wtrID(PK), CatID(FK), wtrName,....)
Report(rpID(PK), title)
Authors(rpID(FK),wtrID(FK))
how can I create a list to show the title of the Reports with Authors that come from exactly 3 different categories ?
select r.title, group_concat(w.wtrID) as writer_ids
from Reports r
inner join Authors a on a.rpID = r.rpID
inner join Writer w on w.wtrID = a.wtrID
inner join Category c on c.CatID = w.CatID
group by r.title
having count(distinct c.CatID) = 3

How can I do this with MySQL?

I am using MySQL and have two database tables as follows:
Users
id username
--------------
1 Bill
2 Steve
Objects
user_id key value
----------------------
1 A X
1 B Y
1 C Z
2 A S
2 C T
What query is required to produce the following result?
username A B C
-------------------
Bill X Y Z
Steve S T
I have tried this with an INNER JOIN, but end up with 5 rows (one for each corresponding object row).
Any help much appreciated.
If 'A', 'B', and 'C' are known beforehand, you can do this:
SELECT users.username,
( SELECT objects.value
FROM objects
WHERE objects.user_id = users.id
AND objects.`key` = 'A'
) AS a,
( SELECT objects.value
FROM objects
WHERE objects.user_id = users.id
AND objects.`key` = 'B'
) AS b,
( SELECT objects.value
FROM objects
WHERE objects.user_id = users.id
AND objects.`key` = 'C'
) AS c
FROM users
ORDER
BY users.username
;
select u.username
, oA.value A
, oB.value B
, oC.value C
from users u
left
join objects oA
on u.id = oA.user_id
and oA.key = 'A'
left
join objects oB
on u.id = oB.user_id
and oB.key = 'B'
left
join objects oC
on u.id = oC.user_id
and oC.key = 'C'
perhaps this is not the query that you are asking for, but this is a clean and sipmle query that I have used in your situation:
select objects.*, matrix.*
from
(select users.id, o.key
from users, (select distinct key from objects) as o
)
as matrix left join objects on matrix.id = objects.user_id
and matrix.key = objets.key
order by matrix.id, matrix.key
This query "fills" the empty spaces. So you can consume the resultset with two nested foreach (or whatever similar) and draw the desired table.