Question

Hello here's my question Retrieve the total number of bookings for each type of the services that have at least three bookings (excluding those cancelled). i.e. where status = 'open' AND 'done'

I'm not to sure on how to exclude and how to count values in a column?

SELECT Service.type, Service.description,   
COUNT (DISTINCT status) 
FROM Booking
LEFT JOIN Service
ON Booking.service = Service.type
WHERE status >= 3
EXCLUDE 'cancelled'
GROUP BY status DESC;

CREATE TABLE Booking(
car         CHAR(8)                             ,
on_date     DATE                        NOT NULL,
at_time     TIME                        NOT NULL,
technician  CHAR(6)                     NOT NULL,
service     VARCHAR(15)                 NOT NULL,
status      VARCHAR(9)CHECK(status IN ('open','done', 'cancelled')) DEFAULT 'open'     NOT         NULL, 
note        VARCHAR(200)                            ,
rating      INTEGER CHECK(rating IN('0','1','2','3','4','5')) DEFAULT '0'   NOT NULL,
feedback    VARCHAR(2048)                           ,

PRIMARY KEY (car, on_date, at_time),

FOREIGN KEY (car) REFERENCES Car (cid)
                ON DELETE CASCADE   
                ON UPDATE CASCADE, 

FOREIGN KEY (technician) REFERENCES Technician (tech_id)
                ON DELETE CASCADE   
                ON UPDATE CASCADE, 

FOREIGN KEY (service) REFERENCES Service (type)
                ON DELETE CASCADE   
                ON UPDATE CASCADE
);

CREATE TABLE Service(
type        VARCHAR(15)     PRIMARY KEY,
description VARCHAR(2048)   
);
Was it helpful?

Solution

It will be faster to aggregate first and join later. Fewer join operations. Hence the subquery:

SELECT s.type, s.description, b.ct
FROM  (
   SELECT service, count(*) AS ct
   FROM   booking
   WHERE  status <> 'cancelled'
   GROUP  BY 1
   HAVING count(*) > 2
   ) b
JOIN service s ON s.type = b.service;

Since you enforce referential integrity with a foreign key constraint and service is defined NOT NULL, you can as well use [INNER] JOIN instead of a LEFT [OUTER] JOIN in this query.

It would be cleaner and more efficient to use an enum data type instead of VARCHAR(9) for the status column. Then you wouldn't need the CHECK constraint either.

For best performance of this particular query, you could have a partial covering index (which would also profit from the enum data type):

CREATE INDEX foo ON booking (service)
WHERE  status <> 'cancelled';

Every index carries a maintenance cost, so only keep this tailored index if it actually makes your query faster (test with EXPLAIN ANALYZE) and it is run often and / or important.

OTHER TIPS

select s.type, s.description, count(*) 
from
    booking b
    inner join
    service s on b.service = s.type
where status != 'cancelled'
group by 1, 2
having count(*) >= 3
order by 3 desc;

How about:

SELECT Service.type, Service.description, COUNT (status) 
FROM Booking 
LEFT JOIN Service ON Booking.service = Service.type 
WHERE status != 'cancelled' 
GROUP BY Service.type, Service.description
HAVING COUNT(status) >= 2;

The service attributes have to be grouped as well. Filtering by aggregate, here COUNT(status), is what the HAVING clause does.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top