I have 2 tables, restaurants and orders, each restaurant can have many orders
restaurants table
id
name
orders table
id
restaurant_id
date
I need to find the restaurants that have no orders on some date range. In orders table I save the order dates like - each row represents one day. So, I need to make inner join, but with no results from the orders table. Say, I need to find restaurants that are free from 2013-08-09 to 2013-08-11 date range. How can I achieve this ? How to make a query, that will give the restaurants with no matching in the orders table - according to the date range ?
Actually I can do it saving all the dates in the orders table with status not_ordered, and make inner join with not_ordered = true condition, but in that case I will have to populate all the table with the dates, which is not a good thing in my case.
Thanks
select r.*
from restaurant r
left join orders o on r.id = o.restaurant_id and o.date between '...' and '...'
where o.id is null;
Or you can do it using not exists as shown in other answers.
You don't want to use an inner join for this. You can do it with an outer join, or with NOT EXISTS and a sub-query.
Here's an example of the latter approach:
select r.id,r.name
from restaurants r
where not exists (
select NULL
from orders o
where o.restaurant_id = r.id
and o.date >= '2013-08-09'
and o.date <= '2013-08-11'
);
I don't know mysql very well, but this should work as general SQL:
SELECT *
FROM restaurants
WHERE NOT EXISTS(SELECT 1
FROM order
WHERE restaurant_id=id AND
date BETWEEN '2013-08-09' AND '2013-08-11')
Related
I tried to write a query, but unfortunately I didn't succeed.
I want to know how many packages delivered over a given period by a person.
So I want to know how many packages were delivered by John (user_id = 1) between 01-02-18 and 28-02-18. John drives another car (another plate_id) every day.
(orders_drivers.user_id, plates.plate_name, orders.delivery_date, orders.package_amount)
I have 3 table:
orders with plate_id delivery_date package_amount
plates with plate_id plate_name
orders_drivers with plate_id plate_date user_id
I tried some solutions but didn't get the expected result. Thanks!
Try using JOINS as shown below:
SELECT SUM(o.package_amount)
FROM orders o INNER JOIN orders_drivers od
ON o.plate_id=od.plate_id
WHERE od.user_id=<the_user_id>;
See MySQL Join Made Easy for insight.
You can also use a subquery:
SELECT SUM(o.package_amount)
FROM orders o
WHERE EXISTS (SELECT 1
FROM orders_drivers od
WHERE user_id=<user_id> AND o.plate_id=od.plate_id);
SELECT sum(orders.package_amount) AS amount
FROM orders
LEFT JOIN plates ON orders.plate_id = orders_drivers.plate_id
LEFT JOIN orders_driver ON orders.plate_id = orders_drivers.plate_id
WHERE orders.delivery_date > date1 AND orders.delivery_date < date2 AND orders_driver.user_id = userid
GROUP BY orders_drivers.user_id
But seriously, you need to ask questions that makes more sense.
sum is a function to add all values that has been grouped by GROUP BY.
LEFT JOIN connects all tables by id = id. Any other join can do this in this case, as all ids are unique (at least I hope).
WHERE, where you give the dates and user.
And GROUP BY userid, so if there are more records of the same id, they are returned as one (and summed by their pack amount.)
With the AS, your result is returned under the name 'amount',
If you want the total of packageamount by user in a period, you can use this query:
UPDATE: add a where clause on user_id, to retrieve John related data
SELECT od.user_id
, p.plate_name
, SUM(o.package_amount) AS TotalPackageAmount
FROM orders_drivers od
JOIN plates p
ON o.plate_id = od.plate_id
JOIN orders o
ON o.plate_id = od.plate_id
WHERE o.delivery_date BETWEEN convert(datetime,01/02/2018,103) AND convert(datetime,28/02/2018,103)
AND od.user_id = 1
GROUP BY od.user_id
, p.plate_name
It groups rows on user_id and plate_name, filter a period of delivery_date(s) and then calculate the sum of packageamount for the group
What i would like to archieve:
Getting the correct sum of the total amount of the orders that has been cancelled of user id 2002.
Some pre information:
I am having deals which that has its price in deals.price and its id in deals.ID
I then have orders with a foreign key to deals.ID
Running this SQL:
select SUM(deals.price), orders.* from orders
JOIN deals ON deals.ID = orders.deal_id
where orders.user_id = 2002
and orders.cancelled = 1
Works just fine.
Here is where i get stuck:
As an addition to deals, each deals has products with their own prices.
Table is called deal_products, deal_products.price hold the price and deal_products.product_id has the ID of it.
A order is attached to a deal product in another table called order_products, where order_products.product_id = deal_products.product_id
To sum up: I would like to do is including a if inside the above SQL.
If a order has a row in order_products, get the order_products.product_id and find the price in deal_products (price) and use this instead of deals.price when SUM()'ing.
If there is no row it should use deals.price.
How can this be archieved? To first look in another table if there is a entry, and then further look in to a third table and get a value to use?
You can use COALESCE + LEFT JOIN:
select SUM(coalesce(dp.price, d.price)), o.*
from orders o JOIN deals d ON d.ID = o.deal_id
LEFT JOIN order_products op on op.order_id = o.id
LEFT JOIN deal_products dp on op.product_id = dp.product_id
where o.user_id = 2002 and o.cancelled = 1
group by ...;
COALESCE function returns first not null operand
LEFT [OUTER] JOIN = [INNER] JOIN + all rows of the structure on the left side of the LEFT JOIN keyword, which don't match the ON clause in the right structure.
I am new to SQL and I cannot figure out why my SQL result returns all data from the wrong table. I want to receive all data from product table but I want to filter the results so it does not show those results that have some value in another table.
My SQL statement is:
SELECT *
FROM orders
JOIN products
ON orders.product_id=products.product_id
AND NOT order_date=somedate;
This query returns everything from the orders table where somedate is not the given value but I want to get everything from the products table if orders table does not have the given somedate as value (when the product is not reserved).
Edit: thanks for the help everyone! each answer worked perfectly for me with some slight modifications :)
You can get products which don't have orders of a certain criteria (order_date != somedate) like this:
SELECT p.*
FROM products p
LEFT JOIN orders o ON o.product_id = p.product_id AND order_date != somedate
WHERE o.id IS NULL
Different orders could have different dates. If you want products that were not ordered on a particular date, then try phrasing the query like this:
If I understand correctly, your original query returned all products with multiple orders, because some orders were on that date and some were not.
select p.*
from product p
where not exists (select 1
from orders o
where o.product_id = p.product_id
and o.order_date = somedate)
Not sure I got your question right, but you can try this:
SELECT p.*
FROM orders
JOIN products
ON orders.product_id=products.product_id
WHERE order_date<>somedate;
I have a query to show customers and the total dollar value of all their orders. The query takes about 100 seconds to execute.
I'm querying on an ExpressionEngine CMS database. ExpressionEngine uses one table exp_channel_data, for all content. Therefore, I have to join on that table for both customer and order data. I have about 14,000 customers, 30,000 orders and 160,000 total records in that table.
Can I change this query to speed it up?
SELECT link.author_id AS customer_id,
customers.field_id_122 AS company,
Sum(orders.field_id_22) AS total_orders
FROM exp_channel_data customers
JOIN exp_channel_titles link
ON link.author_id = customers.field_id_117
AND customers.channel_id = 7
JOIN exp_channel_data orders
ON orders.entry_id = link.entry_id
AND orders.channel_id = 3
GROUP BY customer_id
Thanks, and please let me know if I should include other information.
UPDATE SOLUTION
My apologies. I noticed that entry_id for the exp_channel_data table customers corresponds to author_id for the exp_channel_titles table. So I don't have to use field_id_117 in the join. field_id_117 duplicates entry_id, but in a TEXT field. JOINING on that text field slowed things down. The query is now 3 seconds
However, the inner join solution posted by #DRapp is 1.5 seconds. Here is his sql with a minor edit:
SELECT
PQ.author_id CustomerID,
c.field_id_122 CompanyName,
PQ.totalOrders
FROM
( SELECT
t.author_id
SUM( o.field_id_22 ) as totalOrders
FROM
exp_channel_data o
JOIN
exp_channel_titles t ON t.author_id = o.entry_id AND o.channel_id = 3
GROUP BY
t.author_id ) PQ
JOIN
exp_channel_data c ON PQ.author_id = c.entry_id AND c.channel_id = 7
ORDER BY CustomerID
If this is the same table, then the same columns across the board for all alias instances.
I would ensure an index on (channel_id, entry_id, field_id_117 ) if possible. Another index on (author_id) for the prequery of order totals
Then, start first with what will become an inner query doing nothing but a per customer sum of order amounts.. Since the join is the "author_id" as the customer ID, just query/sum that first. Not completely understanding the (what I would consider) poor design of the structure, knowing what the "Channel_ID" really indicates, you don't want to duplicate summation values because of these other things in the mix.
select
o.author_id,
sum( o.field_id_22 ) as totalOrders
FROM
exp_channel_data customers o
where
o.channel_id = 3
group by
o.author_id
If that is correct on the per customer (via author_id column), then that can be wrapped as follows
select
PQ.author_id CustomerID,
c.field_id_122 CompanyName,
PQ.totalOrders
from
( select
o.author_id,
sum( o.field_id_22 ) as totalOrders
FROM
exp_channel_data customers o
where
o.channel_id = 3
group by
o.author_id ) PQ
JOIN exp_channel_data c
on PQ.author_id = c.field_id_117
AND c.channel_id = 7
Can you post the results of an EXPLAIN query?
I'm guessing that your tables are not indexed well for this operation. All of the columns that you join on should probably be indexed. As a first guess I'd look at indexing exp_channel_data.field_id_117
Try something like this. Possibly you have error in joins. also check whether joins on columns are correct in your databases. Cross join may takes time to fetch large data, by mistake if your joins are not proper on columns.
select
link.author_id as customer_id,
customers.field_id_122 as company,
sum(orders.field_id_22) as total_or_orders
from exp_channel_data customers
join exp_channel_titles link on (link.author_id = customers.field_id_117 and
link.author_id = customer.channel_id = 7)
join exp_channel_data orders on (orders.entry_id = link.entry_id and orders.entry_id = orders.channel_id = 3)
group by customer_id
I have two tables customers and orders, below is the structure.
Table - customers
id
customer_name
Table - orders
id
order_id
customer_id
customers table have customers records and orders table have orders placed by customers,
customer_id in orders table is linked to the id field of customers table.
Now one customer can have zero or one or more than one orders, i want to get the last order placed by customers only.
when i run the following query a simple invisible join, it returns all the orders by the customer
SELECT customers.customer_name,orders.order_id FROM orders,customers WHERE orders.customer_id=customers.id
I have also tried different JOIN statements but cannot get the last order by the customer, i want to get it in one SQL query for all customers.
Thank you in advance for your help.
In MySQL there is just few ways to make it work (that I now actually). The first one is sort your table as desc before the join:
SELECT c.customer_name, o.customer_id, o.order_id,o.id FROM customers c
INNER JOIN orders o
ON o.id = (SELECT id FROM orders WHERE customer_id = c.id ORDER BY id DESC LIMIT 1)
Using in real time is the only way to get it done, but if you need to make some join on not real time you can create a temporary table or a alias table sorting it to make your select, like this:
CREATE TABLE tmp_your_table AS
SELECT * FROM orders ORDER BY id DESC
So now you are able to make this join work:
SELECT c.customer_name, o.customer_id, o.order_id,o.id FROM customers c
INNER JOIN tmp_your_table o ON o.id = tmp_your_table.id
Try this query
SELECT
c.customer_name,
max(o.order_id)
FROM
customers c
INNER JOIN
orders o
ON
o.customer_id = c.id
GROUP BY
c.customer_name
You don't have any date field in the order table so assuming the latest order will be the one which has max(order_id).
Try this query
SELECT
c.customer_name,
o.order_id
FROM
customers c
INNER JOIN
orders o
ON
o.customer_id = c.id
ORDER BY
o.id desc
LIMIT 1;