0

I have exactly the same problem as this one except i am running on Oracle and not SQL-Server.

I have a structure like this :

[Polygons]

IdPolygon (Primary Key) 
VertexA 
VertexB
VertexC

[FrontFaces]

IdFace (Primary Key) 
IdPolygon (Foreign Key)
FaceValue

[LinesGroups]

IdLinesGroup (Primary Key) 
IdFace (Foreign Key)
LinesGroupValue

[Lines]

IdLine (Primary Key) 
IdLinesGroup (Foreign Key)
LineValue

[Points]

IdPoint (Primary Key) 
IdLine (Foreign Key)
PointValue

Here is the idea :

  • Trigger a procedure when a Polygon is added
  • if VertexA VertexB and VertexC in newly inserted Polygon are the same as ones of another existing Polygon in the Database then we duplicate all rows that have dependencies with this matching polygon : FrontFaces / LinesGroups / Lines / Points. The only value that must change in each table is unique primary key that must be set by a sequence_name.nextVal

Is there a way to do this without listing all table/rows (based on Foreign keys search ?)

Community
  • 1
  • 1
kwenji
  • 68
  • 1
  • 7
  • Yes, there's a way, but it's not trivial because of mutating triggers as well as what the use case is if there's a third newly added polygon. Not to mention it's difficult to understand WHY you'd need to completely duplicate contents the entire data structure when you could reuse the existing `PolygonID` value by having a unique key on `(VertexA, VertexB, VertexC)`. – Adam Musch Mar 07 '12 at 17:36
  • ok, let's say that the trigger part is not needed (the Application Tier can handle this). What could i do to create new content based on a search for table / content using Foreign keys from former entity ? And i need the newly created content do be independant from first one it's why i want to duplicate all data structure ;) – kwenji Mar 08 '12 at 08:36

3 Answers3

1

Consider this query:

with ri_tables as
(select parent, parent_key, child, foreign_key, min(ri_depth) as ri_depth
  from (select parent, parent_key, child, foreign_key, level as ri_depth
          from (select parent_table.table_name parent,
                       parent_constraint.constraint_name as parent_key,
                       child_table.table_name child,
                       child_constraint.constraint_name as foreign_key
                  from all_tables      parent_table
                       inner join all_constraints parent_constraint
                          on parent_table.table_name = parent_constraint.table_name
                       inner join all_constraints child_constraint
                          on child_constraint.r_constraint_name = parent_constraint.constraint_name
                       inner join all_tables      child_table
                          on child_table.table_name = child_constraint.table_name
                 where parent_constraint.constraint_type IN( 'P', 'U' )
                   and child_constraint.constraint_type   = 'R'
                   and child_table.table_name != parent_table.table_name
               )
         start with parent = 'POLYGON'
        connect by nocycle prior child = parent
        )
 group by parent, child, parent_key, foreign_key
)
select ri_tables.parent as source_table,
       pc.column_name as source_key_column,
       ri_tables.child as target_table,
       cc.column_name as target_key_column,
       pc.position as position
  from ri_tables
       inner join all_cons_columns pc
          on ri_tables.parent      = pc.table_name
         and ri_tables.parent_key  = pc.constraint_name
       inner join all_cons_columns cc
          on ri_tables.child       = cc.table_name
         and ri_tables.foreign_key = cc.constraint_name
         and pc.position           = cc.position
 order by ri_tables.ri_depth, ri_tables.parent, ri_tables.child,
          pc.position;

This walks the foreign key hierarchy, listing all of the table dependencies in order, showing what how the foreign key column links to the parent.

There isn't a trivial way to build the insert statements you want without trawling through user_tab_columns for each table you're copying data across. The SQL above at least shows which columns you don't need to copy but instead need to use a sequence value.

Of course, if you were to have any multi-column primary or foreign key, any code depending on the SQL above would completely break.

I think it's time to consider whether you NEED to duplicate all this data versus you WANT to duplicate all of this data. Having duplicative data in relational databases is generally considered A Bad Idea.

Adam Musch
  • 13,286
  • 2
  • 28
  • 32
  • Thanks for your help but i couldn't get it work, i asume you forgot parenthesis around ('P', 'U'). But for variables why do i get : SQL Error : ORA-01745: invalid host/bind variable name – kwenji Mar 09 '12 at 11:37
  • Because that's a bind variable - it's designed for you to store your table name in. – Adam Musch Mar 09 '12 at 15:47
0

Giving the name of a table i were finally able to retrieve the children table and the Primarary/Foreign Key linking them together

SELECT   pk.table_name AS "Base Table", fk.table_name AS "Found Table", pc.column_name AS "Linking Key"
  FROM all_constraints pk, all_constraints fk, all_cons_columns pc, all_cons_columns fc
    WHERE pk.owner = pc.owner AND fk.owner = fc.owner 
    AND pk.constraint_name = pc.constraint_name AND fk.constraint_name = fc.constraint_name
    AND fk.r_owner = pk.owner 
    AND fk.r_constraint_name = pk.constraint_name
    AND pc.position = fc.position
    AND pk.constraint_type IN ('P', 'U')
    AND fk.constraint_type = 'R' 
    AND pk.owner = 'USER'
    AND pk.table_name = 'FRONTFACES';

The result for my test base is :

  • Base Table : FRONTFACES
  • Found Table : LINESGROUPS
  • Linking Key : IDFACE

But the execution time is very slow (8 sec or so), if anyone can help to make it faster...

kwenji
  • 68
  • 1
  • 7
0

Well, i really tried to automaticaly get Foreign key but it sems that searching in all_constraints is not really efficient for a production base (8 - 10 sec query on my server). So i wrote down a procedure listing all Table/Fields to copy with cursors to get multiple rows and their children :

CREATE OR REPLACE PROCEDURE DUPLICATEFACE_PRO 
(
  POLYGONID IN NUMBER DEFAULT 1 
) AS 
-- Declare variables to hold values from table columns
-- Table FrontFaces
FrontFaces_Old_idFace FrontFaces.idFace%TYPE;
FrontFaces_New_idFace FrontFaces.idFace%TYPE;
FrontFaces_FaceValue  FrontFaces.FaceValue%TYPE;
-- Table LinesGroups
LinesGroups_Old_idLinesGroup LinesGroups.idLinesGroup%TYPE;
LinesGroups_New_idLinesGroup LinesGroups.idLinesGroup%TYPE;
LinesGroups_LinesGroupValue LinesGroups.LinesGroupValue%TYPE;
-- Table Lines
Lines_Old_idLine Lines.idLine%TYPE;
Lines_New_idLine Lines.idLine%TYPE;
Lines_LineValue Lines.LineValue%TYPE;
-- Table Points
Points_Old_idPoint Points.idPoint%TYPE;
Points_New_idPoint Points.idPoint%TYPE;
Points_PointValue Points.PointValue%TYPE;

-- Cursor to fill FrontFaces
-- Select each Face and Value Referencing POLYGONID from FrontFaces
CURSOR C1 IS select a.facevalue, a.idface
  from frontfaces a
  where a.idpolygon = POLYGONID
  AND ROWNUM = 1;

-- Cursor to fill LinesGroups
-- Select each LinesGroup and Value Referencing Old_idFace from LinesGroups
CURSOR C2 IS select a.LinesGroupValue, a.idLinesGroup
      from LinesGroups a
      where a.idFace = frontfaces_old_idface;

-- Cursor to fill Lines
-- Select each Line and Value Referencing Old_idLinesGroup from Lines
CURSOR C3 IS select a.LineValue, a.idLine
      from Lines a
      where a.idLinesGroup = linesgroups_old_idlinesgroup;

-- Cursor to fill Points
-- Select each Point and Value Referencing Old_idLine from Points
CURSOR C4 IS select a.PointValue, a.idPoint
      from Points a
      where a.idLine = lines_old_idline;

BEGIN
  OPEN C1;
  LOOP
  -- Put Select results in variables
  FETCH C1 INTO FrontFaces_FaceValue, FrontFaces_Old_idFace;
  EXIT WHEN C1%NOTFOUND;
  -- Put the next free id in a variable
  FrontFaces_New_idFace := frontfaces_seq.nextval;
  -- Create a new row with same FaceValue and Reference to POLYGONID
  INSERT INTO FrontFaces(idFace, idPolygon, FaceValue)
    VALUES(FrontFaces_New_idFace, POLYGONID, FrontFaces_FaceValue);
  dbms_output.put_line('New row with FaceValue = ' || FrontFaces_FaceValue || '  & New_Id ' || FrontFaces_New_idFace || '  & POLYGONID ' || POLYGONID);

  -- Start another Cursor to fill LinesGroups
      OPEN C2;
      LOOP
      -- Put Select results in variables
      FETCH C2 INTO LinesGroups_LinesGroupValue, LinesGroups_Old_idLinesGroup;
      EXIT WHEN C2%NOTFOUND;
      -- Put the next free id in a variable
      LinesGroups_New_idLinesGroup := linesgroups_seq.nextval;
      -- Create a new row with same LinesGroupValue and Reference to New_idFace
      INSERT INTO LinesGroups(idLinesGroup, idFace, LinesGroupValue)
        VALUES(LinesGroups_New_idLinesGroup, FrontFaces_New_idFace, LinesGroups_LinesGroupValue);
      dbms_output.put_line('New row with LinesGroupValue = ' || LinesGroups_LinesGroupValue || '  & New_Id ' || LinesGroups_New_idLinesGroup || '  & New_idFace ' || FrontFaces_New_idFace);

        -- Start another Cursor to fill Lines
        OPEN C3;
          LOOP
          -- Put Select results in variables
          FETCH C3 INTO Lines_LineValue, Lines_Old_idLine;
          EXIT WHEN C3%NOTFOUND;
          -- Put the next free id in a variable
          Lines_New_idLine := lines_seq.nextval;
          -- Create a new row with same LineValue and Reference to New_idLinesGroup
          INSERT INTO Lines(idLine, idLinesGroup, LineValue)
            VALUES(Lines_New_idLine, LinesGroups_New_idLinesGroup, Lines_LineValue);
          dbms_output.put_line('New row with LineValue = ' || Lines_LineValue || '  & New_Id ' || Lines_New_idLine || '  & New_idLinesGroup ' || LinesGroups_New_idLinesGroup);

          -- Start another Cursor to fill Lines
            OPEN C4;
              LOOP
              -- Put Select results in variables
              FETCH C4 INTO Points_PointValue, Points_Old_idPoint;
              EXIT WHEN C4%NOTFOUND;
              -- Put the next free id in a variable
              Points_New_idPoint := points_seq.nextval;
              -- Create a new row with same LineValue and Reference to New_idLinesGroup
              INSERT INTO Points(idPoint, idLine, PointValue)
                VALUES(Points_New_idPoint, Lines_New_idLine, Points_PointValue);
              dbms_output.put_line('New row with PointValue = ' || points_pointvalue || '  & New_Id ' || Points_New_idPoint || '  & New_idLine ' || Lines_New_idLine);
              END LOOP;
            CLOSE C4;

          END LOOP;
        CLOSE C3;

      END LOOP;
    CLOSE C2;

  END LOOP;
CLOSE C1;
END DUPLICATEFACE_PRO;
kwenji
  • 68
  • 1
  • 7