Question

I'm creating a database for a hypothetical video rental store. All I need to do is a procedure that check the availabilty of a specific movie (obviously the movie can have several copies). So I have to check if there is a copy available for the rent, and take the number of the copy (because it'll affect other trigger later..). I already did everything with the cursors and it works very well actually, but I need (i.e. "must") to do it without using cursors but just using "pure sql" (i.e. queries).

I'll explain briefly the scheme of my DB:

The tables that this procedure is going to use are 3: 'Copia Film' (Movie Copy) , 'Include' (Includes) , 'Noleggio' (Rent).

Copia Film Table has this attributes:

  • idCopia
  • Genere (FK references to Film)
  • Titolo (FK references to Film)
  • dataUscita (FK references to Film)

Include Table:

  • idNoleggio (FK references to Noleggio. Means idRent)
  • idCopia (FK references to Copia film. Means idCopy)

Noleggio Table:

  • idNoleggio (PK)
  • dataNoleggio (dateOfRent)
  • dataRestituzione (dateReturn)
  • dateRestituito (dateReturned)
  • CF (FK to Person)
  • Prezzo (price)

Every movie can have more than one copy. Every copy can be available in two cases:

  1. The copy ID is not present in the Include Table (that means that the specific copy has ever been rented)
  2. The copy ID is present in the Include Table and the dataRestituito (dateReturned) is not null (that means that the specific copy has been rented but has already returned)

The query I've tried to do is the following and is not working at all:

 SELECT COUNT(*)
 FROM NOLEGGIO
 WHERE dataNoleggio IS NOT NULL AND dataRestituito IS NOT NULL AND idNoleggio IN (
   SELECT N.idNoleggio
   FROM NOLEGGIO N JOIN INCLUDE I ON N.idNoleggio=I.idNoleggio
   WHERE idCopia IN (
     SELECT idCopia
     FROM COPIA_FILM
     WHERE titolo='Pulp Fiction')) -- Of course the title is just an example

Well, from the query above I can't figure if a copy of the movie selected is available or not AND I can't take the copy ID if a copy of the movie were available.

(If you want, I can paste the cursors lines that work properly)

------ USING THE 'WITH SOLUTION' ---- I modified a little bit your code to this

WITH film
as
(
  SELECT idCopia,titolo
    FROM COPIA_FILM
    WHERE titolo = 'Pulp Fiction'
),
copy_info as
(
  SELECT N.idNoleggio, N.dataNoleggio, N.dataRestituito, I.idCopia
    FROM NOLEGGIO N JOIN INCLUDE I ON N.idNoleggio = I.idNoleggio
),
avl as
(
  SELECT film.titolo, copy_info.idNoleggio, copy_info.dataNoleggio,
    copy_film.dataRestituito,film.idCopia
    FROM film LEFT OUTER JOIN copy_info
    ON film.idCopia = copy_info.idCopia
)
SELECT COUNT(*),idCopia FROM avl
WHERE(dataRestituito IS NOT NULL OR idNoleggio IS NULL)
GROUP BY idCopia

As I said in the comment, this code works properly if I use it just in a query, but once I try to make a procedure from this, I got errors. The problem is the final SELECT:

SELECT COUNT(*), idCopia INTO CNT,COPYFILM
FROM avl
WHERE (dataRestituito IS NOT NULL OR idNoleggio IS NULL)
GROUP BY idCopia

The error is: ORA-01422: exact fetch returns more than requested number of rows ORA-06512: at "VIDEO.PR_AVAILABILITY", line 9.

So it seems the Into clause is wrong because obviously the query returns more rows. What can I do ? I need to take the Copy ID (even just the first one on the list of rows) without using cursors.

Was it helpful?

Solution

You can try this -

WITH film
as
(
 SELECT idCopia, titolo
     FROM COPIA_FILM
     WHERE titolo='Pulp Fiction'
),
copy_info as
(
select N.idNoleggio, I.dataNoleggio , I.dataRestituito , I.idCopia
   FROM NOLEGGIO N JOIN INCLUDE I ON N.idNoleggio=I.idNoleggio
),
avl as
(
select film.titolo, copy_info.idNoleggio, copy_info.dataNoleggio, 
copy_info.dataRestituito   
from film LEFT OUTER JOIN copy_info
ON film.idCopia = copy_info.idCopia 
)
select * from avl
where (dataRestituito IS NOT NULL OR idNoleggio IS NULL);

OTHER TIPS

You should think in terms of sets, rather than records. If you find the set of all the films that are out, you can exclude them from your stock, and the rest is rentable.

select copiafilm.* from @f copiafilm 
left join
(
    select idCopia from @r Noleggio 
    inner join @i include on Noleggio.idNoleggio = include.idNoleggio 
    where dateRestituito is null
) out
    on copiafilm.idCopia = out.idCopia
where out.idCopia is null

I solved the problem editing the last query into this one:

SELECT COUNT(*),idCopia INTO CNT,idCopiaFilm
FROM avl
WHERE (dataRestituito IS NOT NULL OR idNoleggio IS NULL) AND rownum = 1
GROUP BY idCopia;

IF CNT > 0 THEN
  -- FOUND AVAILABLE COPY
END IF;

EXCEPTION
WHEN NO_DATA_FOUND THEN
  -- NOT FOUND AVAILABLE COPY

Thank you @Aditya Kakirde ! Your suggestion almost solved the problem.

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