cancel
Showing results for 
Search instead for 
Did you mean: 

Determine Entity Order In Schema

JimDiaz
Participant
0 Kudos
1,576

I needed to determine the order of operation on tables within a Sybase 12 database and couldn't find an easy solution. I came up with the following procedures and thought I might send them our for review / comment. Please feel free to use them if they prove helpful.

Jim

-- James J. Diaz
-- 2016.05.30

CREATE OR REPLACE VIEW DBA.ViewForeignKeys
  AS
  -- Include all tables without a foreign key
  SELECT Entities.Table_Id                    AS PrimaryTableId,
         Entities.Object_Id                   AS PrimaryObjectId,
         Owners.user_name                     AS PrimaryOwner,
         Entities.table_name                  AS PrimaryTableName,
         NULL                                 AS ForeignTableId,
         NULL                                 AS ForeignObjectId,
         NULL                                 AS ForeignOwner,
         NULL                                 AS ForeignTableName,
         'Bogus'                              AS LastColumn
    FROM SYS.SYSTAB     AS Entities
    JOIN SYS.SYSUSER    AS Owners     ON ( Owners.user_id   = Entities.creator  )
   WHERE table_type = 1
     AND PrimaryTableId NOT IN ( SELECT table_id FROM SYS.SYSIDX AS Indexes WHERE index_category = 2 )
  UNION ALL
  SELECT PrimaryTables.Table_Id   AS PrimaryTableId,
         PrimaryTables.Object_Id  AS PrimaryObjectId,
         PrimaryOwners.user_name  AS PrimaryOwner,
         PrimaryTables.table_name AS PrimaryTableName,
         ForeignTables.Table_Id   AS ForeignTableId,
         ForeignTables.Object_Id  AS ForeignObjectId,
         ForeignOwners.user_name  AS ForeignOwner,
         ForeignTables.table_name AS ForeignTableName,
         'Bogus'                  AS LastColumn
    FROM SYS.SYSFKEY AS ForeignKeys
    JOIN SYS.SYSIDX  AS PrimaryIndexes  ON ( PrimaryIndexes.table_id  = ForeignKeys.primary_table_id
                                        AND  PrimaryIndexes.index_id  = ForeignKeys.primary_index_id )
    JOIN SYS.SYSTAB  AS PrimaryTables   ON ( PrimaryIndexes.table_id  = PrimaryTables.table_id )
    JOIN SYS.SYSUSER AS PrimaryOwners   ON ( PrimaryOwners.user_id    = PrimaryTables.creator  )
    JOIN SYS.SYSIDX  AS ForeignIndexes  ON ( ForeignIndexes.table_id  = ForeignKeys.foreign_table_id
                                        AND  ForeignIndexes.index_id  = ForeignKeys.foreign_index_id )
    JOIN SYS.SYSTAB  AS ForeignTables   ON ( ForeignIndexes.table_id  = ForeignTables.table_id )
    JOIN SYS.SYSUSER AS ForeignOwners   ON ( ForeignOwners.user_id    = ForeignTables.creator  );

CREATE OR REPLACE PROCEDURE DBA.EntityOrder()
RESULT(
  EntityId        INTEGER,
  EntityOwner     CHAR(128),
  EntityName      CHAR(128),
  EntityOrder     INTEGER,
  ReferenceLevel  INTEGER
)
BEGIN
  DECLARE @EntityOrder    INTEGER;
  DECLARE @ReferenceLevel INTEGER;
  DECLARE @LastCount      INTEGER;
  DECLARE @ThisCount      INTEGER;

  DECLARE LOCAL TEMPORARY TABLE @Results (
    EntityId        INTEGER,
    EntityOwner     CHAR(128),
    EntityName      CHAR(128),
    EntityOrder     INTEGER,
    ReferenceLevel  INTEGER,
  ) NOT TRANSACTIONAL;

  SET @EntityOrder    = 0;
  SET @ReferenceLevel = 0;

  FOR EachPrimaryTable AS PrimaryTableCursor INSENSITIVE CURSOR FOR
    SELECT DISTINCT
           PrimaryTableId   AS @PrimaryTableId,
           PrimaryOwner     AS @PrimaryOwner,
           PrimaryTableName AS @PrimaryTableName
      FROM DBA.ViewForeignKeys
     WHERE @PrimaryOwner NOT IN ( 'DBO', 'SYS', 'rs_systabgroup')
       AND ForeignTableId IS NULL
  DO
    SET @EntityOrder = @EntityOrder + 1;
    INSERT INTO @Results( EntityId, EntityOwner, EntityName, EntityOrder, ReferenceLevel ) VALUES ( @PrimaryTableId, @PrimaryOwner, @PrimaryTableName, @EntityOrder, @ReferenceLevel );
  END FOR;

  SET @LastCount = 0;
  SELECT COUNT(*) INTO @ThisCount FROM @Results;

  WHILE @ThisCount > @LastCount LOOP
    SET @ReferenceLevel = @ReferenceLevel + 1;
    SET @LastCount = @ThisCount;
    FOR EachResult AS ResultCursor INSENSITIVE CURSOR FOR
      SELECT DISTINCT
             ForeignTableId   AS @ForeignTableId,
             ForeignOwner     AS @ForeignOwner,
             ForeignTableName AS @ForeignTableName
        FROM DBA.ViewForeignKeys
       WHERE @ForeignOwner NOT IN ( 'DBO', 'SYS', 'rs_systabgroup')
         AND PrimaryTableId IN ( SELECT EntityId FROM @Results )
         AND ForeignTableId NOT IN  ( SELECT EntityId FROM @Results )
    DO
      -- don't include those in current result set
      IF @ForeignTableId NOT IN (
        SELECT PrimaryTableId
          FROM DBA.ViewForeignKeys
         WHERE @ForeignOwner NOT IN ( 'DBO', 'SYS', 'rs_systabgroup')
           AND PrimaryTableId IN ( SELECT EntityId FROM @Results )
           AND ForeignTableId NOT IN ( SELECT EntityId FROM @Results )
           AND ForeignTableId <> @ForeignTableId )
      THEN
        SET @EntityOrder = @EntityOrder + 1;
        INSERT INTO @Results( EntityId, EntityOwner, EntityName, EntityOrder, ReferenceLevel ) VALUES ( @ForeignTableId, @ForeignOwner, @ForeignTableName, @EntityOrder, @ReferenceLevel );
      END IF;
    END FOR;
    SELECT COUNT(*) INTO @ThisCount FROM @Results;
  END LOOP;

  SELECT EntityId, EntityOwner, EntityName, EntityOrder, ReferenceLevel FROM @Results ORDER BY EntityOrder;

END;

CREATE OR REPLACE PROCEDURE DBA.DeleteOrder(
  IN @TableOwner        CHAR(128),
  IN @TableName         CHAR(128),
  IN @AttributeName     CHAR(128) DEFAULT NULL,
  IN @AttributeValue    CHAR(128) DEFAULT NULL,
  IN @QuotedIdentifier  BIT DEFAULT 0
  )
RESULT(
  EntityId        INTEGER,
  EntityOwner     CHAR(128),
  EntityName      CHAR(128),
  EntityOrder     INTEGER,
  ReferenceLevel  INTEGER,
  SQLStatement    CHAR(2048)
)
BEGIN
  DECLARE @EntityOrder    INTEGER;
  DECLARE @ReferenceLevel INTEGER;
  DECLARE @LastCount      INTEGER;
  DECLARE @ThisCount      INTEGER;
  DECLARE @SQLStatement   CHAR(2048);

  DECLARE LOCAL TEMPORARY TABLE @Results (
    EntityId        INTEGER,
    EntityOwner     CHAR(128),
    EntityName      CHAR(128),
    EntityOrder     INTEGER,
    ReferenceLevel  INTEGER,
    SQLStatement    CHAR(2048),
  ) NOT TRANSACTIONAL;

  SET @EntityOrder    = 0;
  SET @ReferenceLevel = 0;

  FOR EachPrimaryTable AS PrimaryTableCursor INSENSITIVE CURSOR FOR
    -- Get the tableid of the requested table and make sure
    -- it is foreign keyed to another table
    SELECT DISTINCT
           PrimaryTableId   AS @PrimaryTableId,
           PrimaryOwner     AS @PrimaryOwner,
           PrimaryTableName AS @PrimaryTableName
      FROM DBA.ViewForeignKeys
     WHERE PrimaryOwner     = @TableOwner
       AND PrimaryTableName = @TableName
  DO
    SET @EntityOrder = @EntityOrder + 1;

    SET @SQLStatement = STRING( 'DELETE FROM ', @PrimaryOwner, '.', @PrimaryTableName );
    IF @AttributeName IS NOT NULL THEN
      SET @SQLStatement = STRING( @SQLStatement, ' WHERE ', @AttributeName, ' = ');
      IF @AttributeValue IS NOT NULL THEN
        IF @QuotedIdentifier = 1 THEN
          SET @SQLStatement = STRING( @SQLStatement, '''', @AttributeValue, '''');
        ELSE
          SET @SQLStatement = STRING( @SQLStatement, @AttributeValue );
        END IF;
      END IF;
    END IF;
    SET @SQLStatement = STRING( @SQLStatement, '; COMMIT WORK;');

    INSERT INTO @Results( EntityId, EntityOwner, EntityName, EntityOrder, ReferenceLevel, SQLStatement ) VALUES ( @PrimaryTableId, @PrimaryOwner, @PrimaryTableName, @EntityOrder, @ReferenceLevel, @SQLStatement );
  END FOR;

  SET @LastCount = 0;
  SELECT COUNT(*) INTO @ThisCount FROM @Results;

  -- Table is not referenced may not actually exist
  IF @ThisCount = 0 THEN

    SET @SQLStatement = STRING( 'DELETE FROM ', @TableOwner, '.', @TableName );
    IF @AttributeName IS NOT NULL THEN
      SET @SQLStatement = STRING( @SQLStatement, ' WHERE ', @AttributeName, ' = ');
      IF @AttributeValue IS NOT NULL THEN
        IF @QuotedIdentifier = 1 THEN
          SET @SQLStatement = STRING( @SQLStatement, '''', @AttributeValue, '''');
        ELSE
          SET @SQLStatement = STRING( @SQLStatement, @AttributeValue );
        END IF;
      END IF;
    END IF;
    SET @SQLStatement = STRING( @SQLStatement, '; COMMIT WORK;');

    INSERT INTO @Results( EntityId, EntityOwner, EntityName, EntityOrder, ReferenceLevel, SQLStatement ) VALUES ( -1, @TableOwner, @TableName, @EntityOrder, @ReferenceLevel, @SQLStatement );

  ELSE
    WHILE @ThisCount > @LastCount LOOP
      SET @ReferenceLevel = @ReferenceLevel + 1;
      SET @LastCount = @ThisCount;
      FOR EachResult AS ResultCursor INSENSITIVE CURSOR FOR
        SELECT DISTINCT
               ForeignTableId   AS @ForeignTableId,
               ForeignOwner     AS @ForeignOwner,
               ForeignTableName AS @ForeignTableName
          FROM DBA.ViewForeignKeys
         WHERE PrimaryTableId IN ( SELECT EntityId FROM @Results )
           AND ForeignTableId NOT IN ( SELECT EntityId FROM @Results )
      DO
        -- don't include those in current result set
        IF @ForeignTableId NOT IN (
          SELECT PrimaryTableId
            FROM DBA.ViewForeignKeys
           WHERE PrimaryTableId IN ( SELECT EntityId FROM @Results )
             AND ForeignTableId NOT IN ( SELECT EntityId FROM @Results )
             AND ForeignTableId <> @ForeignTableId )
        THEN

          SET @EntityOrder = @EntityOrder + 1;

          SET @SQLStatement = STRING( 'DELETE FROM ', @ForeignOwner, '.', @ForeignTableName );
          IF @AttributeName IS NOT NULL THEN
            SET @SQLStatement = STRING( @SQLStatement, ' WHERE ', @AttributeName, ' = ');
            IF @AttributeValue IS NOT NULL THEN
              IF @QuotedIdentifier = 1 THEN
                SET @SQLStatement = STRING( @SQLStatement, '''', @AttributeValue, '''');
              ELSE
                SET @SQLStatement = STRING( @SQLStatement, @AttributeValue );
              END IF;
            END IF;
          END IF;
          SET @SQLStatement = STRING( @SQLStatement, '; COMMIT WORK;');

          INSERT INTO @Results( EntityId, EntityOwner, EntityName, EntityOrder, ReferenceLevel, SQLStatement ) VALUES ( @ForeignTableId, @ForeignOwner, @ForeignTableName, @EntityOrder, @ReferenceLevel, @SQLStatement );
        END IF;
      END FOR;
      SELECT COUNT(*) INTO @ThisCount FROM @Results;
    END LOOP;
  END IF;

  SELECT EntityId, EntityOwner, EntityName, EntityOrder, ReferenceLevel, SQLStatement FROM @Results ORDER BY EntityOrder DESC;

END;

CREATE OR REPLACE PROCEDURE DBA.DeleteOrderOwner(
  IN @TableOwner CHAR(128)
  )
RESULT(
  EntityId        INTEGER,
  EntityOwner     CHAR(128),
  EntityName      CHAR(128),
  EntityOrder     INTEGER,
  ReferenceLevel  INTEGER,
  SQLStatement    CHAR(2048)
)
BEGIN
  DECLARE @EntityOrder    INTEGER;
  DECLARE @ReferenceLevel INTEGER;
  DECLARE @LastCount      INTEGER;
  DECLARE @ThisCount      INTEGER;
  DECLARE @SQLStatement   CHAR(2048);

  DECLARE LOCAL TEMPORARY TABLE @Results (
    EntityId        INTEGER,
    EntityOwner     CHAR(128),
    EntityName      CHAR(128),
    EntityOrder     INTEGER,
    ReferenceLevel  INTEGER,
    SQLStatement    CHAR(2048),
  ) NOT TRANSACTIONAL;

  SET @EntityOrder    = 0;
  SET @ReferenceLevel = 0;

  FOR EachPrimaryTable AS PrimaryTableCursor INSENSITIVE CURSOR FOR
    SELECT DISTINCT
           PrimaryTableId   AS @PrimaryTableId,
           PrimaryOwner     AS @PrimaryOwner,
           PrimaryTableName AS @PrimaryTableName
      FROM DBA.ViewForeignKeys
     WHERE @PrimaryOwner    = @TableOwner
       AND ForeignTableId IS NULL
    UNION ALL
    SELECT DISTINCT
           ForeignTableId   AS @PrimaryTableId,
           ForeignOwner     AS @PrimaryOwner,
           ForeignTableName AS @PrimaryTableName
      FROM DBA.ViewForeignKeys
     WHERE ForeignOwner     = @TableOwner
       AND @TableOwner NOT IN ( SELECT PrimaryOwner FROM DBA.ViewForeignKeys WHERE ForeignTableId = @PrimaryTableId )
  DO
    SET @EntityOrder = @EntityOrder + 1;

    SET @SQLStatement = STRING( 'DELETE FROM ', @PrimaryOwner, '.', @PrimaryTableName );
    SET @SQLStatement = STRING( @SQLStatement, '; COMMIT WORK;');

    INSERT INTO @Results( EntityId, EntityOwner, EntityName, EntityOrder, ReferenceLevel, SQLStatement ) VALUES ( @PrimaryTableId, @PrimaryOwner, @PrimaryTableName, @EntityOrder, @ReferenceLevel, @SQLStatement );
  END FOR;

  SET @LastCount = 0;
  SELECT COUNT(*) INTO @ThisCount FROM @Results;


  WHILE @ThisCount > @LastCount LOOP
    SET @ReferenceLevel = @ReferenceLevel + 1;
    SET @LastCount = @ThisCount;

    FOR EachResult AS ResultCursor INSENSITIVE CURSOR FOR

      -- Get all tables with foreign keys that come from results
      SELECT DISTINCT
             ForeignTableId   AS @ForeignTableId,
             ForeignOwner     AS @ForeignOwner,
             ForeignTableName AS @ForeignTableName
        FROM DBA.ViewForeignKeys
       WHERE PrimaryTableId IN ( SELECT EntityId FROM @Results )
         -- don't include those that have foreign keys from tables not already identified
         AND ForeignTableId NOT IN ( SELECT EntityId FROM @Results )
    DO
      -- don't include those in current result set
      IF @ForeignTableId NOT IN (
        SELECT PrimaryTableId
          FROM DBA.ViewForeignKeys
         WHERE PrimaryTableId IN ( SELECT EntityId FROM @Results )
           AND ForeignTableId NOT IN ( SELECT EntityId FROM @Results )
           AND ForeignTableId <> @ForeignTableId )
      THEN
        SET @EntityOrder = @EntityOrder + 1;

        SET @SQLStatement = STRING( 'DELETE FROM ', @ForeignOwner, '.', @ForeignTableName );
        SET @SQLStatement = STRING( @SQLStatement, '; COMMIT WORK;');

        INSERT INTO @Results( EntityId, EntityOwner, EntityName, EntityOrder, ReferenceLevel, SQLStatement ) VALUES ( @ForeignTableId, @ForeignOwner, @ForeignTableName, @EntityOrder, @ReferenceLevel, @SQLStatement );
      END IF;
    END FOR;
    SELECT COUNT(*) INTO @ThisCount FROM @Results;
  END LOOP;

  SELECT EntityId, EntityOwner, EntityName, EntityOrder, ReferenceLevel, SQLStatement FROM @Results ORDER BY EntityOrder DESC;

END;

Accepted Solutions (0)

Answers (0)