"Every derived table must have its own alias" Error in SQL - mysql

I have a following SQL query and I'm getting an error "Every derived table must have its own alias.could any one please help me to solve this?
SELECT
c.clientID_PK,
c.clientName,
d1.draftCount,
d2.purchaseOrderValue,
d2.averageValue
FROM client c
LEFT JOIN
(select
COUNT(DISTINCT d.draftID_PK) as draftCount
from draft d
where d.draftDate between NOW() - INTERVAL 90 DAY and NOW())
)d1 ON TRUE
LEFT JOIN
(
SELECT
ROUND(sum(p.total_finalValue),2) as purchaseOrderValue
ROUND((p.poValue / 12),2) as averageValue
FROM paymentengine_data p
WHERE p.poDate between NOW() - INTERVAL 90 DAY and NOW()
)d2 ON TRUE
WHERE c.typeID_FK = 1 AND c.stateID_FK = 2 AND c.statusID_FK = 2
AND d1.clientID_FK = c.clientID_PK AND d2.purchaserID_FK = c.clientID_PK
GROUP BY c.clientID_PK

Your first LEFT JOIN has an extra closing brace and that is causing the error.
Change:
LEFT JOIN
(select
COUNT(DISTINCT d.draftID_PK) as draftCount
from draft d
where d.draftDate between NOW() - INTERVAL 90 DAY and NOW()) -- <-- xtra )
)d1 ON TRUE
To:
LEFT JOIN
(select
COUNT(DISTINCT d.draftID_PK) as draftCount
from draft d
where d.draftDate between NOW() - INTERVAL 90 DAY and NOW()
)d1 ON TRUE

Here is a query that might actually work:
SELECT c.clientID_PK, c.clientName,
d1.draftCount, d2.purchaseOrderValue, d2.averageValue
FROM client c left join
(select clientID_FK, COUNT(DISTINCT d.draftID_PK)as draftCount
from draft d
where d.draftDate between NOW() - INTERVAL 90 DAY and NOW()
group by clientID_FK
) d1
on d1.clientID_FK = c.clientID_PK LEFT JOIN
(SELECT purchaserID_FK, ROUND(sum(p.total_finalValue),2) as purchaseOrderValue
ROUND((p.poValue / 12),2) as averageValue
FROM paymentengine_data p
WHERE p.poDate between NOW() - INTERVAL 90 DAY and NOW()
GROUP BY purchaserID_FK
) d2
ON d2.purchaserID_FK = c.clientID_PK
WHERE c.typeID_FK = 1 AND c.stateID_FK = 2 AND c.statusID_FK = 2;
Here are some additional changes (besides the extra closing paren):
The first subquery groups by clientID_FK and includes the column in the select.
The on condition has been moved from the where to the on.
The second subquery groups by purchaserID_FK and includes the column in the select.
The on condition has been moved from the where clause to the on.
The outer group by has been removed. Presumably, the primary key is unique on the client table.

Related

Getting the results of a query for each day in the past 30 days

Below is a query I run to extract some data in the past 24 hours.
SELECT
s.symbol,
count(cs.symbol_id) AS mentions
FROM symbols s
LEFT JOIN comments_symbols cs ON cs.symbol_id = s.id
LEFT JOIN comments c ON c.id = cs.comment_id
WHERE c.`date` > DATE_SUB(NOW(), INTERVAL 1 DAY)
GROUP BY (s.symbol)
ORDER BY mentions
DESC LIMIT 15
However, I need 24 hour intervals of data for the past 30 days in order to show a 30-day chart.
Instead of executing this query 30 times for the each day in the past 30 days, is there an approach I can take to do it with just one query execution?
It seems executing this query 30 times per page load may not be the best way to do this, no?
I hope I explained clearly, please let me know if any details are fuzzy.
Let me assume you have a list of dates. If you don't want to list them out, you can generate them:
with recursive dates as (
select curdate() - interval 30 day as dte
union all
select dte + interval 1 day
from dates
where dte < curdate()
)
Second, the LEFT JOIN seems superfluous, because you are filtering the results using LIMIT. However, I'll leave it in. Use a cross join to generate a row for each day and symbol . . . then aggregate:
SELECT s.symbol, COUNT(cs.symbol_id) AS mentions
FROM dates d CROSS JOIN
symbols s LEFT JOIN
comments_symbols cs
ON cs.symbol_id = s.id LEFT JOIN
comments c
ON c.id = cs.comment_id AND
c.date >= d.dte AND
c.date < d.date + interval 1 day
GROUP BY d.dte, s.symbol
ORDER BY d.dte, mentions DESC
Finally, to get 15 per day, let's put that into a CTE and use window functions:
WITH sm as (
SELECT d.dte, s.symbol, COUNT(cs.symbol_id) AS mentions
FROM dates d CROSS JOIN
symbols s LEFT JOIN
comments_symbols cs
ON cs.symbol_id = s.id LEFT JOIN
comments c
ON c.id = cs.comment_id AND
c.date >= d.dte AND
c.date < d.date + interval 1 day
GROUP BY d.dte, s.symbol
)
SELECT cs.*
FROM (SELECT cs.*,
ROW_NUMBER() OVER (PARTITION BY dte ORDER BY mentions DESC) as seqnum
FROM cs
) cs
WHERE seqnum <= 15;
ORDER BY dte, mentions DESC;

How to query a hotel database to return the query for a single room available for three consecutive nights?

I'm trying to find an answer to the following query:
A customer wants a single room for three consecutive nights. Find the first available date in December 2016.
As per the question, this should be the right answer. But I don't know how to solve it.
+-----+------------+
| id | MIN(i) |
+-----+------------+
| 201 | 2016-12-11 |
+-----+------------+
The link is from question number 14 here.
This is the ER diagram of the database:
I apologize that I'm a bit rusty with this kind of query and I can't guarantee that I got all of the syntax correct, but I think that something like the following might work:
SELECT id, DATE_ADD(b.booking_date, INTERVAL (end_date + 1 DAY) as date
FROM (
SELECT r.id, STR_TO_DATE('2016-01-01', '%Y-%m-%d') as start_of_month, b.booking_date as start_date, DATE_ADD(b.booking_date, INTERVAL (nights - 1) DAY) as end_date
FROM room r
LEFT JOIN booking b ON r.id = b.room_no
ORDER BY r.id, b.booking_date
) as room_bookings
WHERE DATE_DIFF(room_bookings.start_of_month, room_bookings.start_date) >= 3
OR DATE_DIFF(room_bookings.end_date, (
SELECT b2.booking_date FROM booking b2
WHERE b2.room_no = room_bookings.id AND b2.booking_date > room_bookings.start_date
ORDER BY b2.booking_date LIMIT 1)
) >= 3
In fact, now that I type that all out, you might be able to tweak the WHERE of the main query so that you don't even need the room_bookings subselect. Hopefully this helps and isn't too far off the mark.
This seems very hard to do without a calendar table -- because an appropriate room might have no booking at all during the month. Without any booking, there is no record in the month to start with.
select r.id, dte
from rooms r cross join
(select date('2018-12-01') as dte union all
select date('2018-12-02') as dte union all
. . .
select date('2018-12-32') as dte
) d
where not exists (select 1 from bookings b where b.room_no = r.id and b.booking_date = d.dte) and
not exists (select 1 from bookings b where b.room_no = r.id and b.booking_date = d.dte + interval 1 day) and
not exists (select 1 from bookings b where b.room_no = r.id and b.booking_date = d.dte + interval 2 day)
order by d.dte
limit 1;
This assumes that booking_date is the start of the stay. You need to provide the logic for a "single room".
select distinct top 1 alll.i,alll.room_no,
case
when (select count(*) from booking where room_no = alll.room_no and booking_date between dateadd(day,1,alll.i) and dateadd(day,3,alll.i)) > 0 then 'Y'
else 'N'
end as av3
from
(select c.i,b.room_no,b.booking_date
from calendar c cross join booking b
where month(c.i) = 12 and year(c.i) = 2016 and b.room_type_requested = 'single'
) as alll
join
(
select distinct c.i, b.room_no
from calendar c join booking b
on c.i between b.booking_date and DATEADD(day,b.nights-1,b.booking_date)
where month(c.i) = 12 and year(c.i) = 2016 and b.room_type_requested = 'single'
) as booked
on alll.i = booked.i
and alll.room_no <> booked.room_no
order by 1
This works. It is a little complicated but basically first checks all the rooms that are booked and then does a comparison between rooms not booked on each day of the month till the next 3 days.
My solution is separate problem into 2 parts (in the end was 2 queries joined together). May not be the most efficient but the solution is correct.
1) Of the single rooms, look at the last check-out date, and see which one is vacant first (i.e. no more bookings for the rest of the month)
2) check in between current reservations - and see if there's a 3 day gap between them
3) join those together - grab the min
WITH subquery AS( -- existing single-bed bookings in Dec
SELECT room_no, booking_date,
DATE_ADD(booking_date, INTERVAL (nights-1) DAY) AS last_night
FROM booking
WHERE room_type_requested='single' AND
DATE_ADD(booking_date, INTERVAL (nights-1) DAY)>='2016-12-1' AND
booking_date <='2016-12-31'
ORDER BY room_no, last_night)
SELECT room_no, MIN(first_avail) AS first_avail --3) join the 2 together
FROM(
-- 1) check the last date the room is booked in December (available after)
SELECT room_no, MIN(first_avail) AS first_avail
FROM(
SELECT room_no, DATE_ADD(MAX(last_night), INTERVAL 1 DAY) AS first_avail
FROM subquery q3
GROUP BY 1
ORDER BY 2) AS t2
UNION
-- 2) check if any 3-day exist in between reservations
SELECT room_no, DATE_ADD(MIN(end2), INTERVAL 1 DAY) AS first_avail
FROM(
SELECT q1.booking_date AS beg1, q1.room_no, q1.last_night AS end1,
q2.booking_date AS beg2, q2.last_night AS end2
FROM subquery q1
JOIN subquery q2
ON q1.room_no = q2.room_no AND q2.booking_date > q1.last_night
GROUP BY 2,1
ORDER BY 2,1) AS t
WHERE beg2-end1 > 3) AS inner_t
This works conceptually as the first avaiable date should always be the end of the previous booking.
SELECT MIN(DATE_ADD(a.booking_date, INTERVAL nights DAY)) AS i
FROM booking AS a
WHERE DATE_ADD(a.booking_date, INTERVAL nights DAY)
>= '2016-12-01'
AND room_type_requested = 'single'
AND NOT EXISTS
(SELECT 1 FROM booking AS b
WHERE b.booking_date BETWEEN
DATE_ADD(a.booking_date, INTERVAL nights DAY)
AND DATE_ADD(a.booking_date, INTERVAL nights+2 DAY)
AND a.room_no = b.room_no)

Incorrect rows returned with date condition

I have a query that left joins two table with a date condition. I want to fetch rows for yesterday's transactions only.
Here the query:
When I add the AND condition still all the rows are returned but with null values to those not matching condition.
SELECT
B.txn_id,
B.txn_time,
B.svc_method,
B.customer_number,
B.amount,
B.amount_commission,
B.status,
A.partner_txn_id,
A.session_id as partner_session_id
FROM Partner A
LEFT JOIN Transaction B
ON A.log_id = B.txn_id
AND B.txn_time >= (CURDATE() - INTERVAL 1 DAY);
YOu should either change LEFT JOIN to INNER JOIN
or
move the call to WHERE section
B.txn_time >= (CURDATE() - INTERVAL 1 DAY)

Filling empty MySQL Result

I want to have a continuos date set with the sales.
SELECT *,
UNIX_TIMESTAMP(calendar.datefield) * 1000 AS time,
IFNULL(Sum(mos + mosnk), 0) AS mosfinal,
IFNULL(Sum(neukunden), 0) AS neukunden
FROM sms_stats
RIGHT JOIN calendar
ON ( DATE(sms_stats.date) = calendar.datefield )
WHERE calendar.datefield BETWEEN Curdate() - INTERVAL 90 day AND Now()
GROUP BY Date_format(calendar.datefield, '%Y%m%d')
this returns me a list of the last 90 days. Now I want to filter it, but if I do
WHERE owner = 2 AND calendar.datefield BETWEEN Curdate() - INTERVAL 90 day AND Now()
it just returns one result and not the list of dates.
The condition in the where clause "undoes" the right outer join. The solution is to move the condition to the where clause. I have a preference for left outer join over right outer join, so I'll swap the tables:
SELECT *,
UNIX_TIMESTAMP(c.datefield) * 1000 AS time,
IFNULL(Sum(mos + mosnk), 0) AS mosfinal,
IFNULL(Sum(neukunden), 0) AS neukunden
FROM calendar c LEFT JOIN
sms_stats ss
ON DATE(ss.date) = c.datefield and
ss.owner = 2
WHERE c.datefield BETWEEN Curdate() - INTERVAL 90 day AND Now()
GROUP BY Date_format(c.datefield, '%Y%m%d') ;
I added in table aliases to make the query a bit more readable.

Trying to correct a mysql query

I currently have the following query;
SELECT a.schedID,
a.start AS eventDate, b.div_id AS divisionID, b.div_name AS divisionName
FROM schedules a
INNER JOIN divisions b ON b.div_id = a.div_id
WHERE date_format(a.start, '%Y-%m-%d') >= '2010-01-01'
AND DATE_ADD(a.start, INTERVAL 5 DAY) <= CURDATE()
AND NOT EXISTS (SELECT results_id FROM results e WHERE e.schedID = a.schedID)
ORDER BY eventDate ASC;
Im trying to basically find any schedules that do not have any results 5 days after the schedule date. My current query has major performance issues. It also times out inconsistently. Is there a different way to write the query? Im at a mental roadblock. Any help is appreciated.
Without antcipating much on the outcome I would suggest the following leads :
* try to remove the date_format as this generates one function call per record. I don't know the format of your column a.start but this should be possible.
* same for DATE_ADD, you could probably put it on the other member like :
a.start <= DATE_SUB(CURDATE(), INTERVAL 5 DAYS)
you get a chance the result is cached rather than being calculated for each line, you could even define it as a parameter upfront
* the NOT EXISTS is very expensive, it seems to mee you could replace this by a left join like :
schedules a LEFT JOIN results e ON a.schedId = e.schedId WHERE e.schedId is NULL
double-check that all join fields are well indexed.
Good luck
Maybe something like:
SELECT
a.schedID, a.start AS eventDate, b.div_id AS divisionID, b.div_name AS divisionName
FROM
schedules a
INNER JOIN divisions b ON b.div_id = a.div_id
WHERE
date_format(a.start, '%Y-%m-%d') >= '2010-01-01'
AND NOT EXISTS (
SELECT
*
FROM
results e
INNER JOIN schedules a2 ON e.schedID = a2.schedID
WHERE
DATE_ADD(a2.start, INTERVAL 5 DAY) <= CURDATE()
AND a2.id = a.id
)
ORDER BY eventDate ASC;
dont know if mysql is same as oracle but are you converting a date to a string here and then comparing it with a string '2010-01-01' ? Can you convvert 2010-01-01 to a date instead so that if there is an index on a.start, it can be used ?
Also does this query definitely return the right answer ?
You mention you want schedules without results 5 days after the schedule date but it looks like you are aksing for anything in the last 5 days ?
a.start >= 1-Jan-10 and start date + 5 days is before today
try this query
SELECT a.schedID,
a.start AS eventDate,
b.div_id AS divisionID,
b.div_name AS divisionName
FROM (SELECT * FROM schedules s WHERE DATE(s.start) >= '2010-01-01' AND DATE_ADD(s.start, INTERVAL 5 DAY) <= CURDATE()) a
INNER JOIN divisions b
ON b.div_id = a.div_id
LEFT JOIN (SELECT results_id FROM results) e
ON e.schedID = a.schedID
WHERE e.results_id = ''
ORDER BY eventDate ASC;