Mysql query involving multiple tables and an OR statement - mysql

Sorry in advance for an ambiguous title, but I can't think of a good one.
I have 2 tables:
bookings(booking_id, customer_id)
charges(customer_id, booking_id, amount)
where in charges table, either booking_id or customer_id must be entered. Not both.
I'm trying to get amount that are associated with a customer_id
Because the customer_id's are null sometimes in charges table, I have to use booking_id to acquire customer_id through bookings table.
So, I had a query like this:
SELECT c.amount
FROM charges as c, bookings as b
WHERE (c.customer_id = 1234) OR
(c.customer_id = null AND c.booking_id = b.booking_id AND b.customer_id = 1234)
However, it seems to create an infinite loop.
Any suggestions?
Thanks,

The problem is that you are doing a cross join (cartesian product), based on the structure of your where clause.
A better approach is to use left outer join (and proper join syntax). So, join to the bookings table, if it exists. Then use or in the where clause to look for a match on the customer_id in either place:
SELECT c.amount
FROM charges as c left outer join
bookings as b
on c.booking_id = b.booking_id
WHERE (c.customer_id = 1234) OR (b.customer_id = 1234)

Why you don't use a JOIN ? like
SELECT c.amout
FROM charges AS c
LEFT JOIN bookings AS b ON c.bookin_id = b.booking_id
WHERE c.customer_id = 1234 OR b.customer_id = 1234

SELECT amount, customer_id from charges where customer_id = 1234
UNION
select amount, b.customer_id from charges c, bookings b where b.booking_id = c.booking_id and c.customer_id is null and b.customer_id = 1234

Related

fetch id who have register all adv

There are 3 types of adventures for which I used distinct function in query.
There is only one 1 customer who have booked all types of adventures.
The query i used to fetch the data is:
select c.customerid,c.name
from customer c
inner join booking b
on c.customerid = b.customerid
inner join destination d
on b.destinationid=d.destinationid
inner join adventure a
on d.adventureid=a.adventureid
group by c.customerid
having count(distinct b.bid)=(select count(*) from bid)
or count(distinct a.adventuretype)=(
select count(distinct a.adventuretype)
from adventure
)
You can get the customer ids using aggregation and having:
select b.customerid
from booking b join
destination d
on b.destinationid = d.destinationid join
adventure a
on d.adventureid = a.adventureid
group by b.customerid
having count(distinct a.advtype) = 3;
Or, if you don't want to hardcode the "3", you can use:
having count(distinct a.advtype) = (select count(distinct advtype from adventure)
I'll leave it up to you to add in the customer name (using join, exists, or in).

Nested SQL query?

I have 2 tables like this:
product_table[ product_id, product_name,original_store_id, destination_store id]
and
store_table[ store_id, establishment_date, location]
What I want to find is: From all those products that are shipped TO stores with no establishment date, how many of them are shipped FROM stores with establishment date?
This is my query:
SELECT count(a.product_id) as count_of_products
FROM product_table a
JOIN store_table b
ON a.original_store_id = b.store_id AND a.destination_store_id = b.store_id
WHERE b.establishment_date IS NULL
I understand this should be a nested query, but how do I put them here?
You may try adding a second join to the store_table table to further restrict to only products shipped from stores with an establishment date:
SELECT COUNT(a.product_id) AS count_of_products
FROM product_table a
INNER JOIN store_table b
ON a.destination_store_id = b.store_id
INNER JOIN store_table c
ON a.original_store_id = c.store_id
WHERE
b.establishment_date IS NULL AND
c.establishment_date IS NOT NULL;

Using SUM with Joins in MySQL returns unexpected result

I have these tables : customers, customer_invoices, customer_invoice_details, each customer has many invoices, and each invoice has many details.
The customer with the ID 574413 has these invoices :
select customer_invoices.customer_id,
customer_invoices.id,
customer_invoices.total_price
from customer_invoices
where customer_invoices.customer_id = 574413;
result :
customer_id invoice_id total_price
574413 662146 700.00
574413 662147 250.00
each invoice here has two details (or invoice lines) :
first invoice 662146:
select customer_invoice_details.id as detail_id,
customer_invoice_details.customer_invoice_id as invoice_id,
customer_invoice_details.total_price as detail_total_price
from customer_invoice_details
where customer_invoice_details.customer_invoice_id = 662146;
result :
detail_id invoice_id detail_total_price
722291 662146 500.00
722292 662146 200.00
second invoice 662147 :
select customer_invoice_details.id as detail_id,
customer_invoice_details.customer_invoice_id as invoice_id,
customer_invoice_details.total_price as detail_total_price
from customer_invoice_details
where customer_invoice_details.customer_invoice_id = 662147;
result :
detail_id invoice_id detail_total_price
722293 662147 100.00
722294 662147 150.00
I have a problem with this query :
select customers.id as customerID,
customers.last_name,
customers.first_name,
SUM(customer_invoices.total_price) as invoice_total,
SUM(customer_invoice_details.total_price) as details_total
from customers
join customer_invoices
on customer_invoices.customer_id = customers.id
join customer_invoice_details
on customer_invoice_details.customer_invoice_id = customer_invoices.id
where customer_id = 574413;
unexpected result :
customerID last_name first_name invoice_total details_total
574413 terry amine 1900.00 950.00
I need to have the SUM of the total_price of the invoices, and the SUM of the total_price of the details for each customer. In this case I'm supposed to get 950 as total_price for both columns (invoice_total& details_total) but it's not the case. what am I doing wrong & how can I get the correct result please. The answers in similar topics don't have the solution for this case.
When you mix normal columns with aggregate functions (for example SUM), you need to use GROUP BY where you list the normal columns from the SELECT.
The reason for the excessive amount in total_price for invoices is that the SUM is also calculated over each detail row as it is part of the join. Use this:
select c.id as customerID,
c.last_name,
c.first_name,
SUM(ci.total_price) as invoice_total,
SUM((select SUM(d.total_price)
from customer_invoice_details d
where d.customer_invoice_id = ci.id)) as 'detail_total_price'
from customers c
join customer_invoices ci on ci.customer_id = c.id
where c.id = 574413
group by c.id, c.last_name, c.first_name
db-fiddle
I used join against sub queries and then did a sum on the sums
SELECT c.id as customerID,
c.last_name,
c.first_name
SUM(i.sum) as invoice_total,
SUM(d.sum) AS details_total
FROM customers c
JOIN (SELECT id, customer_id, SUM(total_price) AS sum
FROM customer_invoices
GROUP BY id, customer_id) AS i ON i.customer_id = c.id
JOIN (SELECT customer_invoice_id as id, SUM(total_price) AS sum
FROM customer_invoice_details
GROUP BY customer_invoice_id) AS d ON d.id = i.id
WHERE c.id = 574413
GROUP BY c.id, c.name
The issue is in the joining logic. The table customers is used as the driving table in the joins. But in the second join, you are using a derivative key column from the first join, to join with the third tables. This is resulting in a Cartesian output doubling the records from the result from the nth-1 join, which is leading to customer_invoices.total_price getting repeated twice, hence the rolled up value of this field is doubled.
At a high level I feel that the purpose of rolling up the prices is already achieved in SUM(customer_invoice_details.total_price).
But if you have a specific project requirement that SUM(customer_invoices.total_price) should also be obtained and must match with SUM(customer_invoice_details.total_price), then you can do this:
In a separate query, Join customer_invoice_details and customer_invoices. Roll up the pricing fields, and have a result such that you have only one record for one customer ID.
Then use this as a sub-query and join it with the customers table.
You are aggregating along multiple dimensions. This is challenging. I would suggest doing the aggregation along each dimension independently:
select c.id as customerID, c.last_name, c.first_name,
ci.invoice_total,
cid.details_total
from customers c join
(select ci.sum(ci.total_price) as invoice_total
from customer_invoices ci
group by ci.customer_id
) ci
on ci.customer_id = c.id join
(select ci.sum(cid.total_price) as details_total
from customer_invoices ci join
customer_invoice_details cid
on cid.customer_invoice_id = ci.id
group by ci.customer_id
) cid
on cid.customer_id = c.id
where c.id = 574413;
A faster version (for one customer) uses correlated subqueries:
select c.id as customerID, c.last_name, c.first_name,
(select ci.customer_id, sum(ci.total_price) as invoice_total
from customer_invoices ci
where ci.customer_id = c.id
) as invoice_total,
(select ci.customer_id, sum(cid.total_price) as details_total
from customer_invoices ci join
customer_invoice_details cid
on cid.customer_invoice_id = ci.id
where ci.customer_id = c.id
) as details_total
from customers c
where c.id = 574413;

mySQL - inner join three tables

The final question on my database. I have triad several approaches to get the receipt for the customer for the transaction in my DB.
customer_tbl - customer_ID PK
transaction_tbl - customer_ID FK
Payment_tbl - customer_id FK
I wonder if you could please help and based on that I will add game title, rental date, return date and total payment?
The JOIN should take the form of:
SELECT ... FROM customer_tbl c
JOIN transaction_tbl t on c.customer_id = t.customer_id
JOIN Payment_tbl p ON t.payment_id = p.payment_id;
If you want more details, you'll have to provide more information from your side.
Found it:
SELECT t.* , p.* FROM transaction_tbl t INNER JOIN payment_tbl p
ON p.payment_id = t.payment_id WHERE t.customer_id = 1;
This solves my transaction report. I
SELECT t.* , g.game_name , p.*
FROM transaction_tbl t
INNER JOIN game_tbl g ON g.game_id = t.game_id
INNER JOIN payment_tbl p ON p.payment_id = t.payment_id
WHERE t.customer_id = 1;
It works for any given customer (in this case '1') and it selects FKs from transaction_tbl and dates. From customer_tbl you have only customer _id (similar to shops where you get only your loyalty card number) and payment details from payment_tbl. Learned a lot and thanks all for your help.

MySql Select query combine results

Hi im having 2 tables customers and customer_items.
There is now a customer_id in both tables in the 2nd table each customer can have multiple items so the table can be like this
id | item
----------------------------
1501 | pillow
1501 | blanket
1501 | others
1502 | pillow
1502 | blanket
1502 | others
now how can i select with a mysql query the customers that have both pillow and blanket
This is my last approach
select custlist.id FROM customers custlist LEFT JOIN customer_items custitems ON custitems.id=custlist.id WHERE (custitems.items='pillow' AND custitems.items='blanket') UNION ALL
This is a Relational Division problem.
SELECT a.customer_ID
FROM customers a
INNER JOIN customer_items b
ON a.customer_ID = b.customer_ID
WHERE b.item IN ('pillow', 'blanket')
GROUP BY a.customer_ID
HAVING COUNT(*) = 2
SQL of Relational Division
If item is not unique for every customer_ID, a DISTINCT keyword is need to count only unique records.
SELECT a.customer_ID
FROM customers a
INNER JOIN customer_items b
ON a.customer_ID = b.customer_ID
WHERE b.item IN ('pillow', 'blanket')
GROUP BY a.customer_ID
HAVING COUNT(DISTINCT b.item) = 2
Slight variation of the above solution by JW:-
SELECT a.customer_ID
FROM customers a
INNER JOIN (SELECT customer_ID, item FROM customer_items WHERE item = 'pillow' GROUP BY customer_ID) PillowCheck
ON a.customer_ID = PillowCheck.customer_ID
INNER JOIN (SELECT customer_ID, item FROM customer_items WHERE item = 'blanket' GROUP BY customer_ID) BlanketCheck
ON a.customer_ID = PillowCheck.customer_ID
Try this query
SELECT
a.id
FROM
customer_items a
INNER JOIN
customer_items b
ON
a.id= b.id and
a.item = 'PILLOW' AND
b.item='blanket'
if you want customer name then just join customer table. If customer have multiple pillow or blanket then add distinct
SELECT
distinct customer_ID ,item
FROM
customer_items
WHERE item in ('PILLOW', 'blanket')
GROUP BY customer_ID
HAVING COUNT(item) = 2