Why is oracle spewing bad table metadata? - oracle

I'm using DBVisualizer to extract DDL from an Oracle 10.2 DB. I'm getting odd instances of repeated columns in constraints, or repeated constraints in the generated DDL. At first I chalked it up to a bug in DBVisualizer, but I tried using Apache DDLUtils against the DB and it started throwing errors which investigation revealed to be caused by the same problem. The table metadata being returned by Oracle appears to have multiple entries for some FK constraints.
I can find no reference to this sort of thing from my google searches and I was wondering if anyone else had seen the same thing. Is this a bug in the Oracle driver, or does the metadata contain extra information which is being dropped when my tools access it, resulting in confusion on the part of the tools...
Here is an example (truncated) DDL output from
CREATE TABLE ARTIST
(
ID INTEGER NOT NULL,
FIRST_NAME VARCHAR2( 128 ),
LAST_NAME VARCHAR2( 128 ),
CONSTRAINT ARTIST_ID_PK PRIMARY KEY( ID ),
CONSTRAINT ARTIST_CONTENT_ID_FK FOREIGN KEY( ID, ID, ID ) REFERENCES CMS_CONTENT( CONTENT_ID, CONTENT_ID, CONTENT_ID )
-- note the multiple instances of ID and CONTENT_ID in the above line
-- rest assured there is nothing bizarre about the foreign table CMS_CONTENT
)
I'm attempting to find a Java example which can show the behaviour, and will update the question when I have a concrete example.

You can try the built-in Oracle DBMS_METADATA.GET_DDL('TABLE','ARTIST') and see if that resolves the issue (ie whether it is a bug in the tools or the DB).
You can look at the data_dictionary tables too. In this case, ALL_CONSTRAINTS and ALL_CONS_COLUMNS.
select ac.owner, ac.constraint_name, ac.table_name, ac.r_owner, ac.r_constraint_name,
acc.column_name, acc.position
from all_constraints ac join all_cons_columns acc on
(ac.owner = acc.owner and ac.constraint_name = acc.constraint_name)
where ac.table_name = 'ARTIST'
and ac.constraint_type = 'R'
I'd suspect that it is a bug in the tools, and they've missed a join on the owning schema and you are picking up the same table/constraint but in another user's schema.

As far as I can see, dbvis (6.5.7) uses own code when you use the 'DDL' tab and it uses dbms_metadata when using the tab 'DDL with Storage'.
Does this make a difference for you ?
Ronald

Related

Does PostgreSQL have a pseudo-column like "LEVEL" in Oracle?

Does PostgreSQL have a pseudo-column like "LEVEL" in Oracle?
If not, then how can we create a column similar to "LEVEL"?
Postgres does not have hierarchical queries. No CONNECT BY, therefore also no LEVEL.
The additional module tablefunc provides the function connectby() doing almost the same. See
mthorley answer for details.
Or you can use a standard recursive CTE with a level column that's incremented with every recursion.
This query in Oracle:
SELECT employee_id, last_name, manager_id, LEVEL
FROM employees
CONNECT BY PRIOR employee_id = manager_id;
.. can be translated to this recursive CTE in Postgres:
WITH RECURSIVE cte AS (
SELECT employee_id, last_name, manager_id, 1 AS level
FROM employees
UNION ALL
SELECT e.employee_id, e.last_name, e.manager_id, c.level + 1
FROM cte c
JOIN employees e ON e.manager_id = c.employee_id
)
SELECT *
FROM cte;
Yes, Postgres has support for "LEVEL" like Oracle.
But, as the other answers point out, you must have the tablefunc extension loaded.
If you have admin access to your Postgres database you can load it with this:
CREATE EXTENSION IF NOT EXISTS tablefunc;
For additional info check the docs
https://www.postgresql.org/docs/current/static/tablefunc.html
Here's a real life example of connectby from one of our apps. We are using it to find all the people who report up to a manager through their reporting tree.
SELECT system_user.system_user_id
, system_user.first_name
, system_user.last_name
, team.mgr_id as managers_system_user_id
, team.level
, team.hierarchy
FROM connectby('system_user_manager_rltnp'
, 'system_user_id'
, 'system_users_managers_id'
, 2963049 -- the users system_user_id
, 5 -- the max levels of depth
, '~') -- the hierarchy delimiter
AS team(rpt_id numeric, mgr_id numeric, level int, hierarchy text),
system_user
WHERE team.rpt_id = system_user.system_user_id
And it returns results like this. Here you can see the level, and also the whole hierarchy as a string.
"system_user_id","first_name","last_name","managers_system_user_id","level","hierarchy"
"2963049","Debbie","Buswell","",0,"2963049"
"2963045","Linda","Simply","2963049",1,"2963049~2963045"
"2963047","Cindy","Brouillard","2963049",1,"2963049~2963047"
"2963048","Sharon","Burns","2963049",1,"2963049~2963048"
"2963050","Marie-Eve","Casper","2963049",1,"2963049~2963050"
"2963051","Tammy","Cody","2963049",1,"2963049~2963051"
The functionality using a Connect By, Starts With, and a level indicator that you are familiar with is available if you enable the tablefunc extension in postgres. The syntax is slightly different, but if you understand connect by from oracle you will pick this up in about 90 seconds. It is great and saved my bacon when I transformed an oracle system into a postgres system.
I gave all the detail to a similar question.
Stackoverflow Connect By answer

Oracle COLUMN_ID implicit index creation

How does Oracle use COLUMN_ID as found in USER_TAB_COLUMNS view? I just need to confirm that it does not use this internal column ordering while creating implicit indexes - such as when a primary key is enforced or a unique key constraint is created (that is key/constraints columns provided are used in the same order - left to right and not these internal column ordering). (if possible please point me in the direction of Oracle documentation.). Thanks in advance.
It'll be hard to find something stating that it doesn't do something, but there isn't anything stating that it will use column_id to override the index creation.
You can see all the reference to column_id in the documentation here; the only one that seems matter is the all_tab_columns view.
You can verify the order of the columns as used in the index by querying the all_ind_columns view, where you will be able to see that there is no enforced relationship between its column_position - which comes from the order the columns are listed in the index creation command - and column_id.
If you are specifically interested in checking indexes that back up constraints, you can do something like:
select ac.owner, ac.table_name, ac.constraint_name, ac.index_owner,
ac.index_name, aic.column_position, aic.column_name
from all_constraints ac
join all_ind_columns aic on aic.index_owner = coalesce(ac.index_owner, ac.owner)
and aic.index_name = ac.index_name
order by 1, 2, 3, 6;
... adding filters for owner or table as needed.

DB2 duplicate key error when inserting, BUT working after select count(*)

I have a - for me unknown - issue and I don't know what's the logic/cause behind it. When I try to insert a record in a table I get a DB2 error saying:
[SQL0803] Duplicate key value specified: A unique index or unique constraint *N in *N
exists over one or more columns of table TABLEXXX in SCHEMAYYY. The operation cannot
be performed because one or more values would have produced a duplicate key in
the unique index or constraint.
Which is a quite clear message to me. But actually there would be no duplicate key if I inserted my new record seeing what records are already in there. When I do a SELECT COUNT(*) from SCHEMAYYY.TABLEXXX and then try to insert the record it works flawlessly.
How can it be that when performing the SELECT COUNT(*) I can suddenly insert the records? Is there some sort of index associated with it which might give issues because it is out of sync? I didn't design the data model, so I don't have deep knowledge of the system yet.
The original DB2 SQL is:
-- Generate SQL
-- Version: V6R1M0 080215
-- Generated on: 19/12/12 10:28:39
-- Relational Database: S656C89D
-- Standards Option: DB2 for i
CREATE TABLE TZVDB.PRODUCTCOSTS (
ID INTEGER GENERATED BY DEFAULT AS IDENTITY (
START WITH 1 INCREMENT BY 1
MINVALUE 1 MAXVALUE 2147483647
NO CYCLE NO ORDER
CACHE 20 )
,
PRODUCT_ID INTEGER DEFAULT NULL ,
STARTPRICE DECIMAL(7, 2) DEFAULT NULL ,
FROMDATE TIMESTAMP DEFAULT NULL ,
TILLDATE TIMESTAMP DEFAULT NULL ,
CONSTRAINT TZVDB.PRODUCTCOSTS_PK PRIMARY KEY( ID ) ) ;
ALTER TABLE TZVDB.PRODUCTCOSTS
ADD CONSTRAINT TZVDB.PRODCSTS_PRDCT_FK
FOREIGN KEY( PRODUCT_ID )
REFERENCES TZVDB.PRODUCT ( ID )
ON DELETE RESTRICT
ON UPDATE NO ACTION;
I'd like to see the statements...but since this question is a year old...I won't old my breath.
I'm thinking the problem may be the
GENERATED BY DEFAULT
And instead of passing NULL for the identity column, you're accidentally passing zero or some other duplicate value the first time around.
Either always pass NULL, pass a non-duplicate value or switch to GENERATED ALWAYS
Look at preceding messages in the joblog for specifics as to what caused this. I don't understand how the INSERT can suddenly work after the COUNT(*). Please let us know what you find.
Since it shows *N (ie n/a) as the name of the index or constraing, this suggests to me that is is not a standard DB2 object, and therefore may be a "logical file" [LF] defined with DDS rather than SQL, with a key structure different than what you were doing your COUNT(*) on.
Your shop may have better tools do view keys on dependent files, but the method below will work anywhere.
If your table might not be the actual "physical file", check this using Display File Description, DSPFD TZVDB.PRODUCTCOSTS, in a 5250 ("green screen") session.
Use the Display Database Relations command, DSPDBR TZVDB.PRODUCTCOSTS, to find what files are defined over your table. You can then DSPFD on each of these files to see the definition of the index key. Also check there that each of these indexes is maintained *IMMED, rather than *REBUILD or *DELAY. (A wild longshot guess as to a remotely possible cause of your strange anomaly.)
You will find the DB2 for i message finder here in the IBM i 7.1 Information Center or other releases
Is it a paging issue? we seem to get -0803 on inserts occasionally when a row is being held for update and it locks a page that probably contains the index that is needed for the insert? This is only a guess but it appears to me that is what is happening.
I know it is an old topic, but this is what Google shown me on the first place.
I had the same issue yesterday, causing me a lot of headache. I did the same as above, checked the table definitions, keys, existing items...
Then I found out the problem was with my INSERT statement. It was trying to insert to identical records at once, but as the constraint prevented the commit, I could not find anything in the database.
Advice: review your INSERT statement carefully! :)

Trace a path between Oracle tables?

Can I use Oracle sys tables to trace a path between two table, all the possibilities to go from X table to Y table.
The problem is:
I work on an enormous database, where it's really difficult to know rapidly, which tables are vital to make a join between two tables.
Can I do this?
First Need:
The problem with SQL Developer Data Modeler and the other tools, is the fact to have to select tables to rev_eng (So I should already know the tables to select) but for me, this is the major problem. In my case I have 800 tables and I can't select them all to trace the path. My desire is to submit as arguments two tables and then generate all the possible paths.
Second Need :
I have already try to query sys.all_constraints and the max I've done, is to detect the tables directly connected to a table X.
The query:
SELECT C1.TABLE_NAME,C2.TABLE_NAME
FROM ALL_CONSTRAINTS C1, ALL_CONSTRAINTS C2
WHERE C2.CONSTRAINT_NAME = C1.R_CONSTRAINT_NAME
AND UPPER(C1.OWNER) LIKE '**MY_SCHEMA**'
AND C1.CONSTRAINT_TYPE='R'
AND UPPER(C1.TABLE_NAME) LIKE '**X**'
ORDER BY C1.TABLE_NAME
So if somebody can help me to conceive at least the query to have this result:
Table1 | Table2 | JoinCollumnofTable1 | JoinCollumnofTable2
To have that, I surmise the other table to join to ALL_CONSTRAINTS is ALL_CON_COLUMNS
But the problem I've found is the composite primary_keys.
This is why Nature gave us data models: to assist in tasks like this.
If you don't have a data model then you can reverse engineer one from the data dictionary. See my answer to a question on reverse engineering.
Reverse engineering can only identify relationships which have been defined by foreign keys. This shouldn't need stating but let's say it anyway: if your database hasn't got constraints you have no chance of deriving a data model automatically.
"I have 800 tables and I can't select them all to trace the path. "
Hmmm, I suppose recommending you reverse engineer a data model is a bit like the punchline to the old joke about how to get to Cork: "Well I wouldn't start from here". The whole point about having a data model upfront is that we have it when when we really need it.
If primary and foreign key relationships are established in the database, you can use a tool like Oracle Developer with Data Modeler to reverse engineer the model and give a graphical representation of what the relationships are.
Tools like this read the Oracle dictionary to determine the relationships between tables. You can do this yourself by querying views such as sys.all_constraints.
I cobbled the following query together using Tim Hall's Generic Function Using a Ref Cursor, since I only have 10g here (you can use 11g's LISTAGG function if you've got 11g). It should get you close.
SELECT ac1.table_name "Table", ac2.table_name "Referencing Table"
, concatenate_list(CURSOR(SELECT acc.column_name
FROM all_cons_columns acc
WHERE acc.constraint_name = ac1.constraint_name
AND acc.owner = 'the_owner'
ORDER BY position)) "PK Columns"
, concatenate_list(CURSOR(SELECT acc.column_name
FROM all_cons_columns acc
WHERE acc.constraint_name = ac2.constraint_name
AND acc.owner = 'the_owner'
ORDER BY position)) "FK Columns"
FROM all_constraints ac1 JOIN all_constraints ac2
ON ac1.constraint_name = ac2.r_constraint_name
WHERE ac1.table_name = 'your_table'
AND ac1.owner = 'the_owner'
AND ac2.owner = 'the_owner'
AND ac1.constraint_type = 'P';
Also try schemaspy - an open source free alternative which uses the foreign keys to generate a relationship model!

How do I get all indices on a Table in Oracle via JDBC, even if they are owned by different users?

Yes, I know about DatabaseMetadata.getIndexInfo, but it doesn't seem to do what I want.
I've got two users/schemas, let's call them A and B.
There's a table in A called TAB. The user B created an index on A.TAB, let's call that index IND.
The information that I want is: what indices are there on the table TAB in the schema A (a.k.a with the owner A). I don't care about the owner of the indices, just that they are on that specific table.
Experimenting with getIndexInfo I found out the following things:
the first argument catalog seems to be entirely ignored by the Oracle JDBC driver.
the second argument schema restricts which table statistics is returned and the owner of the index
unique and approximate do (roughly) what they should (except that giving approximate=false will actually execute an update statistics statement).
Having traced the SQL the JDBC driver executes on getIndexInfo(null, "A", "TAB", false, true), I got this:
select null as table_cat,
owner as table_schem,
table_name,
0 as NON_UNIQUE,
null as index_qualifier,
null as index_name, 0 as type,
0 as ordinal_position, null as column_name,
null as asc_or_desc,
num_rows as cardinality,
blocks as pages,
null as filter_condition
from all_tables
where table_name = 'TAB'
and owner = 'A'
union
select null as table_cat,
i.owner as table_schem,
i.table_name,
decode (i.uniqueness, 'UNIQUE', 0, 1),
null as index_qualifier,
i.index_name,
1 as type,
c.column_position as ordinal_position,
c.column_name,
null as asc_or_desc,
i.distinct_keys as cardinality,
i.leaf_blocks as pages,
null as filter_condition
from all_indexes i, all_ind_columns c
where i.table_name = 'TAB'
and i.owner = 'A'
and i.index_name = c.index_name
and i.table_owner = c.table_owner
and i.table_name = c.table_name
and i.owner = c.index_owner
order by non_unique, type, index_name, ordinal_position
As you can see both table_name and i.owner are restricted to being TAB. This means that this query will only return index information that is owned by the same user as the table.
I can think of three possible workarounds:
always create the index and the table in the same schema (i.e. let them have the same owner). Unfortunately that's not always an option.
Query with schema set to null. This would get ugly as soon as two schemata contained the same table name (because there's no way to find out on which table (i.e. which table owner) a given schema is).
execute that SQL directly (using executeQuery()). I'd rather not fall down to this level, unless it's absolutely unavoidable.
None of those workarounds look particularly pleasing to me, but if nothing else works, I might have to fall back to direct SQL execution.
Both the Database and the JDBC Driver are at 11.2.0.2.0.
So basically my questions are:
Is this a bug in the JDBC driver, or is there some logic behind it that I'm unaware of?
Is there a simple and reasonably portable way to get Oracle to give me the information that I need?
I suggest you query directly the Oracle dictionary tables, BUT start with:
select * from dba_indexes
Using that view it should be almost trivial to get the info you need.
However, accesing the dba_ tables and views requires the user to have special privileges, but since you don't want to give DBA privilege to everyone, you can just:
grant select any dictionary to username
connected as system or sys so the selected user can query the dictionary.
Just in case you want to explore Oracle's dictionary, try:
select * from dict
Best regards.
always create the index and the table in the same schema (i.e. let them have the same owner). Unfortunately that's not always an option.
That would be my preferred way of doing it.
Query with schema set to null. This would get ugly as soon as two schemata contained the same table name (because there's no way to find out on which table (i.e. which table owner) a given schema is)
Of course you can find that out, because the result set returned by getIndexInfo() does contain the correct schema for each table. But you can't find out in which schema the index is.
execute that SQL directly
I would actually use a modified version of that query that also returns the schema for each index to alleviate the identification of the index.
But again: I would also create the index and the table in the same schema.

Resources