Unable to implement join properly - php

I am trying to figure out
(book_id, branch_id, total_copies, number_available)
Here is the schema:
Here is as far I have gone:
SELECT
TITLE,
A.BOOK_ID,
A.BRANCH_NAME,
A.BRANCH_ID,
NO_OF_COPIES,
COUNT(BOOK_LOANS.BOOK_ID) AS NUM_OUT,
NO_OF_COPIES - COUNT(BOOK_LOANS.BOOK_ID) AS NUM_AVAIL,
AUTHOR_NAME
FROM
(SELECT
TITLE,
BOOK.BOOK_ID,
BOOK_COPIES.BRANCH_ID,
BRANCH_NAME,
NO_OF_COPIES,
BOOK_AUTHORS.AUTHOR_NAME
FROM
BOOK,
BOOK_COPIES,
LIBRARY_BRANCH,
BOOK_AUTHORS
WHERE
BOOK_COPIES.BOOK_ID=BOOK.BOOK_ID
AND BOOK_AUTHORS.BOOK_ID = BOOK.BOOK_ID
AND (
BOOK.TITLE LIKE :title
OR BOOK.BOOK_ID = :book_id
OR BOOK_AUTHORS.AUTHOR_NAME LIKE :author_name
)
AND BOOK_COPIES.BRANCH_ID=LIBRARY_BRANCH.BRANCH_ID) AS A
LEFT OUTER JOIN
BOOK_LOANS
ON A.BOOK_ID=BOOK_LOANS.BOOK_ID
AND A.BRANCH_ID=BOOK_LOANS.BRANCH_ID
GROUP BY
A.BOOK_ID,
A.BRANCH_ID
But the problem I am encountering is, since BOOK_AUTHORS table has multiple authors for a single book. So I am able to understand that joining only on book_id of BOOK and BOOK_AUTHORS while I should be able to completely using the both book_id and author_name.
So its returning the count 2 times for a single entry in book_loans.
e.g. If a book with book_id lets say 1234 is checked out once, and it has total copies of 2, so it should return remaining copies as 1. But its returning 0.
Please help me understand how to fix this.
Note: I am using php prepare statement.

I didn't pay attention on you query (too complicated to me),
COUNT(DISTINCT BOOK_LOANS.BOOK_ID) rather than COUNT(BOOK_LOANS.BOOK_ID). If you query works well except its count, DISTINCT is helpful.
performance matters. I guess inline view is too complicate. I wonder if changed like this
SELECT
DISTINCT BOOK.BOOK_ID
FROM
BOOK INNER JOIN BOOK_AUTHORS USING(BOOK_ID)
WHERE
BOOK.TITLE LIKE :title
OR BOOK.BOOK_ID = :book_id
OR BOOK_AUTHORS.AUTHOR_NAME LIKE :author_name
Then final query looks like this
SELECT
TITLE,
BOOK.BOOK_ID,
LIBRARY_BRANCH.BRANCH_NAME,
LIBRARY_BRANCH.BRANCH_ID,
BOOK_COPIES.NO_OF_COPIES,
COUNT(distinct BOOK_LOANS.BOOK_ID) AS NUM_OUT,
BOOK_COPIES.NO_OF_COPIES - COUNT(distinct BOOK_LOANS.BOOK_ID) AS NUM_AVAIL,
AUTHOR_NAME
FROM
(
SELECT
DISTINCT BOOK.BOOK_ID
FROM
BOOK,
BOOK_AUTHORS
WHERE
BOOK.TITLE LIKE :title
OR BOOK.BOOK_ID = :book_id
OR BOOK_AUTHORS.AUTHOR_NAME LIKE :author_name
) AS A
INNER JOIN BOOK USING(BOOK_ID)
INNER JOIN BOOK_AUTHORS USING (BOOK_ID)
INNER JOIN BOOK_COPIES USING(BOOK_ID)
INNER JOIN LIBRARY_BRANCH USING(BRANCH_ID)
LEFT OUTER JOIN
BOOK_LOANS
ON BOOK.BOOK_ID=BOOK_LOANS.BOOK_ID
AND LIBRARY_BRANCH.BRANCH_ID=BOOK_LOANS.BRANCH_ID
GROUP BY
BOOK.BOOK_ID,
LIBRARY_BRANCH.BRANCH_ID,
LIBRARY_BRANCH.BRANCH_NAME,
BOOK_COPIES.NO_OF_COPIES,
AUTHOR_NAME

Related

Error joining 3 tables php/sql

I'm new to this, so I know I'm missing something simple, but I can't figure it out. I'm trying to join 3 tables together and I've got it working with 2 joins, but when combined in the same query, there ends up being an error.
My 3 tables are:
TBL_Authors
Author_ID
Author_Name
TBL_Publishers
Publisher_ID
Publisher_Name
TBL_Books
Title
Author_ID
Publisher_ID
ISBN
Genre
Price
Cost
Rating
What I have that isn't working:
$query = 'SELECT * FROM TBL_PUBLISHERS
JOIN TBL_BOOKS ON TBL_PUBLISHERS.Publisher_ID = TBL_BOOKS.Publisher_ID
SELECT * FROM TBL_AUTHORS
JOIN TBL_BOOKS ON TBL_AUTHORS.Author_ID = TBL_BOOKS.Author_ID
ORDER BY TBL_BOOKS.Title ASC;';
This query assumes that each book was published.
SELECT
*
FROM
TBL_Books b
INNER JOIN TBL_Publishers p ON b.Publisher_ID = p.Publisher_ID
INNER JOIN TBL_Authors a ON b.Author_ID = a.Author_ID
ORDER BY
b.Title
Book will always have an Author, but not necessarily a Publisher, if it's not published. If you need to fetch all the books irrespective of whether published or not, you will have to change INNER join on TBL_Publishers to LEFT.
This Query Will show you the result of all details of book, publisher name, author name.
SELECT
t1.* , t2.Publisher_Name , t3.Author_Name
FROM
TBL_Books as t1
INNER JOIN TBL_Publishers as t2 ON t1.Publisher_ID = t2.Publisher_ID
INNER JOIN TBL_Authors as t3 ON t1.Author_ID = t2.Author_ID
ORDER BY
t1.Title
Check This and update if this query helps you.

Most efficient way to perform query

My goal is to get all records from tables where foreign key values match with IDs returned by a subquery from another table.
I have tried several combinations of but they don't even compile.
To clarify, consider following query:
SELECT *
FROM `news`
WHERE IDFIRM IN (SELECT ID FROM firm WHERE Block=0)
AND Actual=1
This is a simple query, but besides the ID in this subquery, I need to also retrieve other columns and return them in response.
Something like this (but of course this doesn't work):
SELECT news.*, sub.name
FROM `news`,
(SELECT * FROM firm WHERE Block=0) AS sub
WHERE news.IDFIRM IN (SELECT sub.ID FROM sub)
AND news.Actual=1
I have an idea how to implement this using joins, but the problem is that there can be multiple IN statements (from different tables), like this:
SELECT *
FROM `news`
WHERE IDFIRM IN (SELECT ID FROM firm WHERE Block=0)
AND Actual=1
AND id_publisher IN (SELECT ID FROM publisher WHERE Block=0)
So this query can become quite different, depending on the request.
How can I create the most efficient query to solve this task? Or it is possible only with multiple queries?
Thanks.
You should definitely use JOIN instead of IN (SELECT ...):
Query 1:
SELECT *
FROM `news`
WHERE IDFIRM IN (SELECT ID FROM firm WHERE Block=0)
AND Actual=1
Replace this by:
SELECT news.*
FROM news
INNER JOIN form ON news.IDFIRM = form.ID
WHERE news.Actual=1
Query 2:
SELECT news.*, sub.name
FROM `news`,
(SELECT * FROM firm WHERE Block=0) AS sub
WHERE news.IDFIRM IN (SELECT sub.ID FROM sub)
AND news.Actual=1
Replace by:
SELECT news.*,
firm.name
FROM news
INNER JOIN firm ON news.IDFIRM = firm.ID
WHERE news.Actual=1
AND firm.Block=0
Query 3:
SELECT *
FROM `news`
WHERE IDFIRM IN (SELECT ID FROM firm WHERE Block=0)
AND Actual=1
AND id_publisher IN (SELECT ID FROM publisher WHERE Block=0)
Replace by:
SELECT news.*
FROM news
INNER JOIN firm ON news.IDFIRM = firm.ID
INNER JOIN publisher ON news.id_publisher = publisher.ID
WHERE news.Actual=1
AND firm.Block=0
AND publisher.Block=0
Adding tables and conditions
So the idea is that you add all the tables you need with clauses like this:
INNER JOIN table1 ON news.<foreign-key> = table1.ID
and that you add the conditions all in the WHEREclause, like this:
WHERE table1.field = <literal>
AND table2.field = <other literal>
AND ...
Assuming your your indexes are sane on both sides of the JOIN they will actually be leveraged, whereas subqueries might use one side.
SELECT [column_list]
FROM news n
INNER JOIN firm f
ON n.idfirm = f.id
INNER JOIN publisher p
ON n.idpublisher = p.id
WHERE n.actual = 1
AND f.block = 0
AND p.block = 0
You are already suggesting a JOIN.
You can use multiple JOIN's to get what you want.
SELECT * FROM news
JOIN firm ON IDFIRM = firm.ID
WHERE Block = 0
JOIN publisher ON id_publisher = publisher.ID
WHERE Block = 0
I'm doing this from memory but should get you close I think.

combine two different queries into one

I have the following query:
SELECT DISTINCT id, title
FROM
((SELECT
DISTINCT offers.id AS id, offers.title AS title
FROM offers
INNER JOIN categories
ON offers.category=categories.title
WHERE categories.title="Fashion clothes"
GROUP BY offers.id
ORDER BY offers.id)
UNION ALL
(SELECT
DISTINCT offers.id AS id, offers.title AS title
FROM offers
INNER JOIN cities
ON offers.city=cities.title
WHERE cities.title="Australia"
GROUP BY offers.id
ORDER BY offers.id)) as subquery
I would like to fetch from the table offers the rows that have category=Fashion clothes and city=Australia but when I use Union it returns all the rows .
I don't know how to make it work. If anyone can help i would appreciate it.
You don't need a union for this. Just join all the tables and have both conditions in you where clause:
SELECT
DISTINCT offers.id AS id, offers.title AS title
FROM offers
INNER JOIN categories
ON offers.category=categories.title
INNER JOIN cities
ON offers.city=cities.title
WHERE categories.title="Fashion clothes" AND cities.title="Australia"
ORDER BY offers.id
As noted by RubahMalam you don't even need the joins as you are joining the tables by title, so the query can be simplified to:
SELECT
DISTINCT offers.id AS id, offers.title AS title
FROM offers
WHERE offers.category="Fashion clothes" AND offers.city="Australia"
ORDER BY offers.id
However, it would probably be best to have separate unique id's in all your tables and use those to join them in your queries, but that's another story.
You just need :
SELECT id,title
FROM offers
WHERE category = "Fashion clothes" OR city = "Australia"
GROUP BY id,title
ORDER BY offers.id
You don't even need INNER JOIN for this. And as patrickh003 said, you don't even need GROUP BY if id is unique column.
If you want both, then you can use aggregation and a having clause:
SELECT o.id, o.title
FROM offers o
WHERE o.category = 'Fashion clothes' AND o.city = 'Australia'
GROUP BY o.id, o.title
HAVING COUNT(*) = 2
ORDER BY o.id;
If you can have duplicates in the offers table, then you need COUNT(DISTINCT o.category) in the HAVING clause instead of COUNT(*).

write mysql query using inner joins for three tables

I have the following query which works perfectly:
SELECT *
FROM contacts
WHERE id in (
SELECT DISTINCT contacts.id
FROM contacts
INNER JOIN contacts2tags
ON contacts.id = contacts2tags.contactid
WHERE tagid in(7,4)
)
Here contacts table contains id, first_name, last_name, ..and tags table contains id, name. contacts2tags table contains contactid and tagid which are same as contacts.id and tags.id respectively
Now, what I want is, to display only the contacts which have both a tagid 7 and a tagid 4.
I tried something like this:
SELECT *
FROM contacts
WHERE id IN
(
SELECT CT1.contactid
FROM
tags T1, contacts2tags CT1, tags T2, contacts2tags CT2
WHERE CT1.contactid = CT2.contactid
AND CT1.tagid = T1.id
AND CT2.tagid = T2.id
AND (T1.id = 7 AND T2.id = 4)
and it works too.
My problem is, I want to convert the above second query to one using inner joins.
I have an array of ids stored in $tmp in php
I want to use those ids and write the above query for them.
How do I do that? I am not comfortable with sql. Might be its a very simple thing to ask.
Thanks in advance
EDIT:
The answer below solved the problem. But the sql runs very slow for 10k records. Any suggestions to optimise it? Pasting the updated query as given in the answer.
SELECT c.id
FROM contacts c
inner join contacts2tags t on c.id = t.contactid
where t.tagid in (7,4)
group by c.id
having count(distinct t.tagid) = 2
This should work
SELECT c.id
FROM contacts c
inner join contacts2tags t on c.id = t.contactid
where t.tagid in (7,4)
group by c.id
having count(distinct t.tagid) = 2

PHP & Mysql - Left Outer Join between two tables

I have two tables called 'events' and 'topics' each table can have many comments.
What I need to do is list all the events and topics with the amount of comments for each row. I've managed to return all the topics, which works great but I don't know how I can add the events table to the MySql. The comments and events table fields are listed below. Can anyone help me with this query?
Events:
ID
Event_Name
Comments:
post_id <-- the releated id for either the events or topics table
table <-- The table that the row belongs to so either topics or events
SELECT
t.id, t.title, c.created_at,
IF(ISNULL(c.allComments), 0, c.allComments) AS totalComments
FROM topics AS t
LEFT OUTER JOIN (
SELECT created_at, post_id, COUNT(*) AS allComments
FROM comments
GROUP BY post_id
) AS c ON c.post_id = t.id
ORDER BY tc.created_at DESC, c.allComments DESC
Sounds like events and topics should be the same table.
Still, I think we can do this with a UNION. Events and Topics have the same columns i hope? (Or at least the same important ones?)
(SELECT c.table as event_or_topic, e.*, count(C.table), MAX(C.created_at) as latest_c
FROM events E LEFT JOIN comments C on (C.post_id = E.id)
WHERE C.table = 'Events'
GROUP BY C.post_id)
UNION
(SELECT c.table as event_or_topic, t.id*, count(C.table), MAX(C.created_at) as latest_c
FROM topics T LEFT JOIN comments C on (C.post_id = E.id)
WHERE C.table = 'Topics'
GROUP BY C.post_id)
ORDER BY latest_c
Notice that the ORDER BY applies to the whole UNION, not the individual SELECTs.
The use of LEFT JOIN should allow those rows without Comments to still show. I think the problem is that we have parts of our select dependent on comments (ie - C.table, ordering on last comment, etc). The count should be fine - will just be zero if there are no comments.
You might need to change the SELECT part slightly. I'd like to display C.table so you know whether a row is a topic or event, but im afraid it might screw up the count. Do you need anything from comments besides the count? You use some columns other than post_id and table in your query that you neglected to explain in your question.
You still have columns I don't know what they are, like Comment's zoneTable
Try this:
SELECT
t.id, t.title, c.created_at, COUNT(c.allComments) AS totalComments
FROM topics AS t LEFT JOIN comments c ON t.id=c.post_id
GROUP BY t.id ORDER BY tc.created_at DESC, c.allComments DESC
If I understand your question you have 3 tables:
-Events
-Topics
-Comments
If that is true something like this should extract all the data:
SELECT *
FROM events,topics
LEFT JOIN comments ON post_ID = ID
ORDER BY date DESC
Hope i'm along the right lines!
W.
I've got it working. If anyone knows of a better and an efficient way of doing this, then please let me know:
(SELECT t.id, t.title, tc.dateCreated AS commentDate,
IF(ISNULL(tc.allComments), 0, tc.allComments) AS totalComments,
t.LastActive as dateChanged
FROM Events AS t
LEFT OUTER JOIN (
SELECT MAX(created_at) AS dateCreated, post_id,
COUNT(*) AS allComments
FROM comments
GROUP BY post_id
) AS tc ON tc.post_id = t.id)
UNION
(SELECT t.id, t.title, tc.dateCreated AS commentDate,
IF(ISNULL(tc.allComments), 0, tc.allComments) AS totalComments,
t.LastActive as dateChanged
FROM topics AS t
LEFT OUTER JOIN (
SELECT MAX(created_at) AS dateCreated, post_id,
COUNT(*) AS allComments
FROM comments
GROUP BY post_id
) AS tc ON tc.post_id = t.id)
ORDER BY commentDate DESC, dateChanged DESC, totalComments DESC

Categories