Can this query be optimized somehow? - mysql

Is this is a good query in performance or I can somehow optimize this query? I need to select date column from rental_schedules table and count how many available same dates are available (rental_schedule_status = 1):
$reservationTimes = RentalSchedule::distinct()
->select('date',
DB::raw('(SELECT count(date) FROM rental_schedules AS r2
WHERE r2.date = rental_schedules.date and rental_schedule_status = 1) as free_time'))
->where('premises_id', $id)->pluck('free_time', 'date');
Thanks in advance!

You could try to phrase your query using left join to a subquery, e.g.
SELECT
rs1.date,
COALESCE(rs2.cnt, 0) AS free_time
FROM rental_schedules rs1
LEFT JOIN
(
SELECT date, COUNT(*) AS cnt
FROM rental_schedules
WHERE rental_schedule_status = 1
GROUP BY date
) rs2
ON rs1.date = rs2.date
WHERE
rs1.premises_id = ?;
Your updated Laravel code:
$reservationTimes = RentalSchedule:from("RentalSchedule AS rs1")
->select(DB::raw('rs1.date, COALESCE(rs2.cnt, 0) AS free_time'))
->join(DB::raw('(SELECT date, COUNT(*) AS cnt
FROM rental_schedules
WHERE rental_schedules_status = 1
GROUP BY date) rs2'),
function($join) {
$join->on('rs1.date', '=', 'rs2.date');
})
->where('premises_id', $id)
->pluck('free_time', 'date');
You may also try adding an index on (rental_schedules_status, date), to speed up the subquery:
CREATE INDEX idx ON rental_schedules (rental_schedules_status, date)

You can get result without join or subquery :
Try this :
$reservationTimes = RentalSchedule::select('date',\DB::Raw("sum(case when rental_schedule_status = 1 then 1 else 0 end) as free_time"))
->where('premises_id', $id)
->groupBy('date')
->get()
->pluck('free_time', 'date');

Related

mysql GROUP CONCAT not returning values

Here is my query
SELECT
SUM(o.order_disc + o.order_disc_vat) AS manualsale
FROM
orders o
WHERE
o.order_flag IN (0 , 2, 3)
AND o.order_status = '1'
AND (o.assign_sale_id IN (SELECT GROUP_CONCAT(CAST(id AS SIGNED)) AS ids FROM users WHERE team_id = 92))
AND DATE(o.payment_on) = DATE(NOW())
above query return null when i run this query in terminal
When i use subquery below it returns data
SELECT GROUP_CONCAT(CAST(id AS SIGNED)) AS ids FROM users WHERE team_id = 92)
above query returns
'106,124,142,179'
and when i run my first query like below
SELECT
SUM(o.order_disc + o.order_disc_vat) AS manualsale
FROM
orders o
WHERE
o.order_flag IN (0 , 2, 3)
AND o.order_status = '1'
AND (o.assign_sale_id IN (106,124,142,179))
AND DATE(o.payment_on) = DATE(NOW())
it return me value.
Why it is not working with subquery please help
This does not do what you want:
AND (o.assign_sale_id IN (SELECT GROUP_CONCAT(CAST(id AS SIGNED)) AS ids FROM users WHERE team_id = 92))
This compares a single value against a comma-separated list of values, so it never matches (unless there is just one row in users for the given team).
You could phrase this as:
AND assign_sale_id IN (SELECT id FROM users WHERE team_id = 92)
But this would probably be more efficently expressed with exists:
AND EXISTS(SELECT 1 FROM users u WHERE u.team_id = 92 AND u.id = o.assign_sale_id)
Side note: I would also recommend rewriting this condition:
AND DATE(o.payment_on) = DATE(NOW())
To the following, which can take advantage of an index:
AND o.payment_on >= current_date AND o.payment_on < current_date + interval 1 day

GROUPed or used in an aggregate function error

I want to Group By CUSTOM_DRIVER in the query because I am getting over 1.3 million results and I just need to view them by Custom Driver for the month by store.
The query is returning me
I want something like this
I've tried using Distinct and Group By but I keep getting the error:
Attribute A.STORE_NUM must be GROUPed or used in an aggregate function
How do I get around this? The code I've been using is:
SELECT *
FROM(
SELECT
SE.STORE_NUM,
DM.MONTH_NAME_445 AS MO_445,
--DM.WEEK_START_DT_MON AS WK_BEGIN_MONDAY,
--SE.METRIC_DATE,
substring(SE.ROLLUP_NAME, 39, 20) as CUSTOM_DRIVER,
SE.DT_IMPORTED,
SE.METRIC_VALUE
FROM DNA_PUBLIC.ADMIN.SCHEDULE_EFFECTIVENESS SE
JOIN DNA_PUBLIC.ADMIN.DAY_MAP DM ON SE.METRIC_DATE = DM.DATE_DT
JOIN (
SELECT DISTINCT
METRIC_DATE,
MAX(DATE(DT_IMPORTED)) AS MAX_DT
FROM DNA_PUBLIC.ADMIN.SCHEDULE_EFFECTIVENESS
GROUP BY 1
) MX ON SE.METRIC_DATE = MX.METRIC_DATE
WHERE METRIC_NAME = 'Coverage Effectiveness'
AND SE.ROLLUP_NAME LIKE 'O%'
AND SE.METRIC_DATE between '07/27/2020' and '11/11/2020'
AND CAST(SE.DT_IMPORTED AS DATE) = MAX_DT
--AND CUSTOM_DRIVER = 'Truck/Truck'
AND SE.INDICATOR_NAME = 'Required'
--and STORE_NUM = 1
) AS A
Order by STORE_NUM
A work around was to select the values and group by the order of the selected variables. Thanks for the all the input!
SELECT
STORE_NUM,
MO_445,
CUSTOM_DRIVER,
SUM(METRIC_VALUE) as Tot_Hrs
FROM(
SELECT
SE.STORE_NUM,
DM.MONTH_NAME_445 AS MO_445,
DM.WEEK_START_DT_MON AS WK_BEGIN_MONDAY,
SE.METRIC_DATE,
substring(SE.ROLLUP_NAME, 39, 20) as CUSTOM_DRIVER,
SE.DT_IMPORTED,
SE.METRIC_VALUE
FROM DNA_PUBLIC.ADMIN.SCHEDULE_EFFECTIVENESS SE
JOIN DNA_PUBLIC.ADMIN.DAY_MAP DM ON SE.METRIC_DATE = DM.DATE_DT
JOIN (
SELECT DISTINCT
METRIC_DATE,
MAX(DATE(DT_IMPORTED)) AS MAX_DT
FROM DNA_PUBLIC.ADMIN.SCHEDULE_EFFECTIVENESS
GROUP BY 1
) MX ON SE.METRIC_DATE = MX.METRIC_DATE
WHERE METRIC_NAME = 'Coverage Effectiveness'
AND SE.ROLLUP_NAME LIKE 'O%'
AND SE.METRIC_DATE between '07/27/2020' and '11/11/2020'
AND CAST(SE.DT_IMPORTED AS DATE) = MAX_DT
--AND CUSTOM_DRIVER = 'Truck/Truck'
AND SE.INDICATOR_NAME = 'Required'
and STORE_NUM = 1
) AS A
group by 1,2,3
Order by STORE_NUM

Why integer cast is not working with integer group_concat() list?

I'm stuck at the query where I need to concat IDs of the table. And from that group of IDs, I need to fetch that rows in sub query. But when I try to do so, MySQL consider group_concat() as a string. So that condition becomes false.
select count(*)
from rides r
where r.ride_status = 'cancelled'
and r.id IN (group_concat(rides.id))
*************** Original Query Below **************
-- Daily Earnings for 7 days [Final]
select
group_concat(rides.id) as ids,
group_concat(ride_category.name) as rideType,
group_concat(ride_cars.amount + ride_cars.commission) as rideAmount ,
group_concat(ride_types.name) as carType,
count(*) as numberOfRides,
(
select count(*) from rides r where r.ride_status = 'cancelled' and r.id IN (group_concat(rides.id) )
) as cancelledRides,
(
select count(*) from rides r where r.`ride_status` = 'completed' and r.id IN (group_concat(rides.id))
) as completedRides,
group_concat(ride_cars.status) as status,
sum(ride_cars.commission) + sum(ride_cars.amount) as amount,
date_format(from_unixtime(rides.requested_at/1000 + rides.offset*60), '%Y-%m-%d') as requestedDate,
date_format(from_unixtime(rides.requested_at/1000 + rides.offset*60), '%V') as week
from
ride_cars,
rides,
ride_category,
ride_type_cars,
ride_types
where
ride_cars.user_id = 166
AND (rides.ride_status = 'completed' or. rides.ride_status = 'cancelled')
AND ride_cars.ride_id = rides.id
AND (rides.requested_at >= 1559347200000 AND requested_at < 1561852800000)
AND rides.ride_category = ride_category.id
AND ride_cars.car_model_id = ride_type_cars.car_model_id
AND ride_cars.ride_type_id = ride_types.id
group by
requestedDate;
Any solutions will be appreciated.
Try to replace the sub-query
(select count(*) from rides r where r.ride_status = 'cancelled' and r.id IN (group_concat(rides.id) )) as cancelledRides,
with below to count using SUM and CASE, it will make use of the GROUP BY
SUM(CASE WHEN rides.ride_status = 'cancelled' THEN 1 ELSE 0 END) as cancelledRides
and the same for completedRides
And move to using JOIN instead of implicit joins

date_format(from_unixtime()) subselect very slow

I have the following query I'm trying to use to spit out each day in a date range and show the # of leads, assignments, & returns:
select
date_format(from_unixtime(date_created), '%m/%d/%Y') as date_format,
(select count(distinct(id_lead)) from lead_history where (date_format(from_unixtime(date_created), '%m/%d/%Y') = date_format) and (id_vertical in (2)) and (id_website in (3,8))) as leads,
(select count(id) from assignments where deleted=0 and (date_format(from_unixtime(date_assigned), '%m/%d/%Y') = date_format) and (id_vertical in (2)) and (id_website in (3,8))) as assignments,
(select count(id) from assignments where deleted=1 and (date_format(from_unixtime(date_deleted), '%m/%d/%Y') = date_format) and (id_vertical in (2)) and (id_website in (3,8))) as returns
from lead_history
where date_created between 1509494400 and 1512086399
group by date_format
The date_created, date_assigned, and date_deleted fields are integers representing timestamps. id, id_lead, id_vertical and id_website are already indexed.
Would adding indexes to date_created, date_assigned, date_deleted, and deleted help make this faster? The issue I'm having is that it is very slow, and I'm not sure an index will help when using date_format(from_unixtime(...
Here is the EXPLAIN:
Looking to your code you could rewrite the query as ..
select
date_format(from_unixtime(date_created), '%m/%d/%Y') as date_format
, count(distinct(h.id_lead) as leads
, sum(case a.deleted = 1 then 1 else 0 end) assignments
, sum(case b.deleted = 0 then 1 else 0 end) returns
from lead_history h
inner join assignments on a a.date_assigned = h.date_created
and a.id_vertical = 2
and id_website in (3,8))
inner join assignments on b b.deleted = h.date_created
and a.id_vertical = 2
and id_website in (3,8))
where date_created between 1509494400 and 1512086399
group by date_format
anyway you shold avoid unuseful () and nested (), avoid unuseful conversion between date and use join instead of subselect .. or at least reduce similar sabuselect using case
PS for what concern the index remember that the use of conversion on a column value invalid the use of related the index ..

View - Return 0 if no rows found in a grouped by query

Let's say I have the following MySQL view:
create or replace view total_transactions(account_id, total) as
select
t.account_id,
ifnull(sum(t.value), 0) as total
from transactions t
where t.paid IS TRUE
group by t.bank_account_id;
Let's say the account doesn't have any transaction yet, I want the view to return 0.
Right now, if I do a select like:
select * from total_transactions where account_id = 2060;
And account 2060 didn't had any transaction, it will return me nothing, instead of 0.
How could I fix that?
Thanks in advance.
EDIT
I think it could be something with the group by...
If I execute the query that I'm using for the view without the group by, it works (return 0 even with no results), but if I use group by it comes null:
select
t.account_id,
ifnull(sum(t.value), 0) as total
from transactions t
where t.paid IS TRUE
and account_id = 2060;
Returns 0, and
create or replace view total_transactions(account_id, total) as
select
t.account_id,
ifnull(sum(t.value), 0) as total
from transactions t
where t.paid IS TRUE
and account_id = 2060
group by t.bank_account_id;
Return an empty set.
If there is not an entry in the view results, then this will always return NULL - That's SQL. If you change your SELECT that you use against the view, you can achieve what you want:
SELECT IFNULL(total, 0) FROM total_transactions WHERE account_id = 2060
Edit:
(SELECT IFNULL(total, 0) total FROM total_transactions WHERE account_id = 2060)
UNION
(SELECT 0 total)
In production, don't use SELECT *. See this SO question for a thorough response as to why.
So, assuming you're not, you can use COALESCE, which will return the first non-null value.
SELECT
COALESCE(total, 0)
FROM
total_transactions
WHERE
account_id = '2600'
For positive values I use
select max(x.cnt) as cnt
from (
select ifnull(meta_value, 0) as cnt from wp_postmeta where post_id = 5543 and meta_key = '_bbp_voice_count'
union
select 0 as cnt) x
or for any
select x.cnt
from (
select ifnull(meta_value, 0) as cnt from wp_postmeta where post_id = 5543 and meta_key = '_bbp_voice_count'
union
select 0 as cnt
) x
limit 1