Why is my join not returning anything? - mysql

Involved Entities:
Rental
Id(PK), BorrowerId, BookId, CheckOutWorkerId, CheckOutDate, DueDate, CheckInWorkerId, CheckInDate
Worker
BannerId(PK), FirstName, LastName, Credentials
StudentBorrower
BannerId(PK), FirstName, LastName, BorrowerStatus
Book
Barcode(PK), Title, Author
So I am having issues trying to join two tables together. The rental entity could have two different workers in the table. So I tried this and it yielded an empty table when there should be well over 30 different rows. Any ideas?
SELECT Rental.Id, Book.Title AS BookRented,
CONCAT(StudentBorrower.FirstName, ' ', StudentBorrower.LastName) AS
RentedBy, Rental.CheckOutDate, CONCAT(w1.FirstName, ' ', w1.LastName) AS CheckedOutBy,
Rental.DueDate, Rental.CheckInDate, CONCAT(w2.FirstName, ' ', w2.LastName AS CheckedInBy
FROM Rental
JOIN Book
ON Book.Barcode = Rental.BookId
JOIN StudentBorrower
ON StudentBorrower.BannerId = Rental.BorrowerId
JOIN Worker w1
ON w1.BannerId = Rental.CheckOutWorkerId
JOIN Worker w2
ON w2.BannerId = Rental.CheckInWorkerId
ORDER BY Rental.Id ASC

Related

MySQL - subquery or joining multiple rows

I got a little struggle with my mysql query. Maybe someone can help me.
Tables:
contacts:
|id|phone|first_name|last_name|email|company|
custom_values:
|id|c_id|custom_field_id|value|
The table custom_values got different custom_field_ids
id 4 = mobile
id 5 = fax
id 20 = 2nd phonenumber
The current query gives me all information from my contacts table.
concat(contacts.first_name, ' ', contacts.last_name, ' - ', contacts.company) as displayname, contacts.phone, contacts.last_name, contacts.first_name, contacts.company, contacts.email
from contacts
Now I want to add the information from the custom_values table to the query above.
Is it possible to add 3 rows, that adds me mobile, fax and the 2nd phonenomber to every contact?
I tried with the next query, but that doesn´t work.
SELECT
concat(contacts.first_name, ' ', contacts.last_name, ' - ', contacts.company) as displayname, contacts.phone, contacts.last_name, contacts.first_name, contacts.company, contacts.email,custom_values.value as mobile
from custom_values
join contacts on custom_values.customized_id = contacts.id
where custom_values.custom_field_id=4
thanks guys.
If you want additional rows, then your approach is fine. However, I think you want additional columns, not additional rows.
One approach is multiple LEFT JOIN:
select concat(c.first_name, ' ', c.last_name, ' - ', c.company) as displayname,
c.phone, c.last_name, c.first_name, c.company, c.email,
cvm.value as mobile,
cvf.value as fax,
cvp2.value as second_phone
from contacts c left join
custom_values cvm
on cvm.customized_id = c.id and
cvm.custom_field_id = 4 left join
custom_values cvf
on cvf.customized_id = c.id and
cvf.custom_field_id = 5 left join
custom_values cvp2
on cvp2.customized_id = c.id and
cvp2.custom_field_id = 20;

SQL combining multiple columns from same table while querying multiple tables

I have got three tables that looks as follows.
usedetails [ID,first_name,last_name,telephone,email]
address [ID,streetnumber,streetname,town,county,postcode,userdetailsID]
BOOKING [ID,customerID,pickup_address_id,dropoff_address_id,charge,no_of_passenger]
Address table holds two types of address ie pickoff and dropoff. I would like to display each of the two addresses as one string. The following is my query.
query = "SELECT A.streetnumber,
A.streetname,
A.town,
A.postcode
AS pickup_point
AB.streetnumber,
AB.streetname,
AB.town,
AB.postcode
AS dropoff_point
UD.first_name,
UD.last_name,
UD.telephone,
UD.email
FROM userdetails UD
INNER JOIN booking B
ON B.customerID = UD.ID
INNER JOIN address A
ON B.pickup_address_id = A.ID
INNER JOIN address AB
ON AB.drop_off_address_id = A.ID
WHERE UD.ID = A.userdetailsID OR UD.ID = AB.userdetailsID";
Try CONCAT function:
SELECT CONCAT(A.streetnumber,
' ',
A.streetname,
' ',
A.town,
' ',
A.postcode) AS pickup_point, ...
Or CONCAT_WS function to pass separator as the first argument:
SELECT CONCAT_WS(' ',
A.streetnumber,
A.streetname,
A.town,
A.postcode) AS pickup_point, ...

MySQL SELECT all people matching X and then all in each person's household

I have two tables I need to query to print a list - PURCHASE HISTORY and PEOPLE
PURCHASE HISTORY
----------------
purchase_txnid...purchase_date...purchase_userid...purchase_productid
PEOPLE
------
people_householdid...people_userid...people_street...people_city...people_state...(etc)
I need to get everyone in householdid where someone has purchased productid = "X" (basically a list of everyone at any house where SOMEONE has purchased the product) and then display each of their purchase histories.
PURCHASE HISTORY has seven distinct products and 320,000 records.
Right now, I'm querying all people, RIGHT JOINing the purchase history to select people who purchased "X", and then iterating through the results and, with each result, querying to SELECT everyone in that household and each of their purchase histories. It's extremely slow as there are 45,000 people and 320,000 purchases.
Any ideas as to how I can merge this into a single query or optimize it?
UPDATE
Here are the queries:
$buyers = $db->get_results( " SELECT people.*, TIMESTAMPDIFF( YEAR, birth_date, CURDATE() ) AS age FROM people
RIGHT JOIN purchase_history ON purchase_history.purchase_userid = people.userid
WHERE people.region=$region AND purchase_history.purchase_productid = 'D'
GROUP BY people.userid
ORDER BY street_name ASC, street_suffix ASC, street_num ASC, street_unit ASC, household_id DESC, birth_date ASC
" );
foreach( $buyers as $buyer ){
$in_household = $db->get_results( "SELECT *, TIMESTAMPDIFF( YEAR, birth_date, CURDATE() ) AS person_age FROM people WHERE household_id = '$buyer->household_id' ORDER BY birth_date ASC" );
foreach( $in_household as $person ){
$purchases = $db->get_results( "SELECT * FROM purchase_history WHERE purchase_userid='$person->userid'" );
}
}
SELECT DISTINCT peopleB.userid, peopleB.* FROM purchase
JOIN people AS peopleA ON peopleA.people_userid = purchase_userid
JOIN people AS peopleB ON peopleB.people_householdid = peopleA.householdid
WHERE purchase.purchase_productid = "X"
You can speed up this query by adding these indices to your tables:
CREATE INDEX productid ON purchase (purchase_productid)
CREATE INDEX householdid ON people (people_householdid)
I assume people_userid is already your primary key in people, otherwise you should make it unique:
CREATE UNIQUE INDEX userid ON people (people_userid)
EDIT - You asked for the complete purchase history:
SELECT peopleB.*, phB.* FROM purchase_history AS phA
JOIN people AS peopleA ON peopleA.people_userid = phA.purchase_userid
JOIN people AS peopleB ON peopleB.people_householdid = peopleA.householdid
LEFT JOIN purchase_history AS phB ON phB.purchase_userid = peopleB.userID
GROUP BY peopleB.id, phB.purchase_txnid
WHERE purchase.purchase_productid = "X"
Note that this will give you people's data once for every purchase they made.
Also, I don't know if that query really works the way I want it to or how fast/slow it will be. You could also try reversing the colums in the GROUP BY line and see if that is faster.
You can do this with a subquery and a join:
select p.*
from people p join
(select distinct p.people_householdid
from PurchaseHistory ph join
People p
on ph.purchase_userid = p.people_userid
where ph.purchase_productid = 'X'
) ph
on ph.people_userid = p.people_userid;
EDIT:
If you want to flag the member(s) that made the purchase:
select p.*,
max(p.people_userid = ph.people_userid) as IsPurchaser
from people p join
(select p.people_householdid, p.people_userid
from PurchaseHistory ph join
People p
on ph.purchase_userid = p.people_userid
where ph.purchase_productid = 'X'
) ph
on ph.people_userid = p.people_userid
group by p.people_userid

Is there an easier way to perform this type of query

I have spend most of the day creating this to finally get the result that I am looking for. I am not great with SQL but there must be an easier way to achieve this.
The query is
SELECT *,
Date(review_date
+ INTERVAL review_period_weeks week) AS next_review_date
FROM (SELECT *
FROM (SELECT tbl_candidate_temp.id,
tbl_candidate_temp.last_name,
tbl_candidate_temp.first_name,
tbl_candidate_temp.ni_number,
tbl_candidate_temp.date_of_birth,
tbl_candidate_temp.start_date,
tbl_candidate_temp.expected_end_date,
tam_company_main.id AS company_id,
tam_company_main.name AS company_name,
review_period_weeks
FROM (SELECT tam_candidate.id,
tam_candidate.last_name,
tam_candidate.first_name,
tam_candidate.ni_number,
tam_candidate.date_of_birth,
tam_candidate.start_date,
tam_candidate.expected_end_date,
tam_candidate.company_main_lu,
review_period_weeks
FROM tam_candidate
INNER JOIN (SELECT tam_candidate.id
AS
id,
tam_country.name
AS
award_country_name,
tam_country.review_period_weeks AS
review_period_weeks
FROM tam_candidate
INNER JOIN tam_country
ON
tam_candidate.award_country_lu =
tam_country.id
WHERE reviewer = 1) AS
tbl_candidate
ON tam_candidate.id = tbl_candidate.id) AS
tbl_candidate_temp
INNER JOIN tam_company_main
ON
tbl_candidate_temp.company_main_lu = tam_company_main.id)
AS tbl_candidate_temp2
LEFT JOIN (SELECT tam_candidate_review.candidate_lu,
review_date
FROM tam_candidate_review
ORDER BY review_date DESC) AS tbl_review_temp
ON tbl_candidate_temp2.id = tbl_review_temp.candidate_lu) AS
tbl_candidate_temp3
The candidate table contains the main data, eg name, NI number etc
The country contains the county names, as well as the number of weeks that reviews must take place in that country
The company table contains the names of companies
the reviews tables contain a date, and a candidate number
reviwer is the id of the currently logged in reviewer.
My goal is to have the following columns:
id < candidate id
last_name
first_name
ni_number
date_of_birth
start_date
expected_end_date
company_id
company_name<lookup from company table
review_period_weeks<lookup from country
candidate_lu
review_date < last review
next_review_date < last review + review_period_weeks WEEKS

Join three MySQL tables so that I don't get repeating data

I have three mysql table tblOne, tblTwo and tblThree
SELECT tblOne.bookID AS bookID,
tblOne.title AS title,
tblOne.author AS author,
tblOne.blurb AS blurb,
tblOne.isbn AS isbn,
tblOne.coverImage As coverImage,
CONCAT_WS(" ", tblThree.firstName, tblThree.lastName) AS fullName,
tblTwo.rating,
tblTwo.reviewText AS review,
CONCAT_WS(" ", tblTwo.reviewDate, tblTwo.reviewTime) AS reviewDate
FROM tblTwo
INNER JOIN tblOne
ON tblTwo.bookID = tblOne.bookID
INNER JOIN tblThree
ON tblTwo.userID = tblThree.userID
WHERE tblTwo.bookID = 1000102;
The output populates an XML file, for this book, there are two reviews and as such the data for title, author etc is pulled twice. How can I overcome this as when I try to access the tag from within a JavaScript query, it fails as it says it is undefined. I think that this is due to having more than one result.
The above SQL actually does work, my PHP was erroneous, however...
How can I return the book details even when there is no review? Currently, only books that have been reviewed get their details returned.
You are using INNER JOIN, which requires rows to exist in joined table. Use LEFT JOIN instead, which returns rows from the first-listed table even if rows from the left-joined table don't exist:
SELECT tblBooks.bookID AS bookID,
tblBooks.title AS title,
tblBooks.author AS author,
tblBooks.blurb AS blurb,
tblBooks.isbn AS isbn,
tblBooks.coverImage As coverImage,
CONCAT_WS(' ', tblMembers.firstName, tblMembers.lastName) AS fullName,
IFNULL(tblReviews.rating, '0'),
tblReviews.reviewText AS review,
CONCAT_WS(' ', tblReviews.reviewDate, tblReviews.reviewTime) AS reviewDate
FROM tblBooks
LEFT JOIN tblReviews
ON tblReviews.bookID = tblBooks.bookID
LEFT JOIN tblMembers
ON tblReviews.userID = tblMembers.userID
GROUP BY 1, 2, 3, 4, 5, 7, 8;
Notice that I've listed tblOne first in the FROM clause, which is necessary to get all books even if some have no reviews.
You'll have to deal with null values when there are no reviews. Consider using IFNULL() to generate blanks or a default value, eg
IFNULL(tblTwo.rating, '') -- instead of just tblTwo.rating
Or
IFNULL(tblTwo.reviewText, 'None') -- instead of just tblTwo.reviewText
etc
SQL will always form the cartesian product between the tables meaning that if you have one book with 2 reviews and each review has 3 tags, you will have 6 rows returned to cover all of the data combinations.
You cannot have one book per row if you are joining on a table where the number of expected elements per join is greater than 1. You will need to restructure the XML logic (not sure how you go from SQL -> XML) to cluster taking the redundancies into account.
use GROUP_CONCAT and join the table/table_three results. Try something like:
SELECT tblOne.bookID AS bookID,
tblOne.title AS title,
tblOne.author AS author,
tblOne.blurb AS blurb,
tblOne.isbn AS isbn,
tblOne.coverImage As coverImage,
GROUP_CONCAT(
(SELECT CONCAT_WS(" ", tblThree.firstName, tblThree.lastName)
AS fullName,
tblTwo.rating,
tblTwo.reviewText AS review,
CONCAT_WS(" ", tblTwo.reviewDate, tblTwo.reviewTime)
AS reviewDate
FROM tblTwo INNER JOIN
tblOne ON tblTwo.bookID = tblOne.bookID
INNER JOIN
tblThree ON tblTwo.userID = tblThree.userID
WHERE tblTwo.bookID = 1000102)
SEPARATOR ' '
)
FROM tblTwo INNER JOIN
tblOne ON tblTwo.bookID = tblOne.bookID
INNER JOIN
tblThree ON tblTwo.userID = tblThree.userID
WHERE tblTwo.bookID = 1000102;