Domanda

I built an Oracle database (version 9.2, I know it is obsolete, but it is the version installed at my university and I do not have a choice) with a list of users and their immediate friends. I am working on a set of queries to list the "network" of a user, including his friends' friends, and to compute the degree of separation between the user in question and the other members.

I tested the three queries separately and I confirm they do work.

I would now like to create a PL/SQL procedure to combine the 3 queries, so as to be able to feed the ID of a player as a parameter, but also to launch the three queries with one Execute statement.

However, I am unable to run the procedure. I am not even able to convert the first query in a procedure (see my code below).

Could someone please explain what I am doing wrong?

Here are the queries (they are working):

INSERT INTO UserFriendsCopy
SELECT *
FROM UserFriends
WHERE PlayerID < FriendsWith;

INSERT INTO Degrees_Separation
   SELECT PlayerID, FriendsWith, LEVEL AS Degree
      FROM UserFriendsCopy
      START WITH PlayerID = 1
      CONNECT BY PRIOR FriendsWith = PlayerID;

SELECT FriendsWith, MIN(Degree)
FROM Degrees_Separation
GROUP BY FriendsWith;

Here is a procedure that I wrote to replace the first query. It is not working. Why?

CREATE OR REPLACE PROCEDURE procDegrees1
AS
DECLARE
    CURSOR UserFriendsNoDupl IS SELECT * FROM UserFriends WHERE PlayerID < FriendsWith;
BEGIN
    FOR record IN UserFriendsNoDupl
LOOP
    EXIT WHEN UserFriendsNoDupl%NOTFOUND;
    INSERT INTO UserFriendsCopy VALUES(record.PlayerID, record.FriendsWith);
END LOOP;
END;/

EXECUTE procDegrees1/

[UPDATE] I have re-written the procedure as follows, to take into account the comments received so far. Still, it does not work.

CREATE OR REPLACE PROCEDURE procDegrees1
AS
BEGIN
FOR record IN (SELECT * FROM UserFriends WHERE PlayerID < FriendsWith)
LOOP
    INSERT INTO UserFriendsCopy(PlayerID,FriendsWith) VALUES(record.PlayerID, record.FriendsWith);
END LOOP;
END;/

By the way, here are the tables definitions, along with some dummy records:

DROP TABLE Degrees_Separation;
DROP TABLE UserFriends;
DROP TABLE UserFriendsCopy;

CREATE TABLE UserFriends (
    PlayerID INT NOT NULL,
    FriendsWith INT NOT NULL,
    CONSTRAINT pkUserFriends
        PRIMARY KEY (PlayerID, FriendsWith),
    CONSTRAINT fkPlayerIDThird
        FOREIGN KEY (PlayerID)
        REFERENCES Player (PlayerID),
    CONSTRAINT fkPlayerIDFourth
        FOREIGN KEY (FriendsWith)
        REFERENCES Player (PlayerID)
);

CREATE TABLE UserFriendsCopy (
    PlayerID INT NOT NULL,
    FriendsWith INT NOT NULL,
    CONSTRAINT pkUserFriendsBis
        PRIMARY KEY (PlayerID, FriendsWith)
);

INSERT INTO UserFriends (PlayerID, FriendsWith)
VALUES (1, 2);

INSERT INTO UserFriends (PlayerID, FriendsWith)
VALUES (2, 1);

INSERT INTO UserFriends (PlayerID, FriendsWith)
VALUES (2, 3);

INSERT INTO UserFriends (PlayerID, FriendsWith)
VALUES (3, 2);

INSERT INTO UserFriends (PlayerID, FriendsWith)
VALUES (2, 4);

INSERT INTO UserFriends (PlayerID, FriendsWith)
VALUES (4, 2);

INSERT INTO UserFriends (PlayerID, FriendsWith)
VALUES (1, 4);

INSERT INTO UserFriends (PlayerID, FriendsWith)
VALUES (4, 1);

INSERT INTO UserFriends (PlayerID, FriendsWith)
VALUES (5, 6);

INSERT INTO UserFriends (PlayerID, FriendsWith)
VALUES (6, 5);

INSERT INTO UserFriends (PlayerID, FriendsWith)
VALUES (3, 8);

INSERT INTO UserFriends (PlayerID, FriendsWith)
VALUES (8, 3);


CREATE TABLE Degrees_Separation (
    PlayerID INT NOT NULL,
    FriendsWith INT NOT NULL,
    Degree INT NOT NULL,
    CONSTRAINT fkPlayerIDSeventh
        FOREIGN KEY (PlayerID)
        REFERENCES Player (PlayerID),
    CONSTRAINT fkPlayerIDEighth
        FOREIGN KEY (FriendsWith)
        REFERENCES Player (PlayerID)
);

Thanks, LC

È stato utile?

Soluzione

The SQL*Plus reference says:

SQL*Plus treats PL/SQL subprograms in the same manner as SQL commands, except that a semicolon (;) or a blank line does not terminate and execute a block. Terminate PL/SQL subprograms by entering a period (.) by itself on a new line. You can also terminate and execute a PL/SQL subprogram by entering a slash (/) by itself on a new line.

You are putting the slash on the same line as the end;, which isn't valid. You need to do:

CREATE OR REPLACE PROCEDURE procDegrees1
AS
BEGIN
  FOR record IN (SELECT * FROM UserFriends WHERE PlayerID < FriendsWith)
  LOOP
    INSERT INTO UserFriendsCopy(PlayerID,FriendsWith)
    VALUES(record.PlayerID, record.FriendsWith);
  END LOOP;
END;
/

When you call it with execute you should end with a semicolon, not a slash:

EXECUTE procDegrees1;

This is an SQL*Plus shorthand for an anonymous block, and what you have after the execute becomes the single statement within an anonymous block, so it's expanded for actual execution and is the same as doing this:

BEGIN
  procDegrees1;
END;
/

You can see that if the statement itself causes an error. But it's important to note that execute this is not a SQL statement, it is a client command. SQL*Plus and SQL Developer recognise it, but other clients may not.

I started this answer before you mentioned in a comment that you were using Squirrel. I don't know if Squirrel allows execute, but the error you're getting suggests not. If it doesn't then you'll need to use the expanded anonymous block form, which should work anywhere.


Google suggests that execute isn't supported but you might be able to do this:

CALL procDegrees1

... but I have no idea if that's actually true...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top