I have the following table for Customer:
+------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| first | varchar(45) | YES | | NULL | |
| last | varchar(45) | YES | | NULL | |
| password | varchar(45) | NO | | NULL | |
| contact_id | int(11) | NO | PRI | NULL | |
| address_id | int(11) | NO | PRI | NULL | |
+------------+-------------+------+-----+---------+----------------+
And the following structure for Appointment:
+-------------+------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| time | datetime | NO | | NULL | |
| cancelled | tinyint(1) | YES | | 0 | |
| confirmed | tinyint(1) | YES | | 0 | |
| customer_id | int(11) | NO | PRI | NULL | |
+-------------+------------+------+-----+---------+----------------+
I want to use a single query to get the customer information, if they have appointment information, then it'll query it, otherwise it won't.
I am trying to use the following:
CASE
WHEN (SELECT count(a.id) FROM appointment
INNER JOIN customer c ON a.customer_id = c.id)
THEN (SELECT c.first, c.last, c.id, a.id FROM appointent
INNER JOIN customer c ON a.customer_id = c.id)
ELSE
(SELECT c.first, c.last, c.id FROM customer)
END;
Do you have any suggestions?
how about
SELECT * FROM Customer c LEFT JOIN Appointment a ON a.CustomerId = c.Id
You could make two queries and UNION them.
SELECT c.first, c.last, c.id, a.id FROM appointent a
INNER JOIN customer c ON a.customer_id = c.id
UNION
SELECT c.first, c.last, c.id, null FROM customer c
Or an outer join, where the a.id would be populated with null if there was no match during the join.
SELECT c.first, c.last, c.id, a.id FROM customer c
OUTER JOIN appointent a ON a.customer_id = c.id
As per my comment on Zdravko's answer, you could have also used:
select * from customer where id in (select customer_id from appointment where cancelled = 0)
Which would allow you to filter in a nice way.
You can also filter Zdravko's answer like this:
SELECT * FROM Customer c LEFT JOIN Appointment a ON a.CustomerId = c.Id
WHERE a.cancelled 0 and a.confirmed = 1
Related
I have two tables one CUSTOMERS
+-----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| ID | int(11) | NO | PRI | NULL | |
| FIRSTNAME | varchar(50) | YES | | NULL | |
| LASTNAME | varchar(50) | YES | | NULL | |
| ADDRESS | varchar(100) | YES | | NULL | |
+-----------+--------------+------+-----+---------+-------+
and orders
+---------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+-------+
| ID | int(11) | NO | PRI | NULL | |
| PRODUCT_NAME | varchar(100) | YES | | NULL | |
| PRODUCT_PRICE | double(10,4) | YES | | NULL | |
| DATE_ORDER | date | YES | | NULL | |
| ID_CUSTOMER | int(11) | YES | | NULL | |
| AMOUNT | int(11) | YES | | NULL | |
+---------------+--------------+------+-----+---------+-------+
I need to "Get the first and last names of the customers who made orders in total sum greater than the average sum of all orders. Don’t care about duplicates".
Here what I've tried
select FIRSTNAME, LASTNAME, ID,
AVG(PRODUCT_PRICE * AMOUNT)
from CUSTOMERS C
join ORDERS O
on C.ID = ID_CUSTOMER
GROUP BY FIRSTNAME, LASTNAME
HAVING AVG(PRODUCT_PRICE * AMOUNT) < (
select (PRODUCT_PRICE * AMOUNT)
from ORDERS
where C.ID = O.ID_CUSTOMER;
That does not work. I need some help
You need to find average sum of all orders in a subquery.
select
C.ID,
C.FIRSTNAME,
C.LASTNAME,
SUM(O.PRODUCT_PRICE * O.AMOUNT)
from CUSTOMERS C
join ORDERS O
on C.ID = O.ID_CUSTOMER
GROUP BY C.ID, C.FIRSTNAME, C.LASTNAME
HAVING SUM(PRODUCT_PRICE * AMOUNT) > (
select AVG(PRODUCT_PRICE * AMOUNT)
from ORDERS)
Most part of your code is correct, you want just remove the part where C.ID = O.ID_CUSTOMER;. this is not need to calculate the average value of the order items.
using this following code you can get the required outut
SELECT FIRSTNAME,LASTNAME FROM CUSTOMERS
INNER JOIN ORDERS ON CUSTOMERS.ID=ORDERS.ID_CUSTOMER
GROUP BY CUSTOMERS.ID
HAVING SUM(ORDERS.PRODUCT_PRICE*ORDERS.AMOUNT)>
(SELECT AVG(PRODUCT_PRICE*AMOUNT) FROM ORDERS)
I cant seem to figure out how to write these 2 queries on the tables that I have created. The two queries that I am trying to write are
Find users that have reviewed both shops and restaurants.
Find users that reviewed businesses, but not shops or restaurants.
The tables that I am using are
reviews;
+-------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------+------+-----+---------+-------+
| business_id | int(11) | NO | PRI | NULL | |
| user_id | int(11) | NO | PRI | NULL | |
| review_id | int(11) | NO | PRI | NULL | |
| review_date | date | YES | | NULL | |
| star_rating | int(1) | YES | | 1 |
businesses
+--------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+-------+
| business_id | int(11) | NO | PRI | NULL | |
| name | varchar(50) | YES | | NULL | |
| city | varchar(40) | YES | | NULL | |
| state | varchar(20) | YES | | NULL | |
| full_address | varchar(120) | YES | | NULL | |
users;
+------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| user_id | int(11) | NO | PRI | NULL | |
| name | varchar(50) | YES | | NULL | |
| user_since | date | YES | | NULL
explain is_a_restaurant;
+--------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| business_id | int(11) | NO | PRI | NULL | |
| cuisine_type | varchar(20) | YES | | NULL | |
| total_seats | int(11) | YES | | 1 | |
+--------------+-------------+------+-----+---------+-------+
explain is_a_shop;
+-------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------+-------+
| business_id | int(11) | NO | PRI | NULL | |
| shop_type | varchar(50) | YES | | NULL | |
I honestly dont know where to even start. I know I am going to join the businesses with the business ID but I dont know how I am going to find people who reviewed both shops and restaurants. can anyone help?
EDIT: WHat I have tried
For the first query: Find users that have reviewed both shops and restaurants.
SELECT b.business_id
FROM is_a_shop b
JOIN reviews r
ON r.business_id = b.business_id
JOIN is_a_restaurant k
ON r.business_id = k.business_id;
Here's what the first one should look like: you can change the select statement based on what info you want to see.
SELECT u.name
FROM users u
INNER JOIN reviews rev ON rev.user_id = u.user_ID
WHERE EXISTS
(SELECT *
FROM is_a_shop s
WHERE s.business_id = rev.business_id)
OR EXISTS
(SELECT *
FROM is_a_restaurant r
WHERE r.business_id = rev.business_id)
This basically pulls all the users who wrote reviews where the business_id is found in the is_a_shop table or is_a_restaurant. you should be able to figure out the second query easily from this.
Like most things SQL these queries can be solved in a number of different ways. These solutions should be pretty easy to understand:
Find users that have reviewed both shops and restaurants.
-- solution 1 - using joins:
select u.name
from users u
join (
select r.user_id
from reviews r
join is_a_restaurant i on i.business_id = r.business_id
) resturant_reviews on u.user_id = resturant_reviews.user_id
join (
select r.user_id
from reviews r
join is_a_shop i on i.business_id = r.business_id
) shop_reviews on u.user_id = shop_reviews.user_id
-- solution 2: using exists - probably faster than solution 1
select u.name
from users u
join reviews r on u.user_id = r.user_id
where exists (
select 1 from is_a_restaurant i
where i.business_id = r.business_id
) and exists (
select 1 from is_a_shop i
where i.business_id = r.business_id
)
Find users that reviewed businesses, but not shops or restaurants.
-- solution 1: using not in - probably faster than solution 2
select u.name from users u
join reviews r on u.user_id = r.user_id
where r.business_id not in
(
select business_id from is_a_restaurant
union all
select business_id from is_a_shop
)
-- solution 2: using exists
select u.name from users u
join reviews r on u.user_id = r.user_id
where not exists (
select business_id from is_a_restaurant
where r.business_id = business_id
)
and not exists (
select business_id from is_a_shop
where r.business_id = business_id
)
This is very basic, I know, but I tend to happen on this issue often.
Tables
mysql> describe Posts;
+-----------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| Time | int(11) | YES | MUL | NULL | |
+-----------+---------+------+-----+---------+----------------+
mysql> describe PostCategories;
+----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| pid | int(11) | YES | | NULL | |
| Category | varchar(20) | YES | | NULL | |
+----------+-------------+------+-----+---------+----------------+
Query
SELECT P.id AS pid, P.Time, PC.Category FROM Posts P
LEFT OUTER JOIN PostCategories PC ON PC.pid = P.id
WHERE PC.Category IN('value1','value2') OR PC.Category IS NULL
ORDER BY P.Time DESC
Returns
+-----+------------+----------+
| pid | Time | Category |
+-----+------------+----------+
| 8 | 1396906256 | NULL |
| 7 | 1396524835 | value1 |
| 7 | 1396524835 | value2 |
+-----+------------+----------+
Desired outcome
I would like it to only give me one row for every pid. In other words, no matter how many categories the Post have, I want it to only result in one row in the result dataset.
+-----+------------+----------+
| pid | Time | Category |
+-----+------------+----------+
| 8 | 1396906256 | NULL |
| 7 | 1396524835 | value1 |
+-----+------------+----------+
Category result does not matter, I will not fetch it once it works as I want it to.
SELECT P.id AS pid, MIN(P.Time) as first_post_time, MAX(PC.Category) as Category FROM Posts P
LEFT OUTER JOIN PostCategories PC ON PC.pid = P.id
WHERE PC.Category IN('value1','value2') OR PC.Category IS NULL
GROUP BY P.id
ORDER BY P.Time DESC
Just add group by and calculate time and category (use min or max)
Group posts by using Group By and you can also get all categories if you want by using group_concat procedure in mysql.
SELECT P.id AS pid, P.Time, GROUP_CONCAT(PC.Category, ' , ') FROM Posts P
LEFT OUTER JOIN PostCategories PC ON PC.pid = P.id
WHERE PC.Category IN('value1','value2') OR PC.Category IS NULL
GROUP BY P.pid
ORDER BY P.Time DESC
I have been using the following query:
I am using two tables: (there are some others mentioned but not needed for this question)
assessment_criteria
+-------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+--------------+------+-----+---------+----------------+
| id | mediumint(9) | NO | PRI | NULL | auto_increment |
| scheme_of_work_id | mediumint(9) | NO | | NULL | |
| level | char(255) | YES | | NULL | |
| criteria | char(255) | NO | | NULL | |
+-------------------+--------------+------+-----+---------+----------------+
criteria_completed
+------------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------------+--------------+------+-----+---------+----------------+
| id | mediumint(9) | NO | PRI | NULL | auto_increment |
| student_ID | mediumint(9) | NO | | NULL | |
| assessment_criteria_id | mediumint(9) | NO | | NULL | |
| date_marked | date | NO | | NULL | |
| notes | varchar(255) | YES | | NULL | |
| attainment | varchar(15) | YES | | NULL | |
| effort | varchar(15) | YES | | NULL | |
| marked_by | varchar(20) | NO | | NULL | |
+------------------------+--------------+------+-----+---------+----------------+
I was using a query like this to display a list of assessment criteria that a student HAS NOT completed:
SELECT DISTINCT assessment_criteria.id, assessment_criteria.level, assessment_criteria.criteria FROM assessment_criteria, criteria_completed
WHERE (assessment_criteria.scheme_of_work_id = '17')
AND (assessment_criteria.id NOT IN (SELECT criteria_completed.assessment_criteria_id FROM criteria_completed WHERE (student_ID = '403')))
ORDER BY level;
This query has become incredibly slow to run, I have been trying to make it faster using LEFT JOIN.
SELECT DISTINCT a.id, a.level, a.criteria
FROM assessment_criteria a
LEFT JOIN criteria_completed b
ON a.id = b.assessment_criteria_id
WHERE b.assessment_criteria_id IS NULL
But I am having no success when I try to add in clauses for project and student; ie.
SELECT DISTINCT a.id, a.level, a.criteria
FROM assessment_criteria a
LEFT JOIN criteria_completed b
ON a.id = b.assessment_criteria_id
WHERE b.assessment_criteria_id IS NULL
AND (b.student_ID = '403')
AND (a.scheme_of_work_id = '17');
mysql reports "empty set". I suspect I am referencing these foreign keys incorrectly?
(Just to confirm, you are using b.assessment_criteria_id IS NULL to detect failed joins)
Applying the filters on table b to the WHERE clause will filter out any records where the join has failed, which I believe is the cause of the problem.
You can try moving the b filters into the JOIN condition:
SELECT DISTINCT a.id, a.level, a.criteria
FROM assessment_criteria a
LEFT JOIN criteria_completed b
ON a.id = b.assessment_criteria_id
AND (b.student_ID = 403)
WHERE b.assessment_criteria_id IS NULL
AND (a.scheme_of_work_id = 17);
Although personally, I dislike filtering like this in a JOIN. The alternative would be:
SELECT DISTINCT a.id, a.level, a.criteria
FROM assessment_criteria a
LEFT JOIN criteria_completed b
ON a.id = b.assessment_criteria_id
WHERE (a.scheme_of_work_id = 17)
AND (b.assessment_criteria_id IS NULL OR b.student_ID = 403);
I have a table with following structure,
+-----------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------+------------------+------+-----+---------+----------------+
| location_id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| name | varchar(60) | NO | | NULL | |
| city_id | int(10) unsigned | NO | MUL | NULL | |
| parent_location_id | int(11) | NO | | NULL | |
+-----------------------+------------------+------+-----+---------+----------------+
Here one location may or may not have a parent_location_id, which is another row in the same table.
Now I want to select all location which have atleast one sublocation. I have the following query. Is it correct?
SELECT DISTINCT (
a.location_id
), a.name, a.is_delivery_available, a.is_booking_available
FROM `locations` a
JOIN `locations` b ON a.location_id = b.parent_location_id
WHERE a.`city_id` =5
ORDER BY a.name ASC
use LEFT JOIN instead of INNER JOIN
SELECT a.location_id
a.name,
a.is_delivery_available,
a.is_booking_available
FROM `locations` a
LEFT JOIN `locations` b
ON a.location_id = b.parent_location_id
WHERE a.`city_id` = 5
ORDER BY a.name ASC