Reputation: 378
this is the "solution" to a question from a course i am taking. but when i copied it into my postgrSQL 9.5.1 program, and it is riddled with errors.
CREATE RECURSIVE VIEW Ancestor AS
((SELECT parents.child AS Dec, parents.father AS Anc
FROM windsor.parents)
UNION
(SELECT parents.child AS Dec, parents.mother AS Anc
FROM windsor.parents)
UNION
(SELECT parents.Father AS Anc, Ancestor.Dec As Dec
FROM windsor.parents,Ancestor
WHERE parents.child = Ancestor.Anc)
UNION
(Select parents.mother AS Anc, Ancestor.Dec As Dec
FROM windsor.parents, Ancestor
WHERE parents.child = Ancestor.Anc))
It threw an error in line:1, with a syntaxerror for 'AS'. After looking into some declarations here and trying some stuff, it seemed like changing it to
CREATE RECURSIVE VIEW Ancestor(Anc,Dec) AS
worked, but now it throws an error the first time i try to call Ancestor in a FROM
command. the Error this time is:
Error: rekursiver Verweis auf Anfrage „ancestor“ darf nicht in ihrem nicht-rekursiven Teilausdruck erscheinen
SQL Status:42P19
or in my broken english:
recursive call for "ancestor" mustn't be in a non-recursive part
like i said this is not my code. it is a "solution" for a question from a course i am attending. i am trying to fix this answer but dont want to drift too far away from the original intent with this code.
cheers - Jacobus
Upvotes: 1
Views: 497
Reputation: 27424
The error is caused by the fact that in your view definition, you have two different recursive clauses, while only one is admitted.
In the PostgreSQL manual the recursive view is defined as following:
CREATE RECURSIVE VIEW name (columns) AS SELECT ...;
is equivalent to
CREATE VIEW name AS WITH RECURSIVE name (columns) AS (SELECT ...) SELECT columns FROM name;
A view column list must be specified for a recursive view.
(note that the last statement require that you use CREATE RECURSIVE VIEW Ancestor(Anc,Dec)
)
Then, in the page about WITH RECURSIVE
:
If RECURSIVE is specified, it allows a SELECT subquery to reference itself by name. Such a subquery must have the form
non_recursive_term UNION [ ALL | DISTINCT ] recursive_term
where the recursive self-reference must appear on the right-hand side of the UNION. Only one recursive self-reference is permitted per query. (emphasis is mine).
So, you should change your query by using only a recursive subselect.
Edit
Here is a possible solution, with a small modification of the query published by @JacobusConradi in a comment below:
CREATE RECURSIVE VIEW ancestor(anc, dec) AS
SELECT father AS anc, child AS dec
FROM windsor.parents
WHERE father is not null
UNION
SELECT mother AS anc, child AS dec
FROM windsor.parents
WHERE mother is not null
UNION
SELECT anc, child AS dec
FROM windsor.parents, ancestor
WHERE dec = father OR dec = mother
The modification concerns: 1) the elimination of DISTINCT (we can assume that child
is a primary key of the original table, and in any case the use of the UNION
operator automatically eliminates duplicates), 2) the addition of a test for null values, otherwise in the result would appear tuples with null values for either anc
or dec
.
Upvotes: 1
Reputation: 21346
As Renzo pointed out, you've got two issues to work around:
UNION
The second restriction is easily addressed by combining the last two elements of the UNION
into a subquery; instead of A UNION B UNION C UNION D
, simply say A UNION B UNION (C UNION D)
.
As for factoring out the recursive call to a single reference, this is pretty straightforward to do with a common table expression (CTE), giving you something like this:
CREATE RECURSIVE VIEW Ancestor(Anc,Dec) AS
SELECT parents.child AS Dec, parents.father AS Anc
FROM windsor.parents
UNION
SELECT parents.child AS Dec, parents.mother AS Anc
FROM windsor.parents
UNION
(
WITH cte AS (SELECT * FROM Ancestor)
SELECT parents.Father AS Anc, cte.Dec As Dec
FROM windsor.parents, cte
WHERE parents.child = cte.Anc
UNION
SELECT parents.Mother AS Anc, cte.Dec As Dec
FROM windsor.parents, cte
WHERE parents.child = cte.Anc
)
Upvotes: 0