I created a random View like this:
CREATE OR REPLACE NOFORCE VIEW TEMP_VIEW (first_name, age) AS
SELECT 'Gladiolus' first_name, 23 age
FROM dual
/
And I check it out:
SELECT * FROM temp_view
/
FIRST_NAME AGE
---------- ----------
Gladiolus 23
Now, when I open the source to edit it (with PLSQL Developer and SQL Navigator) I get this:
CREATE OR REPLACE VIEW TEMP_VIEW AS
SELECT 'Gladiolus' first_name, 23 age
FROM dual;
The alias (first_name, age) is ommited because they are equal to the columns of the subquery.
But can you tell me why the clause NOFORCE is also not showed?
Thank you!!
Some keywords control how objects are created but they are not a part of the object definition. Oracle does not always care "how" an object was created and does not always store the options used to create it.
When Oracle and 3rd party tools create a DDL statement they must guess which of those options you would like to use. It's not uncommon for the tools to make different guesses, or to have small syntax differences.
(This is one of the many reasons to store all DDL in version-controlled text files instead of the database. What you put into the database will NOT be the same as what you get out.)
To use your example another way:
SQL> CREATE NOFORCE VIEW TEMP_VIEW (first_name, age) AS
2 SELECT 'Gladiolus' first_name, 23 age
3 FROM dual
4 /
View created.
The view was created with NOFORCE and without an OR REPLACE. But the DDL generated by DBMS_METADATA.GET_DDL contains FORCE, OR REPLACE and other differences:
SQL> select dbms_metadata.get_ddl('VIEW', 'TEMP_VIEW') from dual;
DBMS_METADATA.GET_DDL('VIEW','TEMP_VIEW')
--------------------------------------------------------------------------------
CREATE OR REPLACE FORCE EDITIONABLE VIEW "JHELLER"."TEMP_VIEW" ("FIRST_NAME",
"AGE") AS
SELECT 'Gladiolus' first_name, 23 age
FROM dual
The real "source" of the object does not contain any of the CREATE options at all:
SQL> select text from dba_views where view_name = 'TEMP_VIEW';
TEXT
--------------------------------------------------------------------------------
SELECT 'Gladiolus' first_name, 23 age
FROM dual
Here's an incomplete list of things that may be different when you regenerate code from an Oracle databases:
OR REPLACE
FORCE|NOFORCE
AND RESOLVE|AND COMPILE (for Java)
Whitespace
Case
Column Lists
Double Quotes
Terminators
Different DDL generators allow you to control some of those options, but in my experience none of them let you completely control all of them.
NOFORCE is the default, so keeping it is redundant.
Related
Problem: I want to get an average salary of all employees. The query itself is rather simple:
Question: Is it possible to move COUNT(salary) to selected column's title (after AS statement), so the name of the column will be "Average salary of 20 employees"? I've been trying several approaches, but none of them worked. I would really appreciate any help. Thank you in advance.
Screenshot you posted looks like Apex SQL Workshop. I don't know how to do it there, but - if you used SQL*Plus, then you could do something like this (not exactly as you wanted it, though):
SQL> set ver off
SQL> column cnt_emps new_value l_cnt
SQL> select count(*) cnt_emps from emp;
CNT_EMPS
----------
14
SQL> select avg(sal) as "Average salary of &l_cnt employees" from emp;
Average salary of 14 employees
--------------------------------------
2073.21429
SQL>
However, if you're already using Apex, switch to GUI and create a page which will let you format the result any way you want (using colors, large font, bold letters, whatever). SQL*Plus and its descendants are kind of restrictive when such things should be done.
I am quite new to SQL, and my first "job" is to get something out of an Oracle SQL database.
Just to see what's actually found in my connection I use the following:
SELECT owner, table_name FROM dba_tables
This gives me a bunch of tuples with an owner name and table_name. However, some table names are the same for different owners.
So when I run a command like:
SELECT * FROM MyTableName
How do I ensure that this table is coming from owner1 and not owner2, where both of them actually have a table called MyTableName ?
You can do:
SELECT * FROM <owner>.MyTableName
You can specify exactly which table with
select * from some_owner.my_table;
If you do this:
select * from my_table;
You will be selecting from the version of MY_TABLE that belongs to the owner/user with which you you are connected to the database. So, if you connected as user SCOTT, then
select * from my_table;
effectively becomes
select * from scott.my_table;
Note that this behavior can be overridden by the use of synonyms. Suppose user SCOTT has a synonym like this:
create synonym scott.my_table for fred.my_table;
Then SCOTT issues
select * from my_table;
Then oracle will check first for a synonym, and finding it will effectively transform the query to
select * from fred.my_table;
So in the end, the precedence is this. When you reference a table without qualifying it with the owner, oracle will look for it in this sequence:
is there a user synonym by that name? if so, use what it references if the user has been granted necessary privileges.
is there a global synonym by that name? if so, use what it references if the user has been granted necessary privileges.
does the user itself own a table by that name? if so, use that table.
return error "ORA-00942: table or view does not exist"
We have a large amount of data in an Oracle 11g server. Most of the engineers use Tableau for visualizing data, but there is currently not a great solution for visualizing straight from the Oracle server because of the structure of the database. Unfortunately, this cannot be changed, as it's very deeply integrated with the rest of our systems. There is a "dictionary" table, let's call it tab_keys:
name | key
---------------
AB-7 | 19756
BG-0 | 76519
FY-10 | 79513
JB-2 | 18765
...
...
And there are also the tables actually containing the data. Each entry in tab_keys has a corresponding data table named by prefixing the key with an identifier, in this case, we'll use "dat_". So AB-7 will store all its data in a table called dat_19756. These keys are not known to the user, and are only used for tracking "behind the scenes". The user only knows the AB-7 moniker.
Tableau allows communication with Oracle servers using standard SQL select statements, but because the user doesn't know the key value, they cannot write a SQL statement to query the data.
Tableau recently added the ability for users to query Oracle Table Functions, so I started going down the road of writing a table function to query for the key, and return a table of the results for Tableau to use. The problem is that each dat_ table is basically unique with a different numbers of columns, labels, number of records, and datatypes from the next dat_ table.
What is the right way to handle this problem? Can I:
1) Write a function (which tableau can call inline in regular SQL) to return a bonified table name which is dynamically generated? I tried this:
create or replace FUNCTION TEST_FUNC
(
V_NAME IN VARCHAR2
) RETURN user_tables.table_name%type AS
V_KEY VARCHAR(100);
V_TABLE user_tables.table_name%type;
BEGIN
select KEY into V_KEY from my_schema.tab_keys where NAME = V_NAME;
V_TABLE := dbms_assert.sql_object_name('my_schema.dat_' || V_KEY);
RETURN V_TABLE;
END TEST_FUNC;
and then SELECT * from TABLE(TEST_FUNC('AB-7')); but I get:
ORA-22905: cannot access rows from a non-nested table item
22905. 00000 - "cannot access rows from a non-nested table item"
*Cause: attempt to access rows of an item whose type is not known at
parse time or that is not of a nested table type
*Action: use CAST to cast the item to a nested table type
I couldn't figure out a good way to CAST the table as the table type I needed. Could this be done in the function before returning?
2) Write a table function? Tableau can supposedly query these like tables, but then I run into the problem of dynamically generating types (which I understand isn't easy) but with the added complication of this needing to be used by multiple users simultaneously, so each user would need a data type generated for them each time they connect to a table (if I'm understanding this correctly).
I have to think I'm missing something simple. How do I cast the return of this query as this other table's datatype?
There is no simple way to have a single generic function return a dynamically configurable nested table. With other products you could use a Ref Cursor (which maps to ODBC or JDBC ResultSet object) but my understanding is Tableau does not support that option.
One thing you can do is generate views from your data dictionary. You can use this query to produce a one-off script.
select 'create or replace view "' || name || '" as select * from dat_' || key || ';'
from tab_keys;
The double-quotes are necessary because AB-7 is not a valid object name in Oracle, due to the dash.
This would allow your users to query their data like this:
select * from "AB-7";
Note they would have to use the double-quotes too.
Obviously, any time you inserted a row in tab_keys you'd need to create the required view. That could be done through a trigger.
You can build dynamic SQL in SQL using the open source program Method4:
select * from table(method4.dynamic_query(
q'[
select 'select * from dat_'||key
from tab_keys
where name = 'AB-7'
]'
));
A
-
1
The program combines Oracle Data Cartridge Interface with ANYDATASET to create a function that can return dynamic types.
There might be a way to further simplify the interface but I haven't figured it out yet. These Oracle Data Cartridge Interface functions are very picky and are not easy to repackage.
Here's the sample schema I used:
create table tab_keys(name varchar2(100), key varchar2(100));
insert into tab_keys
select 'AB-7' , '19756' from dual union all
select 'BG-0' , '76519' from dual union all
select 'FY-10', '79513' from dual union all
select 'JB-2' , '18765' from dual;
create table dat_19756 as select 1 a from dual;
create table dat_76519 as select 2 b from dual;
create table dat_79513 as select 3 c from dual;
create table dat_18765 as select 4 d from dual;
I am new to Oracle and want to find all tables created by user 'john' .
I connect to Oracle database via command line by the following command:
sqlplus john/passwd
How do i list all the tables created by a given user e.g. john?
This will get all the tables where the "JOHN" user is the owner:
SELECT * FROM USER_TABLES;
or
SELECT * FROM ALL_TABLES WHERE OWNER = 'JOHN';
([TL;DR] 'JOHN' typically needs to be in upper-case. Assuming that the user john was created using the CREATE USER john ... statement then Oracle's default behaviour is to convert all object names (i.e. tables, columns, users, etc) to upper case. When you query the data-dictionary the table details will be stored in this case (and not the case you used in the original command unless you wrap it in double quotes).)
To list the table you can use
SELECT * FROM ALL_TABLES WHERE OWNER = 'JOHN';
TO see the size of the schema you can use
SELECT sum(bytes)
FROM dba_segments
WHERE owner = 'JOHN'
Since you are logged in as the schema owner, you can also use
SELECT SUM(bytes)
FROM user_segments
You can use also
select * from
USER_TABLES;
anyway you can find all the data dictionary explain here https://docs.oracle.com/cd/B28359_01/server.111/b28310/tables014.htm
select * from
USER_TABLES;
The above code will show all the information of the tables under the user which is currently connected. This might clutter your SQL terminal.
To Specifically see only the table names under a user you should use the following code
select table_name
from USER_TABLES;
Try:
select *
from all_tables
where owner = 'jhon';
Working on parsing a bunch of databases put together in an older, more freewheeling time into a new schema. Basically it's one database per year, with database names like foo98, foo99, foo2000, etc.
So for the most recent foo data, I can do something like
SELECT foo_person.mdname AS middle_name,
...
FROM foo_person, foo_place, foo_thing
As you get back into earlier versions of the foo database, middle name isn't stored. I've tried to build a kind of universal query, something like:
SELECT IFNULL(foo_person.mdname, "") AS middle_name,
...
FROM foo_person, foo_place, foo_thing
but MySQL complains about unknown column foo_person.mdname, which is entirely reasonable as it doesn't exist.
Is there some way to handle non-existent columns with just MySQL syntax, or will I have to write database-specific import queries?
There isn't any way of handling a non-existent column in sql (as opposed to an empty column).
You can tell whether the column is there or not using the information_schema tables, like so:
select * from information_schema.columns
where table_name='mytable' and table_schema='mydatabase';
Yes there is a way.
Let's consider these databases
DB2009 has Person with Fname, MInitial and LName
DB2008 has Person with Fname and LName
DB2007 has Person with PersonName
You can do something similar the following (I wrote this for MS SQL Server)
/*all three columns exist*/
SELECT FName, MInitial, LName
From DB2009.Person
UNION
/*one column is a forced null */
SELECT FName, NULL as MInitial, LName
From DB2008.Person
UNION
/*two columns are derived and one column is a forced null */
SELECT SubString (1, CharIndex (PersonName, ' '), PersonName) as FirstName,
NULL as MInitial,
SubString (CharIndex (PersonName, ' '), len (PersonName), PersonName),
From DB2007.Person
Could you rename the tables and create views in their place with the missing columns?
Not sure if this is what you're looking for, but thought I would suggest it.
-- Here is your original table
create table t (fname varchar(30), lname varchar(30));
-- Rename it to something else
alter table t rename to tOrig;
-- Create a view with the columns its missing that you need
create view t as select fname, lname, '' as mname from tOrig;
If you're working with heterogenous databases, I would use database-specific import queries anyways. You might need to join some columns and remove some, and truncate some, etc.
Instead of making more complex sql queries, perhaps it would be better to
alter the foo98 and foo99 tables to add in the missing columns.
For example, to add a column of type varchar(30) called "mdname" to foo98:
ALTER TABLE foo98 ADD mdname varchar(30) AFTER first_name;
Then you can relax and use the same simple SQL query no matter which tabel is being accessed.