Get example data for each column in Database - sql

I'm trying to get EXAMPLE entries for each column in the database
The best I can come up with is something like:
SELECT owner || '.' || table_name || '.' || column_name AS ex FROM all_tab_cols;
When I do this, I get the list like I want however I'm having a very difficult time figuring out how to LOOP in SQL.
I think I have narrowed down what I want to do but am stuck figuring out PUT_LINE
BEGIN
FOR i IN (SELECT owner || '.' || table_name || '.' || column_name AS ex
FROM all_tab_cols)
LOOP
dbms_output.put_line(select * from i fetch first row only);
END LOOP;
END;
/
I have tried taking out the second select and just put in 'test' and I get the message "pl/SQL procedure successfully completed"
but there is no output.
This is an oracle database and I'm using SQL developer.

The simplest thing that might possibly work would be
declare
l_sql_stmt varchar2(4000);
l_value varchar2(4000);
begin
for i in (select owner, table_name, column_name
from all_tab_columns)
loop
l_sql_stmt := 'select ' || i.column_name ||
' from ' || i.owner || '.' || i.table_name ||
' fetch first row only';
execute immediate l_sql_stmt
into l_value;
dbms_output.put_line( l_value );
end loop;
end;
However, this only works if every column in every table you have access to can be implicitly converted to a varchar2(4000). If you have lob columns that exceed the 4000 byte length or you have columns with complex data types, you'll get an error. Since you're doing implicit conversion, you'll get different results for numbers, dates and timestamps depending on your session's NLS settings which might or might not be problematic for you. It would generally be sensible to at least add a predicate to the all_tab_columns query in the cursor to only select those columns whose data types you are prepared to handle.
If you wanted to get more sophisticated, you could use the dbms_sql package to describe the results and fetch the data into a local variable of the appropriate data type. That lets you fix the implicit conversion issue and would let you handle more data types (though, for example, I don't know what you'd want to display for a blob column) but it involves writing quite a bit more code and you'd have to explicitly handle every data type you want to be able to deal with.
Depending on why you're trying to do this, it may make more sense to use the column-level statistics the optimizer has gathered at least for columns that have statistics (but those are probably the columns you'd actually care about having example data for).

Related

Dynamic query building yields no results and crashes process

I am trying to run a SQL query within a PL/SQL script that takes dynamic parameters and should return a specific result. For that I tried the EXECUTE IMMEDIATE command. Unfortunately it crashes always my variable is a varchar. If it is a number instead it runs perfetcly.
Here is my tried code:
plsql_request := 'SELECT :x FROM TEST_TABLE WHERE ID = :y';
EXECUTE IMMEDIATE plsql_request INTO plsql_result USING variable_1, '2837123 | hsiae';
While testing it crashes always in this constelation.
Second approch:
plsql_request := 'SELECT ' || variable_1 || ' FROM TEST_TABLE WHERE ID = ''' || variable_2 || '''';
EXECUTE IMMEDIATE plsql_request ;
I do not get any results at all, hence I think the query-building using dynamic variables in combination with varchars fails.
Any help apreciated,
Filip.
You cannot use a bind variable for an object literal like a column name, table name, etc.. so you must use the second method with || variable_1 || to splice in your dynamic column name. However, you should use real bind variables :y, USING, etc.. for your predicate value in order to not generate an excessive number of child cursors.
So, :
plsql_request := 'SELECT '||variable_1||' FROM TEST_TABLE WHERE ID = :y';
EXECUTE IMMEDIATE plsql_request INTO plsql_result USING '2837123 | hsiae';

Oracle - BULK COLLECT INTO VARRAY used with Bind Variables only collecting column headers

Quick Disclaimer:
First thing out of the way, I know the preferred way of handling dynamic SQL in Oracle now is the DBMS_SQL package but unfortunately my application team does not have the grants to execute these procs at the moment and I am hoping to get this quick workaround knocked out before our DBA team gets back to me. Also, this database is on Oracle 12c.
Script Goal: I recently developed a Stored Proc (let's call it Original) that uses values in a "control table" to make a large number of updates to certain columns in a database with many schemas and tables. This script I am struggling with now (let's call it Test) is meant to be a quick loop through those columns affected by Original so as to verify that everything worked expectedly. Ultimately, I want to output the top 5 results of each changed column and hand a spooled file to my testing team for validation.
The control_table used in both scripts has 4 columns and looks like this:
OWNER
TABLE_NAME
COLUMN_NAME
ALGORITHM
Schema1
TableA
ColumnA
Method1
Schema1
TableB
ColumnB
Method1
Schema2
TableC
ColumnC
Method2
An example of one of the tables that gets updated by Original (let's say for TableA above) would be:
OtherCol1
OtherCol2
ColumnA
OtherCol3
Ignored
Ignored
UpdatedData1
Ignored
Ignored
Ignored
UpdatedData2
Ignored
Ignored
Ignored
UpdatedData3
Ignored
Issue with Test script: I have the dynamic SQL - I believe - working as it needs and I have been trying to figure out how best to print the results of the EXECUTE IMMEDIATE command to output. In doing some reading, I found that BULK COLLECT INTO should allow me to store the results of the dynamic queries into a COLLECTION which I can then print with dbms_output. I have attempted to do this with both a TABLE and a VARRAY but in both cases when I print, I am finding that the data stored in my collection is the column header of my dynamic query instead of the query values! The only thing I can think that could be the problem is the combining of BULK COLLECT INTO with the USING command when I run the dynamic statement but I have seen nothing in the documentation to indicate that these two commands are incompatible and my Test procedure below compiles without issue (and even seems to run ok).
Test Script:
SET SERVEROUTPUT ON SIZE UNLIMITED;
DECLARE
l_script VARCHAR2(500);
l_errm VARCHAR2(64);
TYPE results IS VARRAY(5) OF VARCHAR2(250);
va_cols results; --Defining here with a VARRAY but I have also tried with a table
BEGIN
FOR c_col IN(
SELECT owner, table_name, column_name, algorithm FROM control_list)
LOOP
l_errm := NULL;
va_cols := NULL;
BEGIN
dbms_output.put_line('Column '|| c_col.column_name || ' of table ' || c_col.owner ||
'.' || c_col.table_name || ' used algorithm ' || c_col.algorithm);
l_script := 'SELECT :1 FROM ' || c_col.owner || '.' || c_col.table_name ||
' WHERE :2 IS NOT NULL FETCH FIRST 5 ROWS ONLY';
dbms_output.put_line('Script sent to Exec Immediate: ' || l_script); --Print l_script for debugging
EXECUTE IMMEDIATE l_script BULK COLLECT INTO va_cols USING c_col.column_name, c_col.column_name;
dbms_output.put_line(va_cols(1));
dbms_output.put_line(va_cols(2));
dbms_output.put_line(va_cols(3));
dbms_output.put_line(va_cols(4));
dbms_output.put_line(va_cols(5));
EXCEPTION
WHEN OTHERS THEN
l_errm := SUBSTR(SQLERRM, 1, 64);
dbms_output.put_line(' ERROR: ' || l_errm || '. Skipping row');
CONTINUE;
END;
END LOOP;
END;
/
So my intended dbms_output of the script above is:
Column ColumnA of table Schema1.TableA used algorithm Method1
Script sent to Exec Immediate: SELECT :1 FROM SCHEMA1.TABLEA WHERE :2 IS NOT NULL FETCH FIRST 5 ROWS ONLY
UpdatedData1
UpdatedData2
UpdatedData3
UpdatedData4
UpdatedData5
Instead, however, bizarrely, what I am getting when I run this is:
Column ColumnA of table Schema1.TableA used algorithm Method1
Script sent to Exec Immediate: SELECT :1 FROM SCHEMA1.TABLEA WHERE :2 IS NOT NULL FETCH FIRST 5 ROWS ONLY
ColumnA
ColumnA
ColumnA
ColumnA
ColumnA
Has anyone seen this before and know what I am doing wrong? Thanks in advance!!
You can't use bind variables to change what columns you're referencing. You use bind variables to specify particular values at runtime. When you do
l_script := 'SELECT :1 FROM ' || c_col.owner || '.' || c_col.table_name ||
' WHERE :2 IS NOT NULL FETCH FIRST 5 ROWS ONLY';
EXECUTE IMMEDIATE l_script BULK COLLECT INTO va_cols USING c_col.column_name, c_col.column_name;
you're telling Oracle that you want to select the literal string in the variable c_col.column_name. Not the column in the table by that name. Which is why every row returns that literal value.
You'd need to dynamically assemble the SQL statement with the column names, not try to use them as bind variables. So something like
l_script := 'SELECT ' || c_col.column_name ||
' FROM ' || c_col.owner || '.' || c_col.table_name ||
' WHERE ' || c_col.column_name || ' IS NOT NULL FETCH FIRST 5 ROWS ONLY';
EXECUTE IMMEDIATE l_script BULK COLLECT INTO va_cols;
This is approximately what you want. I outer cursor over tables and column to inspect that generate the dynamic SQL.
Inner loop reading the column values from the previous query
DECLARE
TYPE CurTyp IS REF CURSOR;
v_cursor CurTyp;
v_value VARCHAR2(200);
v_stmt_str VARCHAR2(200);
BEGIN
FOR c IN (
SELECT table_name, column_name FROM control_list)
LOOP
dbms_output.put_line('tab: '||c.table_name);
v_stmt_str := 'SELECT '||c.column_name||' FROM '|| c.table_name;
OPEN v_cursor FOR v_stmt_str;
LOOP
FETCH v_cursor INTO v_value;
EXIT WHEN v_cursor%NOTFOUND;
dbms_output.put_line('col: '||c.column_name||' val: '||v_value);
END LOOP;
END LOOP;
CLOSE v_cursor;
END;
/

Oracle SQL - UNION breaks when "table does not exist"

I am new to Oracle SQL (and not generally experienced in SQL anyway). I would appreciate it if I could get some help with my problem.
I need to query several tables and views (30 or so) and do it as a UNION across all and return a single result set. However, some of the tables may or may not exist. I know the exact names of all 30 tables/views to be queried. I may do other queries prior to doing the large UNION, but eventually, I must return a single result of all rows. I cannot "define" variables in my sql script.
These are constraints placed on my and beyond my control. In order to work with the existing workflow, I have to operate within these contraints.
The problem is that if any of the tables/views do not exist, then the entire UNION fails and I get no results at all. I get this at the first occurrence of the missing view/table:
SQL Error: ORA-00942: table or view does not exist
00942. 00000 - "table or view does not exist"
*Cause:
*Action:
I would like be able to handle this without breaking. I'd like to simply skip over missing tables and return the remaining rows (though I know this is poor design to try to query without knowing if the table exists). It's ok if I end up including error messages as these will be skipped by the parser eventually.
I have something like this. If any of the views do not exist, everything fails and I get no results:
select col1 || col2 || ... from view1
union
select col1|| col2 || ... from view2
union
select name from view3
.
.
select ... from view30;
I can switch to PL/SQL if that helps. I don't have much experience with PL/SQL but I can use it if it can solve the problem. I did some reading on PL/SQL but could not figure out some straightforward way of doing this.
Any help is highly appreciated. Let me know if I can provide any other details.
Thanks.
Colleagues don't say that you CAN'T do that - they are saying you SHOULDN'T, cause of the fundamental problems. But the world isn't perfect - it often gives you a saw and tells you to hammer a nail. There is a solution to your problem - it involves dynamic SQL and some custom PL/SQL code. Simplified solution:
Create a package and a function:
function does_tbl_exists(p_tbl_name in varchar2) return number is
l_stmt varchar2(60);
begin
l_stmt := 'select count(*) from ' ||p_tbl_name;
execute immediate l_stmt;
return 1;
exception
when others then
return 0 ;
end;
Created a test table TEST_TBL with some data. And then we build up our SQL statement in a pl/sql block:
declare
l_count number;
l_stmt varchar2(400);
l_tab_name_1 varchar2(30) := 'TEST_TBL';
l_tab_name_2 varchar2(30) := 'TEST_TBL2';
begin
if test_pck.does_tbl_exists(p_tbl_name => l_tab_name_1) = 1 then
l_stmt := 'select count(*) count_number from ' || l_tab_name_1;
end if;
if test_pck.does_tbl_exists(p_tbl_name => l_tab_name_2) = 1 then
if l_stmt is not null then
l_stmt := l_stmt || ' union all' || chr(10);
end if;
l_stmt := l_stmt || 'select count(*) count_number from ' || l_tab_name_2;
end if;
if l_stmt is not null then
l_stmt := 'select sum(tmp.count_number) from (' || l_stmt || ') tmp';
dbms_output.put_line(l_stmt);
execute immediate l_stmt into l_count;
dbms_output.put_line('count: '||l_count);
else
dbms_output.put_line('Nothing todo.');
end if;
end;
By this template, you can build up your final SQL statement.
But use this as a last resort - I think you should still go to higher-ups and talk with them, that their current "requirement" is "bad" and they should feel "bad" (Insert meme here).

How to Refer to a Column by ID or Index Number

In Oracle PL/SQL, I have run a query and am trying to read through each column for each row one by one so I can concatenate them together with a delimiter (hard format requirement). The script is used on multiple tables of varying sizes, so the number of columns is not known in advance. I used
SELECT COUNT(column_name) INTO NumColumns FROM all_tabs_cols
WHERE table_name = Table_Array(i);
where Table_Array has already been defined. This is in the middle of a for loop and has successfully gotten me a total number of columns. Table_Cursor is a SELECT * statement. After this I am trying to do something like
FOR j IN 0..NumColumns-1 LOOP
FETCH TABLE_CURSOR.column(j) INTO DataValue;
DBMS_OUTPUT.PUT(DataValue || '/');
END LOOP
The above is pseudo code. It illustrates the concept I am after. I do not know PL/SQL well enough to know how to get a value like this out of a row. I am also worried about accidentally advancing the cursor while doing this. How can I accomplish this task?
You must use some form of dynamic SQL. Here is a quick example:
It builds the SQL statement that will select the '/' separated columns from the table you want. Then it uses dynamic SQL to run that SQL statement.
DECLARE
p_table_name VARCHAR2(30) := 'DBA_OBJECTS';
l_sql VARCHAR2(32000);
TYPE varchar2tab IS TABLE OF VARCHAR2(32000);
l_array varchar2tab;
BEGIN
SELECT 'SELECT ' || listagg(column_name,' ||''/''||') within group ( order by column_id ) || ' FROM ' || owner || '.' || table_name || ' WHERE ROWNUM <= 100'
INTO l_sql
FROM dba_tab_columns
where table_Name = 'DBA_OBJECTS'
group by owner, table_Name;
EXECUTE IMMEDIATE l_sql BULK COLLECT INTO l_array;
FOR i in l_array.first .. l_array.last LOOP
dbms_output.put_line(l_array(i));
END LOOP;
END;
This is how your code should look:
SELECT F1 || ', ' || F2 || ', ' || ... || ', ' || FN
FROM TABLE
NO LOOPS
Here is how you can generate code that does not use loops.
Note, if you want you can take out the where statement and generate the code for the whole database.
Test with just one table first.
SELECT 'SELECT '|| LISTAGG(COLUMN_NAME, ' || '', '' || ') || ' FROM '||TABLE_NAME as sql_stm
FROM ALL_TAB_COLUMNS
WHERE TABLE_NAME='tablename'
GROUP BY TABLE_NAME;

Get max(length(column)) for all columns in an Oracle table

I need to get the maximum length of data per each column in a bunch of tables. I'm okay with doing each table individually but I'm looking for a way to loop through all the columns in a table at least.
I'm currently using the below query to get max of each column-
select max(length(exampleColumnName))
from exampleSchema.exampleTableName;
I'm basically replacing the exampleColumnName with each column in a table.
I've already went through 3-4 threads but none of them were working for me either because they weren't for Oracle or they had more details that I required (and I couldn't pick the part I needed).
I'd prefer to have it in SQL than in PLSQL as I don't have any create privileges and won't be able to create any PLSQL objects.
Got the below query to work -
DECLARE
max_length INTEGER; --Declare a variable to store max length in.
v_owner VARCHAR2(255) :='exampleSchema'; -- Type the owner of the tables you are looking at
BEGIN
-- loop through column names in all_tab_columns for a given table
FOR t IN (SELECT table_name, column_name FROM all_tab_cols where owner=v_owner and table_name = 'exampleTableName') LOOP
EXECUTE IMMEDIATE
-- store maximum length of each looped column in max_length variable
'select nvl(max(length('||t.column_name||')),0) FROM '||t.table_name
INTO max_length;
IF max_length >= 0 THEN -- this isn't really necessary but just to ignore empty columns. nvl might work as well
dbms_output.put_line( t.table_name ||' '||t.column_name||' '||max_length ); --print the tableName, columnName and max length
END IF;
END LOOP;
END;
Do let me know if the comments explain it sufficiently, else I'll try to do better. Removing table_name = 'exampleTableName' might loop for all tables as well, but this is okay for me right now.
You can try this; although it uses PL/SQL it will work from within SQL-Plus. It doesn't loop. Hopefully you don't have so many columns that the SELECT query can't fit in 32,767 characters!
SET SERVEROUTPUT ON
DECLARE
v_sql VARCHAR2(32767);
v_result NUMBER;
BEGIN
SELECT 'SELECT GREATEST(' || column_list || ') FROM ' || table_name
INTO v_sql
FROM (
SELECT table_name, LISTAGG('MAX(LENGTH(' || column_name || '))', ',') WITHIN GROUP (ORDER BY NULL) AS column_list
FROM all_tab_columns
WHERE owner = 'EXAMPLE_SCHEMA'
AND table_name = 'EXAMPLE_TABLE'
GROUP BY table_name
);
EXECUTE IMMEDIATE v_sql INTO v_result;
DBMS_OUTPUT.PUT_LINE(v_result);
END;
/