Oracle COLUMN_ID implicit index creation - oracle

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.

Related

Oracle - select statement alias one column and wildcard to get all remaining columns

New to SQL. Pardon me if this question is a basic one. Is there a way for me to do this below
SELECT COLUMN1 as CUSTOM_NAME, <wildcard for remaining columns as is> from TABLE;
I only want COLUMN1 appear once in the final result
There is no way to make that kind of dynamic SELECT list with regular SQL*.
This is a good thing. Programming gets more difficult the more dynamic it is. Even the simple * syntax, while useful in many contexts, causes problems in production code. The Oracle SQL grammar is already more complicated than most traditional programming languages, adding a little meta language to describe what the queries return could be a nightmare.
*Well, you could create something using Oracle data cartridge, or DBMS_XMLGEN, or a trick with the PIVOT clause. But each of those solutions would be incredibly complicated and certainly not as simple as just typing the columns.
This is about as close as you will get.
It is very handy for putting the important columns up front,
while being able to scroll to the others if needed. COLUMN1 will end up being there twice.
SELECT COLUMN1 as CUSTOM_NAME,
aliasName.*
FROM TABLE aliasName;
In case you have many columns it might be worth to generate a full column list automatically instead of relying on the * selector.
So a two step approach would be to generate the column list with custom first N columns and unspecified order of the other columns, then use this generated list in your actual select statement.
-- select comma separated column names from table with the first columns being in specified order
select
LISTAGG(column_name, ', ') WITHIN GROUP (
ORDER BY decode(column_name,
'FIRST_COLUMN_NAME', 1,
'SECOND_COLUMN_NAME', 2) asc) "Columns"
from user_tab_columns
where table_name = 'TABLE_NAME';
Replace TABLE_NAME, FIRST_COLUMN_NAME and SECOND_COLUMN_NAME by your actual names, adjust the list of explicit columns as needed.
Then execute the query and use the result, which should look like
FIRST_COLUMN_NAME, SECOND_COLUMN_NAME, OTHER_COLUMN_NAMES
Ofcourse this is overhead for 5-ish columns, but if you ever run into a company database with 3 digit number of columns, this can be interesting.

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!

Is it possible -or recommended- to index an inline query to improve performance?

I am not an expert on TSQL but I wonder if such a thing is possible:
Imagine I have a select that joins to a table which is another query's result set:
SELECT *
FROM tProduct
JOIN (SELECT ProductId FROM ...... -- some other joins) tInlineQuey
ON tInlineQuery.ProductId = tProduct.Id
WHERE tInlineQuery. -- some econdition
Is it possible or meaningful to create an indedx on the tInlineQuery so that to apply a filtering on that result set can perform faster?
If so, how is it possible?
No, you could have suitable indexes on the objects within the subquery, but you can't add an temporary index to the subquery as you have it there. You can query hint the way in which the data is joined, e.g. nested loop, merge or hash join - but the optimizer tends to make the right decision.
An option to get that effect would be to select the results of that subquery into a temp table, and place an index on there, then join to that temp table.
To do this, you would need a stored procedure and to include the following code:
SELECT yourFields
INTO #TempTableName
JOIN SomeOtherTables
WHERE SomeField=SomeValue;
CREATE CLUSTERED INDEX SomeIndexName ON #TempTableName(SomeField,AnotherField);
SELECT *
FROM tProduct p
JOIN #TempTableName t ON t.SomeField = p.SomeField
...
DROP TABLE #TempTableName -- optional, the table will die when it goes out of scope at the end of the procedure.
The temp table index doesn't have to be clustered, thats down to your choice.
Andrew has an excellent answer, but if this is a subquery you will be using often, another option would be to create an indexed view. There are several good articles about that, including one I wrote at SqL Server Central titled On Indexes and Views .

Oracle Select Query, Order By + Limit Results

I am new to Oracle and working with a fairly large database. I would like to perform a query that will select the desired columns, order by a certain column and also limit the results. According to everything I have read, the below query should be working but it is returning "ORA-00918: column ambiguously defined":
SELECT * FROM(SELECT * FROM EAI.EAI_EVENT_LOG e,
EAI.EAI_EVENT_LOG_MESSAGE e1 WHERE e.SOURCE_URL LIKE '%.XML'
ORDER BY e.REQUEST_DATE_TIME DESC) WHERE ROWNUM <= 20
Any suggestions would be greatly appreciated :D
The error message means your result set contains two columns with the same name. Each column in a query's projection needs to have a unique name. Presumably you have a column (or columns) with the same name in both EAI_EVENT_LOG and EAI_EVENT_LOG_MESSAGE.
You also want to join on that column. At the moment you are generating a cross join between the two tables. In other words, if you have a hundred records in EAI_EVENT_LOG and two hundred records EAI_EVENT_LOG_MESSAGE your result set will be twenty thousand records (without the rownum). This is probably your intention.
"By switching to innerjoin, will that eliminate the error with the
current code?"
No, you'll still need to handle having two columns with the same name. Basically this comes from using SELECT * on two multiple tables. SELECT * is bad practice. It's convenient but it is always better to specify the exact columns you want in the query's projection. That way you can include (say) e.TRANSACTION_ID and exclude e1.TRANSACTION_ID, and avoid the ORA-00918 exception.
Maybe you have some columns in both EAI_EVENT_LOG and EAI_EVENT_LOG_MESSAGE tables having identical names? Instead of SELECT * list all columns you want to select.
Other problem I see is that you are selecting from two tables but you're not joining them in the WHERE clause hence the result set will be the cross product of those two table.
You need to stop using SQL '89 implicit join syntax.
Not because it doesn't work, but because it is evil.
Right now you have a cross join which in 99,9% of the cases is not what you want.
Also every sub-select needs to have it's own alias.
SELECT * FROM
(SELECT e.*, e1.* FROM EAI.EAI_EVENT_LOG e
INNER JOIN EAI.EAI_EVENT_LOG_MESSAGE e1 on (......)
WHERE e.SOURCE_URL LIKE '%.XML'
ORDER BY e.REQUEST_DATE_TIME DESC) s WHERE ROWNUM <= 20
Please specify a join criterion on the dotted line.
Normally you do a join on a keyfield e.g. ON (e.id = e1.event_id)
It's bad idea to use select *, it's better to specify exactly which fields you want:
SELECT e.field1 as customer_id
,e.field2 as customer_name
.....

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