I'm trying to find the available seat. I try using the following code, to get the total row after that minus the value at capacity. But it fails to do so. May I know what is the correct way to do it ?
SELECT r.Route_No, s.Serial_No, s.Flight_Fly_On, a.Capacity, (Capacity - (count(*) FROM scheduled) AS Available_Seat
FROM Route r, Scheduled s, Airplane a
WHERE s.Serial_No = a.Serial_No
AND r.Route_No = s.Route_No;
Assuming that your SCHEDULED table counts when a person is scheduled on a particular flight and that you can uniquely identify a flight using the route, airplane's serial number and the flight date then you can us an analytic COUNT function (rather than using a sub-query):
SELECT r.Route_No,
s.Serial_No,
s.Flight_Fly_On,
a.Capacity,
a.Capacity - COUNT(*) OVER (PARTITION BY s.route_no, s.serial_no, s.flight_fly_on)
AS Available_Seat
FROM Route r
INNER JOIN Scheduled s
ON (r.Route_No = s.Route_No)
INNER JOIN Airplane a
ON (s.Serial_No = a.Serial_No);
If you did want to use a sub-query then you need to correlate it to the outer query:
SELECT r.Route_No,
s.Serial_No,
s.Flight_Fly_On,
a.Capacity,
a.Capacity - (SELECT COUNT(*)
FROM Scheduled c
WHERE s.route_no = c.route_no
AND s.serial_no = c.serial_no
AND s.flight_fly_on = c.flight_fly_on)
AS Available_Seat
FROM Route r
INNER JOIN Scheduled s
ON (r.Route_No = s.Route_No)
INNER JOIN Airplane a
ON (s.Serial_No = a.Serial_No);
If it is not the SCHEDULED table that holds details of a person's bookings but it is, for example, the BOOKINGS table then you are using the wrong table and need to count from the correct table:
SELECT r.Route_No,
s.Serial_No,
s.Flight_Fly_On,
a.Capacity,
a.Capacity - (SELECT COUNT(*)
FROM Bookings b
WHERE b.scheduled_no = s.scheduled_no)
AS Available_Seat
FROM Route r
INNER JOIN Scheduled s
ON (r.Route_No = s.Route_No)
INNER JOIN Airplane a
ON (s.Serial_No = a.Serial_No);
Please test the following query.
I have normalised the JOIN conditions, specified that we are counting the schedule table, and added GROUP BY.
I have also added the column count(s.Route_No) AS seats_booked so that, if the query doesn't return the expected results you can check whether the COUNT() is correct. This will help find the problem if there is one.
SELECT
r.Route_No,
s.Serial_No,
s.Flight_Fly_On,
a.Capacity AS total_seats,
COUNT(s.Route_No) AS seats_booked
(a.Capacity - (count(s.Route_No) FROM scheduled) AS Available_Seats
FROM Route r
JOIN Scheduled s ON r.Route_No = s.Route_No
JOIN Airplane a on s.Serial_No = a.Serial_No
GROUP BY
r.Route_No,
s.Serial_No,
s.Flight_Fly_On,
a.Capacity ;
Related
I am attempting to optimize a query in MariaDb that is really bogged down by its ORDER BY clause. I can run it in under a tenth of a second without the ORDER BY clause, but it takes over 25 seconds with it. Here is the gist of the query:
SELECT u.id, u.display_name, u.cell_phone, u.email,
uv.year, uv.make, uv.model, uv.id AS user_vehicle_id
FROM users u
LEFT JOIN user_vehicles uv ON uv.user_id = u.id AND uv.current_owner=1
WHERE u.is_deleted = 0
GROUP BY u.id
ORDER BY u.display_name
LIMIT 0, 10;
I need it to be a left join because I want to include users that aren't linked to a vehicle.
I need the group by because I want only 1 result per user (and display_name is not guaranteed to be unique).
users table has about 130K rows, while user_vehicles has about 230K rows.
Here is the EXPLAIN of the query:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE u index dms_cust_idx PRIMARY 4 null 124825 Using where; Using temporary; Using filesort
1 SIMPLE uv ref user_idx user_idx 4 awscheduler.u.id 1 Using where
I have tried these two indices to speed things up, but they don't seem to do much.
CREATE INDEX idx_display_speedy ON users(display_name);
CREATE INDEX idx_display_speedy2 ON users(id, display_name, is_deleted, dms_cust_id);
I am looking for ideas on how to speed this up. I attempted using nested queries, but since the order by is the bottleneck & order within the nested query is ignored, I believe that attempt was in vain.
how about:
WITH a AS (
SELECT u.id, u.display_name, u.cell_phone, u.email
FROM users u
WHERE u.is_deleted = 0
GROUP BY u.id
LIMIT 0, 10
)
SELECT a.id, a.display_name, a.cell_phone, a.email,
uv.year, uv.make, uv.model, uv.id AS user_vehicle_id
FROM a LEFT JOIN user_vehicles uv ON uv.user_id = a.id AND uv.current_owner=1
ORDER BY a.display_name;
The intention is we take a subset of users before joining it with user_vehicles.
Disclaimer: I haven't verified if its faster or not, but have similar experience in the past where this helps.
with a as (
SELECT u.id, u.display_name, u.cell_phone, u.email,
uv.year, uv.make, uv.model, uv.id AS user_vehicle_id
FROM users u
LEFT JOIN user_vehicles uv ON uv.user_id = u.id AND uv.current_owner=1
WHERE u.is_deleted = 0
GROUP BY u.id
)
select * from a
ORDER BY u.display_name;
)
I suspect it's not actually the ordering that is causing the problem... If you remove the limit, I bet the ordered and un-ordered versions will end up performing pretty close to the same.
Depending on if your actual query is as simple as the one you posted, you may be able to get good performance in a single query by using RowNum() as described here:
SELECT u.id, u.display_name, u.cell_phone, u.email,
uv.year, uv.make, uv.model, uv.id AS user_vehicle_id
FROM (
SELECT iu.id, iu.display_name, iu.cell_phone, iu.email
FROM users iu
WHERE iu.is_deleted = 0
ORDER BY iu.display_name) as u
LEFT JOIN user_vehicles uv ON uv.user_id = u.id AND uv.current_owner=1
WHERE ROWNUM() < 10
GROUP BY u.id
ORDER BY u.display_name
If that doesn't work, you probably need to select the users in one select and then select their vehicles in a second Select
I have two query that looks close to the same but Oracle have very different performance.
Query A
Create Table T1 as Select * from FinalView1 where CustomerID in ('A0000001','A000002')
Query B
Create Table T1 as Select * from FinalView1 where CustomerID in (select distinct CustomerID from CriteriaTable)
The CriteriaTable have 800 rows but all belongs to Customer ID 'A0000001' and 'A000002'.
This means the subquery: "select distinct CustomerID from CriteriaTable" also only returns the same two elements('A0000001','A000002') as manually entered in query A
Following is the query under the FinalView1
create or replace view FinalView1_20200716 as
select
Customer_ID,
<Some columns>
from
Table1_20200716 T1
INNER join Table2_20200716 T2 on
T1.Invoice_number = T2.Invoice_number
and
T1.line_id = T2.line_id
left join Table3_20200716 T3 on
T3.id = T1.Customer_ID
left join Table4_20200716 T4 on
T4.Shipping_ID = T1.Shipping_ID
left join Table5_20200716 Table5 on
Table5.Invoice_ID = T1.Invoice_ID
left join Table6_20200716 T6 on
T6.Shipping_ID = T4.Shipping_ID
left join First_Order first on
first.Shipping_ID = T1.Shipping_ID
;
Table1_20200716,Table2_20200716,Table3_20200716,Table4_20200716,Table5_20200716,Table6_20200716 are views to the corresponding table with temporal validity feature. For example
The query under Table1_20200716
Create or replace view Table1_20200716 as
select
*
from Table1 as for period of to_date('20200716,'yyyymmdd')
However table "First_Order" is just a normal table as
Following is the performance for both queries (According to explain plan):
Query A:
Cardinality: 102
Cost : 204
Total Runtime: 5 secs max
Query B:
Cardinality:27921981
Cost: 14846
Total Runtime:20 mins until user cancelled
All tables are indexed using those columns that used to join against other tables in the FinalView1. According to the explain plan, they have all been used except for the FirstOrder table.
Query A used uniquue index on the FirstOrder Table while Query B performed a full scan.
For query B, I was expecting the Oracle will firstly query the sub-query get the result into the in operator, before executing the main query and therefore should only have minor impact to the performance.
Thanks in advance!
As mentioned from my comment 2 days ago. Someone have actually posted the solution and then have it removed while the answer actually work. After waiting for 2 days the So I designed to post that solution.
That solution suggested that the performance was slow down by the "in" operator. and suggested me to replace it with an inner join
Create Table T1 as
Select
FV.*
from
FinalView1 FV
inner join (
select distinct
CustomerID
from
CriteriaTable
) CT on CT.customerid = FV.customerID;
Result from explain plan was worse then before:
Cardinality:28364465 (from 27921981)
Cost: 15060 (from 14846)
However, it only takes 17 secs. Which is very good!
I have three tables and trying to join these tables with count(working_days) and sum(Overtime) functions but overtime is not showing correct i am trying the query is.
SELECT E.EMP_CODE,E.EMP_NAME,
COALESCE(SUM(O.OVERTIME),0) AS OVERTIME,COALESCE(COUNT(C.EMP_ATT),0) WORKED_DAYS
FROM EMP E
LEFT JOIN OVERTIME O
ON E.EMP_CODE = O.EMP_CODE
LEFT JOIN ATT_REG C
ON E.EMP_CODE=C.EMP_CODE
GROUP BY E.EMP_CODE,E.EMP_NAME
ORDER BY EMP_CODE
Yes, You will get multiple records of the overtime for employees as shift date (or similar in your table) is missing in the join.
SELECT E.EMP_CODE,E.EMP_NAME,
COALESCE(SUM(O.OVERTIME),0) AS OVERTIME,
COALESCE(COUNT(C.EMP_ATT),0) WORKED_DAYS
FROM EMP E
LEFT JOIN ATT_REG C
ON E.EMP_CODE=C.EMP_CODE
LEFT JOIN OVERTIME O
ON E.EMP_CODE = O.EMP_CODE
AND C.SHIFTDATE = O.SHIFDATE -- YOU NEED SOMETHING LIKE THIS
GROUP BY E.EMP_CODE,E.EMP_NAME
ORDER BY EMP_CODE
We need to find out total cost for each project in XYZ company (from the project). Total cost is considered to be sum of (unit price * quantity) from invoice details table.
To find out total cost for each project you need join 3 tables in the query: XYZ_PROJECTS with XYZ_INVOICES (using PROJECT_NUMBER) and XYZ_INVOICES with XYZ_INVOICE_DETAILS (using INVOICE_NUMBER).
Go to SQL WorkshopSQL Commands. Write and execute a “select” query to select project number, project name from XYZ_PROJECTS, sum(c.qty * c.Unit_price) from XYZ_INVOICE_DETAILS. (You need to add “group by project_number, project_name” at the end of the query since SUM aggregate function is used). Run the query. Your screen should like as follows
I used this to join table but I do not know how to join 3 table at the same time.
select *
from XYZ_PROJECTS c
join XYZ_INVOICES do on c.PROJECT_NUMBER = do.PROJECT_NUMBER
select *
from XYZ_INVOICES i
join XYZ_INVOICE_DETAILS do on i.INVOICE_NUMBER = do.INVOICE_NUMBER
Maybe I do it wrong. Please kindly help me.
select c.project_number, c.project_name, sum(do.qty * do.Unit_price)
from XYZ_PROJECTS c
join XYZ_INVOICES i
on c.PROJECT_NUMBER = i.PROJECT_NUMBER
join XYZ_INVOICE_DETAILS do
on i.INVOICE_NUMBER = do.INVOICE_NUMBER
group by c.project_number, c.project_name
If you need to include zero cost projects who do not have any invoices yet, use left outer join to keep all rows from XYZ_PROJECTS:
select c.project_number, c.project_name, sum(do.qty * do.Unit_price)
from XYZ_PROJECTS c
left join XYZ_INVOICES i
on c.PROJECT_NUMBER = i.PROJECT_NUMBER
left join XYZ_INVOICE_DETAILS do
on i.INVOICE_NUMBER = do.INVOICE_NUMBER
group by c.project_number, c.project_name
I am very new at using Oracle (in the class now). I have a problem with the query I am trying to run. I have researched a lot of other answers on this site and none of them seem to apply directly to my problem so the solutions aren't working.
I need to find the total amount spent on lunches by each employee.
Show first_name, last_name, credit_limit, and total_price_spent in your results.
Order the answer by total_price_spent in descending order. Only show the employees who spent more than their credit limit.
I figured out how to do everything but the part about showing only the employees who spent more than their credit limit. I tried to use a select statement at the end but discovered that I can't use an alias in a select statement so I don't really know where to go from here. Any help would be appreciated. This is what I have so far.
select a.first_name, a.last_name, credit_limit, sum(c.quantity * d.price) as total_price_spent
from l_employees a join l_lunches b on a.employee_id = b.employee_id join l_lunch_items c on b.lunch_id = c.lunch_id join l_foods d on c.supplier_id = d.supplier_id and c.product_code = d.product_code
group by a.first_name, a.last_name, a.credit_limit
order by total_price_spent desc;
As Mike said : Add HAVING
select a.first_name, a.last_name, credit_limit, sum(c.quantity * d.price) as total_price_spent
from l_employees a join l_lunches b on a.employee_id = b.employee_id join l_lunch_items c on b.lunch_id = c.lunch_id join l_foods d on c.supplier_id = d.supplier_id and c.product_code = d.product_code
group by a.first_name, a.last_name, a.credit_limit
having sum(c.quantity * d.price) > credit_limit
order by total_price_spent desc;
I think what you're looking for is a HAVING clause. It's like a WHERE, but is used when you're using group by. You want to drop it in between the group by and order by. Something like 'HAVING total > a.credit_limit' should work. If using the alias 'total' doesn't work (haven't tested this), you might have to do 'sum(c.quantity * d.price)' again in the HAVING clause instead of using total, so HAVING sum(c.quantity * d.price) > a.credit_limit.