fetching values from database which are not from specific month - mysql

I am trying to fetch hotel id, hotel name and hotel type of hotels which has not taken any orders in the month of 'MAY 19' but i am not getting proper output what is wrong in my query?
select hotel_details.hotel_id,hotel_name,hotel_type
from hotel_details inner join orders on hotel_details.hotel_id=orders.hotel_id
where Month(order_date) between 1 and 4 or Month(order_date) between 6 and 12
order by hotel_id;

You can use the following, using NOT EXISTS to check if there is any order for the hotel in May 2019:
SELECT hotel_id, hotel_name, hotel_type
FROM hotel_details
WHERE NOT EXISTS (
SELECT 1
FROM orders
WHERE hotel_id = hotel_details.hotel_id
AND MONTH(order_date) = 5
AND YEAR(order_date) = 2019
)
The sub-query on EXISTS checks if the hotel_id is available in orders on May 2019. Using NOT in front of EXISTS filters all hotels which have no order in May 2019. The sub-query is connected to the outer part of the query with hotel_id = hotel_details.hotel_id.

Here's a standard, if somewhat old-fashioned approach...
(I've assumed a column name on the orders table, but you can change it to any non-nullable orders column, if it's wrong)
SELECT d.hotel_id
, d.hotel_name
, d.hotel_type
FROM hotel_details d
LEFT
JOIN orders o
ON d.hotel_id = o.hotel_id
AND d.order_date >= '2019-05-01'
AND d.order_date < '2019-06-01'
WHERE o.id IS NULL
ORDER
BY d.hotel_id;
For next time, see: Why should I provide an MCRE for what seems to me to be a very simple SQL query?

SELECT HOTEL_ID,HOTEL_NAME,HOTEL_TYPE FROM HOTEL_DETAILS
WHERE HOTEL_ID NOT IN
(SELECT HOTEL_ID FROM ORDERS
WHERE MONTH(ORDER_DATE) = 5)
ORDER BY HOTEL_ID ASC;
Here in the below sub query we are trying to obtain the HOTEL_ID(s) which have placed order in the month of May using the MONTH function. In outer query which receives a list of HOTEL_ID(s) which have an ordered in the month of may. Now the NOT IN condition omits the HOTEL_ID present in the list and displays the other HOTEL_ID which have not ordered in the month of May.

SELECT DISTINCT h.hotel_id,
h.hotel_name,
h.hotel_type
FROM hotel_details h
WHERE h.hotel_id NOT IN (SELECT od.hotel_id
FROM orders od
WHERE ( h.hotel_id = od.hotel_id
AND Month(order_date) = 05 )
GROUP BY h.hotel_id
ORDER BY h.hotel_id ASC);

Use Nested Queries:
SELECT hotel_id, hotel_name, hotel_type
FROM hotel_details
WHERE hotel_id NOT IN (
SELECT DISTINCT hotel_id
FROM orders
WHERE order_date BETWEEN '2019-05-01' AND '2019-05-31'
)
ORDER BY hotel_id;
Explanation :
In the Inner Query, we are selecting distinct hotel IDs from the order table with orders between May 1 and May 31.
Once we have list of hotel IDs, in the outer query we can display the required columns of the hotel table which have IDs not in the list.

Related

Trying to make a new table by pulling data from two tables and keep getting 'Error: Every derived table must have its own alias' on this query

I have an 'Orders' table and a 'Records' table.
Orders table has the following columns:
order_id order_date seller order_price
Records table has the following columns:
order_id record_created_at record_log
I'm trying to pull and compile the following list of data but I keep getting an error message:
order_week
seller
total_num_orders
under100_count --this is the number of orders that were < $100
over100_count --this is the number of order that >= $100
approved --this is the number of orders that were approved by the payment platform
Here's my query:
SELECT order_week, seller, total_num_orders, under100_count, over100_count, approved
FROM (
SELECT
EXTRACT(WEEK FROM order_created_at) AS order_week,
merchant_name AS seller,
COUNT(merchant_name) AS total_num_orders,
SUM(DISTINCT total_order_price < 100) AS under100_count,
SUM(DISTINCT total_order_price >= 100) AS over100_count
FROM orders o
GROUP BY order_week, seller)
INNER JOIN (
SELECT
COUNT(DISTINCT o.order_id) AS approved
FROM records r
WHERE record_log = 'order approved'
GROUP BY order_id)
ON l.order_id = o.order_id;
What am I doing wrong?
The subquery in the join needs an alias. It also needs to return the order_id column, so it can be joined.
inner join ( select order_id, ... from records ... group by order_id) r --> here
on l.order_id = o.order_id
I would actually write your query as:
select
extract(week from o.order_created_at) as order_week,
o.merchant_name as seller,
count(*) as total_num_orders,
sum(o.total_order_price < 100) as under100_count,
sum(o.total_order_price >= 100) as over100_count,
sum(r.approved) approved
from orders o
inner join (
select order_id, count(*) approved
from records r
where record_log = 'order approved'
group by order_id
) r on r.order_id = o.order_id;
group by order_week, seller, approved
Rationale:
you don't want, and need, distinct in the aggregate functions here; it is inefficient, and might even yield wrong results
count(*) is more efficient count(<expression>) - so, use it, unless you know why you are doing otherwise
I removed an unecessary level of nesting
If there are orders without records, you might want a left join instead.

MySQL: Query for selecting unique patients

I'm stuck with a query not returning unique records.
I have following tables:
clinics (id => PK)
patients (id => PK, clinic_id => FK)
patient_visits(id => PK, patient_id => FK, clinic_id => FK)
A patient is registered to a clinic. A patient can visit to any clinic any number of times.
What I want is to return all unique patients who visited in a clinic.
I tried following query which is not returning unique records for a clinic
SELECT v.id
, v.patient_id
, v.clinic_id
, c.name clinic_name
, p.name
, p.mobile
, p.email
, p.gender
, p.created_at
, last_visit_date
, visit_count
FROM
( SELECT DISTINCT patient_id
, clinic_id
FROM patient_visits
) pat
JOIN patient_visits v
ON pat.patient_id = v.patient_id
JOIN clinics c
ON c.id = v.clinic_id
JOIN patients p
ON p.id = v.clinic_id
JOIN
( SELECT patient_id
, MAX(patient_visits.created_at) last_visit_date
, COUNT(patient_visits.created_at) visit_count
FROM patient_visits
GROUP
BY patient_id
) visits_aggregate
ON visits_aggregate.patient_id = p.id
WHERE v.clinic_id = ?
ORDER
BY visit_date
One problem I understand is if I join with patient_visits, it will pick matching duplicate patient_id, clinic_id combination.
You should refrain from JOINing to all the rows in patients_visits as it will cause the notorious combinatorial explosion leading to duplicate rows. You need an aggregate.
But, your example showed patient_visits.id. If you don't want duplicates in your result set for each visit, you cannot show that column; it has a different value for each visit.
You need an aggregate from the patient_visits table, like this:
SELECT patient_id, clinic_id,
MAX(created_at) last_visit_date,
COUNT(*) visit_count
FROM patient_visits
GROUP BY patient_id, clinic_id
That query contains one row per combination of patient and clinic, so you can JOIN it to your other tables without generating duplicate rows. Before you do that, run it separately to convince yourself it works correctly.
Then... use it in your query like this
select patients.id patient_id, clinics.id clinic_id,
clinics.name as clinic_name,
patients.name, patients.mobile, patients.email, patients.gender,
patients.created_at,
pv.last_visit_date, pv.visit_count
from patients
join ( SELECT patient_id, clinic_id,
MAX(created_at) last_visit_date,
COUNT(*) visit_count
FROM patient_visits
GROUP BY patient_id, clinic_id
) pv ON patients.id = pv.patient_id
join clinics ON pv.clinic_id = clinics.id
order by pv.last_visit_date
See how this works? You don't want all the visits, just an aggregate of them giving the date of the most recent one and the count.
I don't realy understand your complex query. Your Query should be straight forward:
select
pv.patient_id,
pv.clinic_id,
max(pv.visit) as last_visit,
count(*) as visit_count
from
patient_visit pv,
patients p
-- You may now here join the other tables
where
pv.patients_id = p.id
and pv.clinic_id = ?
group by
pv.patient_id
As I see this, your use of distinct is in the inner select. And you are selecting patient_visit twice creating a power set of the table getting all combinations.

SQL beginner practice problems

Given two tables, orders (order_id, date, $, customer_id) and customers (ID, name)
Here's my method but I'm not sure if it's working & I'd like to know if there's faster/better way of solving these problems:
1) find out number of customers who made at least one order on date 7/9/2018
Select count (distinct customer_id)
From
(
Select customer_id from orders a
Left join customer b
On a.customer_id = b.ID
Group by customer_id,date
Having date = 7/9/2018
) a
2) find out number of customers who did not make an order on 7/9/2018
Select count (customer_id) from customer where customer_id not in
(
Select customer_id from orders a
Left join customer b
On a.customer_id = b.ID
Group by customer_id,date
Having date = 7/9/2018
)
3) find the date with most sales between 7/1 and 7/30
select date, max($)
from (
Select sum($),date from orders a
Left join customer b
On a.customer_id = b.ID
Group by date
Having date between 7/1 and 7/30
)
Thanks,
For problem 1, a valid solution might look like this:
SELECT COUNT(DISTINCT customer_id) x
FROM orders
WHERE date = '2018-09-07'; -- or is that '2018-07-09' ??
For problem 2, a valid solution might look like this:
SELECT COUNT(*) x
FROM customer c
LEFT
JOIN orders o
ON o.customer_id = x.customer_id
AND o.date = '2018-07-09'
WHERE o.crder_id IS NULL;
Assuming there are no ties, a valid solution to problem 3 might look like this:
SELECT date
, COUNT(*) sales
FROM orders
WHERE date BETWEEN '2018-07-01' AND '2018-07-30'
GROUP
BY date
ORDER
BY sales DESC
LIMIT 1;
The default format for a date in MySQL is YYYY-MM-DD, although this can be customized. You have to put quotes around it, otherwise it's treated as an arithmetic expression.
And none of your queries need to join with the customer table. The customer ID is already in the orders table, and you're not returning any info about the customers (like the name or address), you're just counting them.
1) You don't need the subquery or grouping.
SELECT COUNT(DISTINCT customer_id)
FROM orders
WHERE date = '2018-07-09'
2) Again, you don't need GROUP BY in the subquery. There's also a better pattern than NOT IN to get the count of non-matching rows.
SELECT COUNT(*)
FROM customer AS c
LEFT JOIN order AS o on c.id = o.customer_id AND o.date = '2018-07-09'
WHERE o.id IS NULL
See Return row only if value doesn't exist for various patterns to do this.
3) You can't use MAX($) in the outer query because the inner query doesn't return a column with that name. But even if you fix that, it still won't work, because the date column won't necessarily come from the same row that has the maximum. See SQL select only rows with max value on a column for more explanation of this.
You don't need a subquery at all. Use a query that returns the total sales for each day, then use ORDER BY to get the highest one.
SELECT date, SUM($) AS total_sales
FROM orders
WHERE date BETWEEN '2018-07-01' AND '2017-07-30'
GROUP BY date
ORDER BY total_sales DESC
LIMIT 1
If "most sales" is supposed to mean "most number of sales", replace SUM($) with COUNT(*).

mysql - Finding count of 0 when using joined tables

EDIT I've put up an sqlfiddle with this schema here: http://sqlfiddle.com/#!2/0726f2. I'm trying to select customers 3, 4, 5, 6.
Consider a db with three tables:
customers
---------
id
seats
-----
id
buyer_id (fk to customers)
flight_id
flights
-------
id
datetime (This is the UTC time of the flight)
I'm trying to find customers who have not booked seats on any flight in March.
This query provides a list of customers who have not booked seats on any flight:
SELECT customers.id, count(seats.id) as seat_count FROM `customers`
LEFT JOIN `seats` ON `seats`.`buyer_id` = `customers`.`id`
LEFT JOIN `flights` ON `flights`.`id` = `seats`.`flight_id`
GROUP BY customers.id
HAVING seat_count=0
I tried this query to find a list of customers who have not booked seats on any flight in March
SELECT customers.id, count(seats.id) as seat_count FROM `customers`
LEFT JOIN `seats` ON `seats`.`buyer_id` = `customers`.`id`
LEFT JOIN `flights` ON `flights`.`id` = `seats`.`flight_id`
WHERE flights.datetime >= '2014-03-01 00:00:00'
AND flights.datetime <= '2014-04-01 00:00:00'
GROUP BY customers.id
HAVING seat_count=0
But it returns an empty list. I understand why: I'm selecting a list of customers who have booked seats in March then finding customers in that list who have not booked seats. Clearly an empty set.
Likewise with adding this to the WHERE clause
AND seats.is is null
I can't figure a proper way to do this.
I've tried:
Flipping the JOINs every which way
Using a subquery in the LEFT JOIN statement. Performance was prohibitively bad.
Trying SELECT customers.id from customers where id not in ([above query]) MySql uses a correlated subquery and performance is also prohibitively awful.
Because this is wrapped up in a larger search feature, I can't come at this from another direction (selecting from seats and going from there, for example). Schema changes are not possible.
Thanks.
You can use NOT EXISTS like
SELECT *
FROM customers
WHERE NOT EXISTS (
SELECT * FROM seats
INNER JOIN flights ON flights.id = seats.flight_id
WHERE flights.datetime >= '2014-03-01 00:00:00'
AND flights.datetime <= '2014-04-01 00:00:00'
AND seats.buyer_id = customers.id
)
here is a corresponding SQLFiddle.
By the way you should at least add an index on seats.buyer_id, since this is a column you need to join on. With the named index the execution plan does not look that bad.
this works:
SELECT customers.id, count(seats.id) as seat_count FROM `seats`
INNER JOIN (SELECT id FROM flights WHERE DATE(flights.datetime) >= '2014-03-01'
AND DATE(flights.datetime) <='2014-04-01') `flights` ON `flights`.`id` = `seats`.`flight_id`
RIGHT JOIN customers ON customers.id=seats.buyer_id
GROUP BY customers.id
HAVING seat_count=0
here's the fiddle
here's another way to do it:
SELECT customers.id FROM customers WHERE id NOT IN (SELECT seats.buyer_id FROM seats
INNER JOIN `flights` ON `flights`.`id` = `seats`.`flight_id`
WHERE flights.datetime >= '2014-03-01 00:00:00'
AND flights.datetime <= '2014-04-01 00:00:00')
second fiddle

How to get all order ID which not paid in SQL Server 2008?

I want to get all order id numbers for selected customer which not paid till now, my data show as following:
What I want is Write a SELECT statement that answers this question:
select orderID
from order
where customer id = #custID
and Total cashmovementValue
for current order id
is less than total (sold quantity * salePrice )
for current order id
How to do it?
Thanks.
You need to compare the sum of each order line with the sum of each payment per order. GROUP BY and a few sub-queries is what you need to get the job done.
Something like this should work:
SELECT
O.OrderID
FROM [Order] O
INNER JOIN (
-- Add up cost per order
SELECT
OrderID,
SUM(SoldQuantity * P.SalePrice) AS Total
FROM OrderLine
INNER JOIN Product P ON P.ProductID = OrderLine.ProductID
GROUP BY OrderID
) OL ON OL.OrderID = O.OrderID
LEFT JOIN (
-- Add up total amount paid per order
SELECT
OrderID,
SUM(CashMovementValue) AS Total
FROM CashMovement
GROUP BY OrderID
) C ON C.OrderID = O.OrderID
WHERE
O.CustomerID = #custID
AND ( C.OrderID IS NULL OR C.Total < OL.Total )
EDIT
I've just noticed you're not storing the sale price on each order line. I've updated my answer accordingly, but this is a very bad idea. What will happen to your old orders if the price of an item changes? It is okay (and actually best practice) to denormalise the data by storing the price at the time of sale on each order line.