Reputation: 5174
I have a stored procedure that produces the correct output, and I'd like to store the results in a materialized view. I'm having trouble conceptualizing how to get this working because the procedure relies on a temporary table that I created called tmp$
.
My goal is to re-write the stored procedure so it doesn't need to utilize the temporary table and the results can be stored in a materialized view.
My end goal is to create a data structure that conforms to the requirements of the Modified Preorder Tree Traversal
algorithm.
tmp$
temporary table:
CREATE GLOBAL TEMPORARY TABLE tmp$
(
id VARCHAR2(8),
l INT,
r INT
) ON COMMIT DELETE ROWS;
Stored procedure:
CREATE OR REPLACE PACKAGE pkg AS
PROCEDURE p (
p_id IN VARCHAR2,
seq IN OUT INT
);
END pkg;
CREATE OR REPLACE PACKAGE BODY pkg AS
PROCEDURE p (
p_id IN VARCHAR2,
seq IN OUT INT
)
AS
BEGIN
seq := seq + 1;
INSERT INTO tmp$ (
id,
l,
r
) VALUES (
p_id,
seq,
NULL
);
FOR i IN (
SELECT
id
FROM
tab1
WHERE
parent_id = p_id
ORDER BY
id
) LOOP
p(i.id,seq);
END LOOP;
seq := seq + 1;
UPDATE tmp$
SET
r = seq
WHERE
id = p_id;
END;
END pkg;
tab1
data
+-----+-----------+
| id | parent_id |
+-----+-----------+
| 01 | null |
+-----+-----------+
| 100 | 01 |
+-----+-----------+
| 901 | 01 |
+-----+-----------+
| 250 | 01 |
+-----+-----------+
| 400 | 01 |
+-----+-----------+
| 001 | 01 |
+-----+-----------+
| 500 | 01 |
+-----+-----------+
| 600 | 01 |
+-----+-----------+
| 010 | 01 |
+-----+-----------+
| 450 | 01 |
+-----+-----------+
Calling the stored procedure:
VAR n NUMBER
EXEC :n := 0;
EXEC pkg.p('01',:n);
select * from tmp$;
The execution of pkg.p
populates the tmp$
table with the following data:
+-----+----+----+
| id | l | r |
+-----+----+----+
| 01 | 1 | 20 |
+-----+----+----+
| 001 | 2 | 3 |
+-----+----+----+
| 010 | 4 | 5 |
+-----+----+----+
| 100 | 6 | 7 |
+-----+----+----+
| 250 | 8 | 9 |
+-----+----+----+
| 400 | 10 | 11 |
+-----+----+----+
| 450 | 12 | 13 |
+-----+----+----+
| 500 | 14 | 15 |
+-----+----+----+
| 600 | 16 | 17 |
+-----+----+----+
| 901 | 18 | 19 |
+-----+----+----+
If I try to put those four lines above within a CREATE MATERIALIZED VIEW
clause, the materialized view is empty.
I'm not sure how to even start solving this. Should I look at using a collection table? I'm not sure how that will work since p
works recursively.
Upvotes: 4
Views: 141
Reputation: 553
In your stored procedure you are creating a nested set model.
Model your materialized view on a query that generates the nested set model from your original data table and this will remove the need for a temporary table.
It is a rather long exercise to implement, so here is an example already available taken from this article: CTEs with Multiple Recursive Members by Itzik Ben-Gan
https://www.itprotoday.com/development-techniques-and-management/ctes-multiple-recursive-members
*Note that this example is written for SQL Server, but the conversion should be trivial as the recursive CTEs also work in Oracle. There may be some data types that need to be modified to fit Oracle's syntax.
Listing 6: Code to Compute Left and Right Values in the Nested Set Model
WITH EmpsRN AS
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY mgrid ORDER BY empname, empid) * 2 - 1 AS n
FROM dbo.Employees
),
C1 AS
(
-- root's left arm
SELECT empid, 1 AS arm, CAST(0x01 AS VARBINARY(8000)) AS sortpath
FROM dbo.Employees
WHERE mgrid is NULL
UNION ALL
-- root's right arm
SELECT empid, 2 AS arm, CAST(0x02 AS VARBINARY(8000)) AS sortpath
FROM dbo.Employees
WHERE mgrid is NULL
UNION ALL
SELECT E.empid, 1 AS arm,
CAST(M.sortpath + CAST(E.n AS BINARY(1)) AS VARBINARY(8000)) AS sortpath
FROM C1 AS M
INNER JOIN EmpsRN AS E
ON E.mgrid = M.empid
WHERE M.arm = 1
UNION ALL
SELECT E.empid, 2 AS arm,
CAST(M.sortpath + CAST(E.n + 1 AS BINARY(1)) AS VARBINARY(8000)) AS sortpath
FROM C1 AS M
INNER JOIN EmpsRN AS E
ON E.mgrid = M.empid
WHERE M.arm = 1
),
c2 AS
(
SELECT empid, ROW_NUMBER() OVER(ORDER BY sortpath) AS sortval
FROM C1
)
SELECT empid, MIN(sortval) AS lft, MAX(sortval) AS rgt
FROM c2
GROUP BY empid;
Upvotes: 1