MySQL: Selecting available vehicles that are not reserved - mysql

I have the following two records in the reservation table of a MySQL database.
When I use this query to get the vehicles that are available, it seems to give me every vehicle instead of just the ones not in the reservation table between the specified dates.
this is the query:
SELECT v.id, v.brand, v.type, v.description, v.airco, v.seats, v.hourly_rent
FROM vehicle as v
WHERE v.id
NOT IN(
(SELECT v.id
FROM vehicle as v
LEFT JOIN reservation as r on r.`vehicle_id` = v.id
WHERE r.status_id in(3,4,5)
AND (
(
('2014-01-01' >= r.startdate AND '2014-01-03' <= r.enddate )
OR
('2014-01-01' <= r.startdate AND '2014-01-03' >= r.enddate )
)
OR
(
('2014-01-01' >= r.startdate AND '2014-01-01' <= r.enddate)
OR
('2014-01-03' >= r.startdate AND '2014-01-03' <= r.enddate)
)
)
GROUP BY v.id
)
)
And when I use this query:
SELECT *
FROM reservation as r
WHERE (
(
('2014-01-01' >= r.startdate AND '2014-01-03' <= r.enddate )
OR
('2014-01-01' <= r.startdate AND '2014-01-03' >= r.enddate )
)
OR
(
('2014-01-01' >= r.startdate AND '2014-01-01' <= r.enddate)
OR
('2014-01-03' >= r.startdate AND '2014-01-03' <= r.enddate)
)
)
To get the reservations only, I get 1 record as u can see in the picture.
What could be wrong and how can I fix this?

Try This -
SELECT v.id, v.brand, v.type, v.description, v.airco, v.seats, v.hourly_rent
FROM vehicle AS v
LEFT JOIN reservation AS r
ON v.id = r.vehicle_id
AND r.startdate <= '2014-01-03'
AND r.enddate >= '2014-01-01'
WHERE r.id IS NULL;
Or with your status entries
SELECT v.id, v.brand, v.type, v.description, v.airco, v.seats, v.hourly_rent
FROM vehicle AS v
LEFT JOIN reservation AS r
ON v.id = r.vehicle_id
AND r.startdate <= '2014-01-03'
AND r.enddate >= '2014-01-01'
AND r.status NOT IN (3,4,5)
WHERE r.id IS NULL;

Without seeing the table structure it's harder to assume however this is my crack.
Just look where the V.id not exeists int the reservation table.
SELECT
v.id,
v.brand,
v.type,
v.description,
v.airco,
v.seats,
v.hourly_rent
FROM vehicle as v
WHERE NOT EXISTS (
SELECT 1
FROM reservation as r
WHERE r.vehicle_id = v.id
AND r.status_id NOT IN (3,4,5)
AND(
(
('2014-01-01' >= r.startdate AND '2014-01-03' <= r.enddate )
OR
('2014-01-01' <= r.startdate AND '2014-01-03' >= r.enddate )
)
OR
(
('2014-01-01' >= r.startdate AND '2014-01-01' <= r.enddate)
OR
('2014-01-03' >= r.startdate AND '2014-01-03' <= r.enddate)
)
)
)
GROUP BY v.id))

Related

Improving the performance of a MySQL left join sub query

I have the following MySQL query which calculates the total number of orders for each month within a given date range e.g. a year. The query works correctly, but the performance is slow (around 250ms).
Any ideas on how to rewrite it to make it more efficient?
WITH recursive `dates` AS (
(
SELECT '2019-11-28' AS item
)
UNION
ALL (
SELECT
item + INTERVAL 1 DAY
FROM
`dates`
WHERE
item + INTERVAL 1 DAY <= '2020-11-27'
)
)
SELECT
DATE_FORMAT(`item`, '%b %y') AS `date`,
COUNT(`orders`.`id`) AS `total`
FROM
`dates`
LEFT JOIN (
SELECT
`orders`.`id`,
`orders`.`created_at`
FROM
`orders`
INNER JOIN `locations` ON `orders`.`location_id` = `locations`.`id`
WHERE
`orders`.`shop_id` = 10379184
AND `locations`.`country_id` = 128
AND `orders`.`created_at` >= '2019-11-28 12:01:42'
AND `orders`.`created_at` <= '2020-11-27 12:01:42'
) AS `orders` ON DATE(`orders`.`created_at`) = `dates`.`item`
GROUP BY
`date`
UPDATE: Some have suggested using two left joins, however if I do that then the country_id filter is not applied:
WITH recursive `dates` AS (
(
SELECT
'2019-11-28' AS item
)
UNION
ALL (
SELECT
item + INTERVAL 1 DAY
FROM
`dates`
WHERE
item + INTERVAL 1 DAY <= '2020-11-27'
)
)
SELECT
DATE_FORMAT(`item`, '%b %y') AS `date`,
COUNT(`orders`.`id`) AS `total`
FROM
`dates`
LEFT JOIN `orders` USE INDEX (`orders_created_at_index`) ON DATE(`created_at`) = `dates`.`item`
AND `orders`.`shop_id` = 10379184
AND `orders`.`created_at` >= '2019-11-28 12:22:43'
AND `orders`.`created_at` <= '2020-11-27 12:22:43'
LEFT JOIN `locations` ON `orders`.`location_id` = `locations`.`id`
AND `locations`.`country_id` = 128
GROUP BY
`date`
Thanks!
I would suggest using a correlated subquery:
SELECT DATE_FORMAT(d.item, '%b %y') AS `date`,
(SELECT COUNT(*)
FROM orders o JOIN
locations l
ON o.location_id = l.id
WHERE shop_id = 10379184 AND
country_id = 128 AND
o.created_at >= d.item AND
o.created_at < d.item + interval 1 day
) as total
FROM dates d;
This avoids the outer aggregation, which is often a performance improvement.
In addition, indexes could probably help the query, but it is unclear where columns such as country_id and shop_id are coming from.
After much tinkering, I produced the following which operates in under 40ms, which is good enough for my needs. I still think it's not ideal and would welcome any improvements...
SELECT
`date`,
COUNT(`order`)
FROM
(
WITH recursive `dates` AS (
(
SELECT
'2019-11-28' AS item
)
UNION
ALL (
SELECT
item + INTERVAL 1 DAY
FROM
`dates`
WHERE
item + INTERVAL 1 DAY <= '2020-11-27'
)
)
SELECT
DATE_FORMAT(`item`, '%b %y') AS `DATE`,
`orders`.`id` AS `order`,
`locations`.`id` AS `location`
FROM
`dates`
LEFT JOIN
`orders`
ON
DATE(`created_at`) = `dates`.`item`
AND
`orders`.`shop_id` = 10379184
AND
`orders`.`created_at` >= '2019-11-28 12:22:43'
AND
`orders`.`created_at` <= '2020-11-27 12:22:43'
LEFT JOIN
`locations`
ON
`orders`.`location_id` = `locations`.`id`
AND
`locations`.`country_id` = 209
) AS items
WHERE
(
`order` IS NULL
AND `location` IS NULL
)
OR (
`order` IS NOT NULL
AND `location` IS NOT NULL
)
GROUP BY
`date`

Mysql GROUP BY first record and then filter on that record

I want the GROUP BY to pick the earliest StartSessionTime and then I want to filter based on the StartSessionTime, I tried various things, most recent being:
$sql_d = "SELECT a.* FROM Session_Log a INNER JOIN
( SELECT ID, MIN(SessionStartTime)
FROM Session_Log GROUP BY ID
) b
ON a.ID = b.ID
AND a.SessionStartTime = b.SessionStartTime
WHERE DATE(SessionStartTime) < ( '2018-01-01' + INTERVAL $b DAY )
AND DATE(SessionStartTime) >= ( '2018-01-01' + INTERVAL $a DAY )";
please help with the correct expression
You need to assign an alias to MIN(SessionStartTime) so you can reference it in the ON clause.
SELECT a.*
FROM FMan_Session_Log AS a
JOIN (
SELECT DeviceID, MIN(SessionStartTime) AS MinSessionStartTime
FROM FMan_Session_Log
) AS b ON a.DeviceID = b.DeviceID AND a.SessionStartTime = b.MinSessionStartTime
WHERE a.SessionStartTime >= DATE_ADD('2018-01-01', INTERVAL $a DAY)
AND a.SessionStartTime < DATE_ADD('2018-01-01, INTERVAL $b DAY)
Fixed the errors. First the SessionStartTime was not defined for b. Second the SessionStartTime was ambiguous in WHERE clause
$sql_d = "SELECT a.* FROM Session_Log a INNER JOIN
( SELECT ID, MIN(SessionStartTime) **as SessionStartTime**
FROM Session_Log GROUP BY ID
) b
ON a.ID = b.ID
AND a.SessionStartTime = b.SessionStartTime
WHERE DATE(a.SessionStartTime) < ( '2018-01-01' + INTERVAL $b DAY )
AND DATE(a.SessionStartTime) >= ( '2018-01-01' + INTERVAL $a DAY )";

UNION issue in select sub-query

In the below code, how do I combine MRR_Created with MRR_Destroyed in a UNION so it only shows the next number? Right now the query is correct but I don't need to see the increase/decrease separately.
select account.email, account.vip, datediff(now(), account.date_created) AS Age,
(select sum(account_subscription.next_invoice_price) as ActiveMRR
from account_subscription
where account_subscription.status = 'active'
and account_subscription.acctid = account.acctid
) as MRR,
(select count(account_subscription.subid) as Churn
from account_subscription
where account_subscription.date_created between DATE_ADD(NOW(), INTERVAL -2880 MINUTE) and NOW()
and account_subscription.date_closed between DATE_ADD(NOW(), INTERVAL -2880 MINUTE) and NOW()
and account_subscription.acctid = account.acctid
) as Churn,
(select sum(account_subscription.next_invoice_price) as MRR
from account_subscription
where date(account_subscription.date_created) = date(curdate())
and account_subscription.acctid = account.acctid
) as MRR_Created,
(select sum(account_subscription.next_invoice_price) as MRR
from account_subscription
where date(account_subscription.date_closed) = date(curdate())
and account_subscription.acctid = account.acctid
) as MRR_Destroyed,
concat("https://sitetest.com?ACCTID=",account.acctid) as URL
from account
where account.status = 'active'
and (
account.type in ('affiliate', 'customer', 'customer_freetrial', 'customer_duplicate', 'customer_match')
or account.type is null
)
group by account.acctid
order by MRR desc
Not sure if you really need a UNION here. Try replacing
(select sum(account_subscription.next_invoice_price) as MRR
from account_subscription
where date(account_subscription.date_created) = date(curdate())
and account_subscription.acctid = account.acctid
) as MRR_Created,
(select sum(account_subscription.next_invoice_price) as MRR
from account_subscription
where date(account_subscription.date_closed) = date(curdate())
and account_subscription.acctid = account.acctid
) as MRR_Destroyed,
with
(select sum(account_subscription.next_invoice_price) as MRR
from account_subscription
where (date(account_subscription.date_created) = date(curdate())
or date(account_subscription.date_closed) = date(curdate()))
and account_subscription.acctid = account.acctid
) as MRR_Created,
Hope this helps!

Mysql Joins with nested Select

I am trying to get the total of all orders of each seller per year, month, and day using this sql. I am still wrapping my head around joins, but from what I know i thought this should work
SELECT sellers.username, sellers.registerDate, sellers.sellerid,
orders.orderPrice, orders.orderDate, orders.sellerid,
count(orders.orderPrice) AS products, SUM( orders.orderPrice ) AS total,
FROM `sellers`
JOIN
`orders`,
(SELECT SUM( orders.orderPrice ) FROM `orders` WHERE YEAR( orderDate ) = YEAR( CURDATE( ) ) ) AS year,
(SELECT SUM( orders.orderPrice ) FROM `orders` WHERE MONTH( orderDate ) = MONTH( CURDATE( ) ) ) AS month,
(SELECT SUM( orders.orderPrice ) FROM `orders` WHERE orderDate = CURDATE() ) AS day
ON orders.sellerid = sellers.sellerid
GROUP BY sellers.username
HAVING total > 0
ORDER BY total desc
LIMIT 0 , 4
But it gives me an error (#1064 - near 'ON orders.sellerid = sellers.sellerid GROUP BY sellers.username HAVING' at line 9)
You need to set ON clause on every join table not only the lastone.
Regards
JOIN
`orders` ON ??? = ???,
(SELECT SUM( orders.orderPrice ) FROM `orders` WHERE YEAR( orderDate ) = YEAR( CURDATE( ) ) ) AS year ON orders.sellerid = sellers.sellerid,
(SELECT SUM( orders.orderPrice ) FROM `orders` WHERE MONTH( orderDate ) = MONTH( CURDATE( ) ) ) AS month ON orders.sellerid = sellers.sellerid,
(SELECT SUM( orders.orderPrice ) FROM `orders` WHERE orderDate = CURDATE() ) AS day
ON orders.sellerid = sellers.sellerid

Setting user defined variables with joins

I have a query like below:
Result gives #sold_count:=SUM(I.quantity) = 10, but #sold_count = 0,
so calculations are all 0.
What should be wrong here?
SET #sold_count :=0;
SELECT
#sold_count:=SUM(I.quantity),
#sold_count,I.from_widget,COUNT(from_widget) as order_count,
(#sold_count * buy_price) as ciro,
(#sold_count * list_price) as liste_ciro,
(#sold_count * widget_price) as vitrin_ciro,
P.*
FROM
tbl_products P
LEFT JOIN tbl_order_items I on I.product_id = P.id
WHERE
P.publish_date BETWEEN DATE_SUB( CURDATE( ) ,INTERVAL 3 MONTH ) AND DATE_SUB( CURDATE( ) ,INTERVAL 0 MONTH )
GROUP BY I.from_widget,I.product_id
ORDER BY publish_date DESC
Don't use variables. Just:
SELECT
SUM(I.quantity),
I.from_widget,
COUNT(from_widget) AS order_count,
SUM(I.quantity) * buy_price AS ciro,
SUM(I.quantity) * list_price AS liste_ciro,
SUM(I.quantity) * widget_price AS vitrin_ciro,
P.*
FROM
tbl_products P
LEFT JOIN tbl_order_items I
ON I.product_id = P.id
WHERE
P.publish_date BETWEEN DATE_SUB( CURDATE( ) , INTERVAL 3 MONTH )
AND DATE_SUB( CURDATE( ) , INTERVAL 0 MONTH )
GROUP BY I.from_widget,
I.product_id
ORDER BY publish_date DESC ;
You could also make the query a nested one, if you don't like using SUM(quantity) many times:
SELECT
sum_quantity * buy_price AS ciro,
sum_quantity * list_price AS liste_ciro,
sum_quantity * widget_price AS vitrin_ciro,
tmp.*
FROM
( SELECT
SUM(I.quantity) AS sum_quantity,
I.from_widget,
COUNT(from_widget) AS order_count,
buy_price,
list_price,
widget_price,
P.*
FROM
tbl_products P
LEFT JOIN tbl_order_items I
ON I.product_id = P.id
WHERE
P.publish_date BETWEEN DATE_SUB( CURDATE( ) , INTERVAL 3 MONTH )
AND DATE_SUB( CURDATE( ) , INTERVAL 0 MONTH )
GROUP BY I.from_widget,
I.product_id
) AS tmp
ORDER BY publish_date DESC ;