Oracle porting natural JOIN - oracle

I am reviewing code written by a previous colleague. It uses a "natural join", which I am unfamiliar with and never used.
I would like to change this into JOIN inner, outer, left….. whatever the correct equivalent is, which shows what is actually being joined.
Below is my test case. Any help would be greatly appreciated.
create table holidays(
holiday_date DATE not null,
holiday_name VARCHAR2(20),
constraint holidays_pk primary key (holiday_date),
constraint is_midnight check ( holiday_date = trunc ( holiday_date ) )
);
INSERT into holidays (HOLIDAY_DATE,HOLIDAY_NAME)
WITH dts as (
select to_date('25-NOV-2021 00:00:00','DD-MON-YYYY HH24:MI:SS'), 'Thanksgiving 2021' from dual union all
select to_date('29-NOV-2021 00:00:00','DD-MON-YYYY HH24:MI:SS'), 'Hanukkah 2021' from dual
)
SELECT * from dts;
SELECT constraint_name, constraint_type, column_name
from user_constraints natural join user_cons_columns
where table_name = 'HOLIDAYS';
CONSTRAINT_NAME CONSTRAINT_TYPE COLUMN_NAME
SYS_C0075523509 C HOLIDAY_DATE
IS_MIDNIGHT C HOLIDAY_DATE
HOLIDAYS_PK P HOLIDAY_DATE

In a natural join, Oracle creates an "implicit" join clause based on common column names. If you want to switch to inner join, you'll have to do it yourself.
Natural:
SQL> SELECT constraint_name, constraint_type, column_name
2 from user_constraints natural join user_cons_columns
3 where table_name = 'EMP';
CONSTRAINT_NAME C COLUMN_NAME
------------------------------ - --------------------
SYS_C00105284 C EMPNO
PK_EMP P EMPNO
Which columns are common? Let's see:
SQL> select column_name from all_tab_columns where table_name = 'USER_CONSTRAINTS'
2 intersect
3 select column_name from all_tab_columns where table_name = 'USER_CONS_COLUMNS';
COLUMN_NAME
--------------------
CONSTRAINT_NAME
OWNER
TABLE_NAME
SQL>
Inner:
SQL> SELECT a.constraint_name, a.constraint_type, b.column_name
2 from user_constraints a join user_cons_columns b
3 on a.constraint_name = b.constraint_name
4 and a.owner = b.owner
5 and a.table_name = b.table_name
6 where a.table_name = 'EMP';
CONSTRAINT_NAME C COLUMN_NAME
------------------------------ - --------------------
SYS_C00105284 C EMPNO
PK_EMP P EMPNO
SQL>
OK, but - we know better. As we're querying user_cons... views, they contain data we own so we don't really need an owner. Moreover, constraint names are unique within a single schema, so we don't need table_name either. Therefore, this would do:
SQL> SELECT a.constraint_name, a.constraint_type, b.column_name
2 from user_constraints a join user_cons_columns b on a.constraint_name = b.constraint_name
3 where a.table_name = 'EMP';
CONSTRAINT_NAME C COLUMN_NAME
------------------------------ - --------------------
SYS_C00105284 C EMPNO
PK_EMP P EMPNO
SQL>

Related

Oracle - How to find where a table is used in a foreign key constraint? [duplicate]

In Oracle SQL Developer, if I'm viewing the information on a table, I can view the constraints, which let me see the foreign keys (and thus which tables are referenced by this table), and I can view the dependencies to see what packages and such reference the table. But I'm not sure how to find which tables reference the table.
For example, say I'm looking at the emp table. There is another table emp_dept which captures which employees work in which departments, which references the emp table through emp_id, the primary key of the emp table. Is there a way (through some UI element in the program, not through SQL) to find that the emp_dept table references the emp table, without me having to know that the emp_dept table exists?
No. There is no such option available from Oracle SQL Developer.
You have to execute a query by hand or use other tool (For instance PLSQL Developer has such option). The following SQL is that one used by PLSQL Developer:
select table_name, constraint_name, status, owner
from all_constraints
where r_owner = :r_owner
and constraint_type = 'R'
and r_constraint_name in
(
select constraint_name from all_constraints
where constraint_type in ('P', 'U')
and table_name = :r_table_name
and owner = :r_owner
)
order by table_name, constraint_name
Where r_owner is the schema, and r_table_name is the table for which you are looking for references. The names are case sensitive
Be careful because on the reports tab of Oracle SQL Developer there is the option "All tables / Dependencies" this is from ALL_DEPENDENCIES which refers to "dependencies between procedures, packages, functions, package bodies, and triggers accessible to the current user, including dependencies on views created without any database links.". Then, this report have no value for your question.
To add this to SQL Developer as an extension do the following:
Save the below code into an xml file (e.g. fk_ref.xml):
<items>
<item type="editor" node="TableNode" vertical="true">
<title><![CDATA[FK References]]></title>
<query>
<sql>
<![CDATA[select a.owner,
a.table_name,
a.constraint_name,
a.status
from all_constraints a
where a.constraint_type = 'R'
and exists(
select 1
from all_constraints
where constraint_name=a.r_constraint_name
and constraint_type in ('P', 'U')
and table_name = :OBJECT_NAME
and owner = :OBJECT_OWNER)
order by table_name, constraint_name]]>
</sql>
</query>
</item>
</items>
Add the extension to SQL Developer:
Tools > Preferences
Database > User Defined Extensions
Click "Add Row" button
In Type choose "EDITOR", Location is where you saved the xml file above
Click "Ok" then restart SQL Developer
Navigate to any table and you should now see an additional tab next to SQL one, labelled FK References, which displays the new FK information.
Reference
http://www.oracle.com/technetwork/issue-archive/2007/07-jul/o47sql-086233.html
Replace [Your TABLE] with emp in the query below
select owner,constraint_name,constraint_type,table_name,r_owner,r_constraint_name
from all_constraints
where constraint_type='R'
and r_constraint_name in (select constraint_name
from all_constraints
where constraint_type in ('P','U')
and table_name='[YOUR TABLE]');
You may be able to query this from the ALL_CONSTRAINTS view:
SELECT table_name
FROM ALL_CONSTRAINTS
WHERE constraint_type = 'R' -- "Referential integrity"
AND r_constraint_name IN
( SELECT constraint_name
FROM ALL_CONSTRAINTS
WHERE table_name = 'EMP'
AND constraint_type IN ('U', 'P') -- "Unique" or "Primary key"
);
SQL Developer 4.1, released in May of 2015, added a Model tab which shows table foreign keys which refer to your table in an Entity Relationship Diagram format.
SELECT DISTINCT table_name,
constraint_name,
column_name,
r_table_name,
position,
constraint_type
FROM (SELECT uc.table_name,
uc.constraint_name,
cols.column_name,
(SELECT table_name
FROM user_constraints
WHERE constraint_name = uc.r_constraint_name) r_table_name,
(SELECT column_name
FROM user_cons_columns
WHERE constraint_name = uc.r_constraint_name
AND position = cols.position) r_column_name,
cols.position,
uc.constraint_type
FROM user_constraints uc
inner join user_cons_columns cols
ON uc.constraint_name = cols.constraint_name
WHERE constraint_type != 'C')
START WITH table_name = '&&tableName'
AND column_name = '&&columnName'
CONNECT BY NOCYCLE PRIOR table_name = r_table_name
AND PRIOR column_name = r_column_name;
This has been in the product for years - although it wasn't in the product in 2011.
But, simply click on the Model page.
Make sure you are on at least version 4.0 (released in 2013) to access this feature.
How about something like this:
SELECT c.constraint_name, c.constraint_type, c2.constraint_name, c2.constraint_type, c2.table_name
FROM dba_constraints c JOIN dba_constraints c2 ON (c.r_constraint_name = c2.constraint_name)
WHERE c.table_name = <TABLE_OF_INTEREST>
AND c.constraint_TYPE = 'R';
To add to the above answer for sql developer plugin, using the below xml will help in getting the column associated with the foreign key.
<items>
<item type="editor" node="TableNode" vertical="true">
<title><![CDATA[FK References]]></title>
<query>
<sql>
<![CDATA[select a.owner,
a.constraint_name,
a.table_name,
b.column_name,
a.status
from all_constraints a
join all_cons_columns b ON b.constraint_name = a.constraint_name
where a.constraint_type = 'R'
and exists(
select 1
from all_constraints
where constraint_name=a.r_constraint_name
and constraint_type in ('P', 'U')
and table_name = :OBJECT_NAME
and owner = :OBJECT_OWNER)
order by table_name, constraint_name]]>
</sql>
</query>
</item>
</items>
I like to do this with a straight SQL query, rather than messing about with the SQL Developer application.
Here's how I just did it. Best to read through this and understand what's going on, so you can tweak it to fit your needs...
WITH all_primary_keys AS (
SELECT constraint_name AS pk_name,
table_name
FROM all_constraints
WHERE owner = USER
AND constraint_type = 'P'
)
SELECT ac.table_name || ' table has a foreign key called ' || upper(ac.constraint_name)
|| ' which references the primary key ' || upper(ac.r_constraint_name) || ' on table ' || apk.table_name AS foreign_keys
FROM all_constraints ac
LEFT JOIN all_primary_keys apk
ON ac.r_constraint_name = apk.pk_name
WHERE ac.owner = USER
AND ac.constraint_type = 'R'
AND ac.table_name = nvl(upper(:table_name), ac.table_name)
ORDER BY ac.table_name, ac.constraint_name
;
Only Replace table_name with your primary table name
select *
from all_constraints
where r_constraint_name in (
select constraint_name
from all_constraints
where table_name='table_name'
);
Replace MY_OWNER_NAME and MY_TABLE_NAME below and you are ready to go RECURSIVELY:
DECLARE
FUNCTION list_all_child_tables_and_constraints(asked_table_name in VARCHAR2, parent_table_name in VARCHAR2)
RETURN VARCHAR2 IS
current_path VARCHAR2(100);
BEGIN
FOR item IN
(SELECT fk.TABLE_NAME, constraint_parent.FK FK1, constraint_child.FK FK2
FROM all_constraints fk, all_constraints pk,
(SELECT acc.CONSTRAINT_NAME, LISTAGG(acc.COLUMN_NAME, ', ') WITHIN GROUP (ORDER BY acc.COLUMN_NAME) AS FK
FROM ALL_CONS_COLUMNS acc
WHERE acc.OWNER = 'MY_OWNER_NAME'
GROUP BY acc.CONSTRAINT_NAME) constraint_parent,
(SELECT acc.CONSTRAINT_NAME, LISTAGG(acc.COLUMN_NAME, ', ') WITHIN GROUP (ORDER BY acc.COLUMN_NAME) AS FK
FROM ALL_CONS_COLUMNS acc
WHERE acc.OWNER = 'MY_OWNER_NAME'
GROUP BY acc.CONSTRAINT_NAME) constraint_child
WHERE pk.owner = fk.r_owner
AND pk.constraint_name = fk.r_constraint_name
AND fk.constraint_type = 'R'
AND pk.table_name = asked_table_name
AND constraint_parent.CONSTRAINT_NAME = fk.CONSTRAINT_NAME
AND constraint_child.CONSTRAINT_NAME = fk.R_CONSTRAINT_NAME
AND pk.owner = 'MY_OWNER_NAME'
AND fk.owner = 'MY_OWNER_NAME')
LOOP
current_path := parent_table_name || ' // ' || item.TABLE_NAME;
DBMS_OUTPUT.PUT_LINE(current_path);
DBMS_OUTPUT.PUT_LINE(' [' || item.FK1 || '] [' || item.FK2 || ']');
DBMS_OUTPUT.PUT_LINE('');
current_path := list_all_child_tables_and_constraints(item.TABLE_NAME, current_path);
END LOOP;
RETURN '-----------FINISHED-----------';
EXCEPTION
WHEN OTHERS THEN
RETURN '-----------FINISHED-----------';
END list_all_child_tables_and_constraints;
BEGIN
DBMS_OUTPUT.PUT_LINE(list_all_child_tables_and_constraints('MY_TABLE_NAME', ''));
END;

How to improve performance of oracle query in finding referential table name

I got query below:
SELECT TABLE_NAME, CONSTRAINT_NAME FROM USER_CONSTRAINTS
WHERE R_OWNER = 'OWNERNAME' AND STATUS ='ENABLED' AND CONSTRAINT_TYPE = 'R'
AND R_CONSTRAINT_NAME IN ( SELECT CONSTRAINT_NAME FROM ALL_CONSTRAINTS
WHERE CONSTRAINT_TYPE IN ('P', 'U') AND TABLE_NAME = 'REGIONS'
AND OWNER = 'OWNERNAME' )
It took 4 Secs to get output of it.
Is there any better way to get the name of referential table name?
Thanks in advance.
Joon
There is a join condition missing in your query:
SELECT TABLE_NAME,
CONSTRAINT_NAME
FROM USER_CONSTRAINTS
WHERE R_OWNER = 'OWNERNAME'
AND STATUS ='ENABLED'
AND CONSTRAINT_TYPE = 'R'
AND (R_OWNER, R_CONSTRAINT_NAME) IN ( SELECT OWNER, CONSTRAINT_NAME
FROM ALL_CONSTRAINTS
WHERE CONSTRAINT_TYPE IN ('P', 'U')
AND TABLE_NAME = 'REGIONS'
AND OWNER = 'OWNERNAME' )
You are not limiting R_OWNER to 'OWNERNAME' which means that is has to look at all constraints in the schema OWNERNAME not only those that link to the table OWNERNAME.REGIONS.

Get details from System Catalog in Oracle 11G

I need to find the following information on all user defined tables in a SINGLE query in Oracle11g:
Table name
Column name
Constraint name
Constraint type - Use 'PK', 'FK', 'CK' and 'NN' instead of 1 letter codes
Search Condition for any Check (CK) constraints
Table and Column that each FK references
So far, I have managed this:
SELECT table_name FROM user_tables ORDER BY table_name;
TABLE_NAME
---------------
CLIENT
EMPLOYEE
FAULTREPORT
OUTLET
RAGREEMENT
VEHICLE
SELECT table_name, constraint_name, constraint_type
FROM user_constraints
WHERE table_name IN ('CLIENT','EMPLOYEE','FAULTREPORT','OUTLET','RAGREEMENT','VEHICLE')
ORDER BY table_name;
TABLE_NAME CONSTRAINT_NAME C SEARCH_CONDITION
--------------- ------------------------------ - --------------------------------------------------------------------------------
CLIENT CLIENT_CLIENTNAME_NN C clientName IS NOT NULL
CLIENT CLIENT_STREET_NN C street IS NOT NULL
CLIENT CLIENT_CITY_NN C city IS NOT NULL
CLIENT CLIENT_STATE_NN C state IS NOT NULL
CLIENT CLIENT_EMAIL_CK C REGEXP_LIKE(email,'^[A-Z0-9._%-]+#[A-Z0-9.-]+\.[A-Z]{2,}$','i') AND email IS NOT
NULL
CLIENT CLIENT_PHONE_CK C REGEXP_LIKE(phone,'^(\(\d{3}\))([[:blank:]])\d{3}-\d{4}$|^\d{3}(-)\d{3}(-)\d{4}$
|^\d{10}$')
AND phone IS NOT NULL
CLIENT CLIENT_CLIENTNO_PK P
EMPLOYEE EMPLOYEE_LNAME_NN C lname IS NOT NULL
EMPLOYEE EMPLOYEE_NUM_PK P
EMPLOYEE EMPLOYEE_OUTLET_NUM_FK R
EMPLOYEE EMPLOYEE_FNAME_NN C fname IS NOT NULL
EMPLOYEE EMPLOYEE_DOB_CK C hireDate >= ADD_MONTHS(dob, 216)
EMPLOYEE EMPLOYEE_PHONE_CK C REGEXP_LIKE(phone,'^(\(\d{3}\))([[:blank:]])\d{3}-\d{4}$|^\d{3}(-)\d{3}(-)\d{4}$
|^\d{10}$')
AND phone IS NOT NULL
EMPLOYEE EMPLOYEE_GENDER_CK C gender in ('MALE', 'FEMALE')
FAULTREPORT FAULTREPORT_DATECHECKED_NN C dateChecked IS NOT NULL
FAULTREPORT FAULTREPORT_REPORTNUM_PK P
FAULTREPORT FAULTREPORT_EMPLOYEE_NUM_FK R
FAULTREPORT FAULTREPORT_RENTAL_NUM_FK R
FAULTREPORT FAULTREPORT_LICENSENO_NN C licenseNo IS NOT NULL
OUTLET OUTLET_STREET_NN C street IS NOT NULL
OUTLET OUTLET_NUM_PK P
OUTLET OUTLET_STATE_NN C state IS NOT NULL
OUTLET OUTLET_ZIPCODE_NN C zipCode IS NOT NULL
OUTLET OUTLET_PHONE_CK C REGEXP_LIKE(phone,'^(\(\d{3}\))([[:blank:]])\d{3}-\d{4}$|^\d{3}(-)\d{3}(-)\d{4}$
|^\d{10}$')
AND phone IS NOT NULL
OUTLET OUTLET_CITY_NN C city IS NOT NULL
OUTLET OUTLET_MGR_NUM_FK R
RAGREEMENT RAGREEMENT_CLIENT_NUM_FK R
RAGREEMENT RAGREEMENT_LICENSE_NUM_FK R
RAGREEMENT RAGREEMENT_MILEAGE_CK C mileagebefore < mileageafter
RAGREEMENT RAGREEMENT_INSURANCETYPE_NN C insurancetype IS NOT NULL
RAGREEMENT RAGREEMENT_PK P
RAGREEMENT RAGREEMENT_STARTDATE_NN C startdate IS NOT NULL
RAGREEMENT RAGREEMENT_MILEAGEBEFORE_NN C mileagebefore IS NOT NULL
VEHICLE VEHICLE_DAILYRATE_NN C dailyrate IS NOT NULL
VEHICLE VEHICLE_MODEL_NN C model IS NOT NULL
VEHICLE VEHICLE_MAKE_NN C make IS NOT NULL
VEHICLE SYS_C0010620 C "LICENSENO" IS NOT NULL
VEHICLE VEHICLE_LICENSE_NUM_PK P
VEHICLE VEHICLE_YEAR_CK C EXTRACT(YEAR FROM year) > 2005 AND year IS NOT NULL
VEHICLE VEHICLE_OUT_NUM_FK R
40 rows selected.
Can someone help me out? Thanks!
Well, you could simply use
SELECT table_name, constraint_name, constraint_type
FROM user_constraints
WHERE table_name IN (SELECT table_name FROM user_tables)
ORDER BY table_name;
or
SELECT UT.table_name, UC.constraint_name, UC.constraint_type
FROM user_tables UT
INNER JOIN user_constraints UC ON UT.table_name = UC. table_name
ORDER BY UT.table_name;
This will give you details about all the tables you own.
Now, if you want the details about all tables your account has access to, you can use
SELECT table_name, constraint_name, constraint_type
FROM user_constraints
WHERE table_name IN (SELECT table_name FROM all_tables)
ORDER BY table_name;
or
SELECT AT.table_name, UC.constraint_name, UC.constraint_type
FROM all_tables AT
INNER JOIN user_constraints UC ON AT.table_name = UC. table_name
ORDER BY AT.table_name;
EDITED : If you want to see all column names and the constraint details if any, use the below (untested)
SELECT UTC.table_name
, UTC.column_name
, UC.constraint_name
, UC.constraint_type
FROM USER_TAB_COLUMNS UTC
LEFT JOIN user_constraints UC ON UTC.table_name = UC. table_name
ORDER BY UTC.table_name;

check data with multiple 'in' result

It seems a stupid question, but how to get multiple 'in' result ?
I have 2 tables(tab1,tab2) with the the same 2 columns : table_name and column_name.
I want to check the data in table_name and column_name in tab1 that doest not exists in tab2
this query is wrong but similar to what I want:
select distinct s.table_name , s.column_name from tab1 s where
(S.table_name ,s.column_name) not in ((select table_name from tab2 ,
(select column_name from tab2))
If you want to eliminate rows where pairs (tname, tcolumn) are present in second table then:
select distinct s.table_name, s.column_name
from tab1 s
where (s.table_name, s.column_name)
not in (select table_name, column_name from tab2)
... and if you want to eliminate everything, if tname or tcolumn exists in table2:
select distinct s.table_name, s.column_name
from tab1 s
where s.table_name not in (select table_name from tab2)
and s.column_name not in (select column_name from tab2)
select s.table_name , s.column_name
from tab1 s
MINUS
select t.table_name , t.column_name
from tab2 t;
This should give the data that is only in tab1
There is always the option of using NOT EXISTS:
SELECT s.table_name, s.column_name
FROM tab1 s
WHERE NOT EXISTS ( SELECT 1 FROM tab1 t
WHERE t.table_name = s.table_name
AND t.column_name = s.column_name );

List of table which don't have a primary key

Need to get the list of tables name from a schema which doesn't have a primary key. I tried the following query to get this, but it will list all other keys except primary keys.
SELECT a.constraint_name,a.table_name
FROM ALL_CONS_COLUMNS A
JOIN ALL_CONSTRAINTS C
ON A.CONSTRAINT_NAME = C.CONSTRAINT_NAME
WHERE
C.CONSTRAINT_TYPE not in('P')
and a.owner ='my_schema';
If you only want this for the current user, it's better to user the user_xxx views instead of the all_xxx views.
The following should do what you want:
select ut.table_name
from user_tables ut
where not exists (select 1
from user_constraints ac
where ac.table_name = ut.table_name
and ac.constraint_type = 'P'
);
If you do need this for a different user, then you can use the following.
select at.*
from all_tables at
where not exists (select 1
from all_constraints ac
where ac.owner = at.owner
and ac.table_name = at.table_name
and ac.constraint_type = 'P'
)
and at.owner = 'MY_SCHEMA';
Don't forget that Oracle is case sensitive and that user names are stored in uppercase, so a.owner ='my_schema' will most probably not return anything.
Another way:
select owner,table_name
from all_tables
where owner = 'my_schema'
MINUS
select owner,table_name
from all_constraints
where owner = 'my_schema'
and constraint_type = 'P'
Try like this,
SELECT table_name
FROM all_tables A
WHERE table_name NOT IN
(
SELECT table_name
FROM all_constraints WHERE constraint_type ='P'
)
AND a.owner = 'my_schema';
Select tables.owner || ‘.’ ||
tables.table_name
as table_owner
From all_tables tables,
(Select
owner,
TABLE_NAME,
constraint_type,
CONSTRAINT_NAME
FROM ALL_CONSTRAINTS
WHERE CONSTRAINT_TYPE = ‘P’ /* list of all tables with PK */
) constr
Where tables.owner = constr.owner (+)
And tables.table_name = constr.table_name (+)
and tables.owner <> ‘SYS’
and tables.owner <> ‘SYSTEM’
And constr.owner IS NULL
And constr.table_name IS NULL
ORDER BY 1

Resources