I think I understand what you are asking. A FULL OUTER alone won't give you the results you are looking for, at least as described in your question. It seems a little atypical to query results this way, and normally I would say to pull the data in 2 queries, and display in your tool separately as there is no real relationship being displayed in your result.
However in the interest of answering the question...
CREATE TABLE Requested_Products (
orderId VARCHAR(128)
,productId VARCHAR(128)
,productDesc VARCHAR(128)
,prodQty NUMBER(18)
);
COPY Requested_Products FROM STDIN DELIMITER '|';
order1|product1|description1|1
order2|product2|description2|2
order2|product3|description3|5
order2|product4|description4|6
\.
CREATE TABLE Used_Materials (
orderId VARCHAR(128)
,materialId VARCHAR(128)
,materialDesc VARCHAR(128)
,matQty NUMBER(18)
);
COPY Used_Materials FROM STDIN DELIMITER '|';
order1|material1|description4|3
order1|material2|description5|6
order1|material3|description6|2
order2|material4|description7|8
\.
\pset null null
SELECT COALESCE(RP.orderId, UM.orderId) AS orderId, productId, productDesc, prodQty, materialId, materialDesc, matQty
FROM ( SELECT X.*, ROW_NUMBER() OVER (PARTITION BY OrderId) Order_Product_Number FROM Requested_Products X ) AS RP
FULL OUTER JOIN
( SELECT X.*, ROW_NUMBER() OVER (PARTITION BY OrderId) Order_Material_Number FROM Used_Materials X ) AS UM
ON ( RP.orderId = UM.orderId AND RP.Order_Product_Number = UM.Order_Material_Number );
orderId | productId | productDesc | prodQty | materialId | materialDesc | matQty
---------+-----------+--------------+---------+------------+--------------+--------
order1 | product1 | description1 | 1 | material1 | description4 | 3
order1 | null | null | null | material2 | description5 | 6
order1 | null | null | null | material3 | description6 | 2
order2 | product2 | description2 | 2 | material4 | description7 | 8
order2 | product3 | description3 | 5 | null | null | null
order2 | product4 | description4 | 6 | null | null | null
(6 rows)
As for what and why... You have to use FULL OUTER to ensure all rows on either side of the join are pulled in. You don't want a fanout, though. To do this, we will line up the data using a sequence generated by the ROW_NUMBER() partitioned by orderId. This will create a number that resets on change of orderId so we can join on it. You may decide you want to order the sequences a certain way, and you can do that in the partition by clause. Finally, because orderId may be null for either table and you are using a FULL OUTER, you must COALESCE to ensure you always show a non-null value.
This probably won't be efficient. I do feel like I'm giving you a shotgun and aiming it at your foot for you. I'll just have to assume you know what you're doing, look the other way, and put my finger in my ears.