Oracle - Performing operation on each row of result set - sql

I need help with a query. The query returns a column of all the views in the database. My ultimate goal is to have the whole result set be one column containing all the views in the database, and the other column containing how many records/rows are present in each corresponding table.
This:
SELECT DISTINCT OWNER,
OBJECT_NAME
FROM DBA_OBJECTS
WHERE OBJECT_TYPE = 'VIEW'
AND OWNER = 'ADMIN'
returns the first column however I can't seem to find a way to combine it with :
select count(*) from view_X
to get the second column of the result set.
Any help would be appreciated.
Thanks

With some XML magic, this can be done with a single statement:
select object_name as view_name,
to_number(extractvalue(xmltype(dbms_xmlgen.getxml('select count(*) c from "'||owner||'"."'||object_name||'"')),'/ROWSET/ROW/C')) as row_count
from dba_objects
where object_type = 'VIEW'
and owner = 'ADMIN'
order by 1;

Ed Gibbs has provided a comprehensive answer.
I have the following solution for what you are looking for.
SET serveroutput ON;
DECLARE
x INTEGER;
BEGIN
FOR i IN (SELECT 'ADMIN' AS owner, object_name
FROM all_objects
WHERE object_type = 'VIEW'
AND owner = 'ADMIN') LOOP
EXECUTE IMMEDIATE ('SELECT count(*) FROM ' || i.object_name) INTO x;
dbms_output.put_line (i.owner || ' | ' || i.object_name || ' | ' || x);
END LOOP;
END;
/

This is a toughie. You can't join to the select count(*) from view_X or anything like that using straight SQL, so the best thing I can think of is a function that takes a view name and returns its count:
CREATE OR REPLACE FUNCTION ViewRowCount(viewName VARCHAR2) RETURN NUMBER
AS
rowCount NUMBER := 0;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ' || viewName INTO rowCount;
RETURN rowCount;
END;
/
Once the function is in place you can call it from your query:
SELECT DISTINCT OWNER,
OBJECT_NAME,
ViewRowCount(OBJECT_NAME)
FROM DBA_OBJECTS
WHERE OBJECT_TYPE = 'VIEW'
AND OWNER = 'ADMIN';
BTW, I don't think you need the DISTINCT for this query, but I don't have DBA access today so I can't be sure. The record counting will be slow enough as it is, so if there are duplicates before filtering with DISTINCT there will be a count for every duplicate row, making it even slower.
Also take a look at Rachcha's solution, which doesn't need to create a new object (the function) like mine does. If you'll be calling from a front-end you'll need to use something like my answer, but if you'll be calling from SQL*Plus Rachcha's will work very well.

Take at cursors in Oracle. They let you loop over a result set. Once you do that, you can dynamically execute sql of the form:
EXECUTE IMMEDIATE 'select ''' || OBJECT_NAME || ''' count(*) FROM ' || OBJECT_NAME'
for each view in your list.

Related

oracle - concatenate in select for drop, create, grant - lost in ''''

I am new to Oracle database, got a task to write several selects, but I do not get somethow principle, how it works (or better to say does not work). Could you please help me to find a mistake and probably you have a link to some info or converter that helps such writings.
v_sql := 'SELECT ''DROP TABLE ''||object_name||'' as
select *
FROM all_objects
WHERE object_name LIKE '''%''|| v_date ||''%'''
and object_type = ''TABLE''
and owner =''||v_owner||''';
Are you trying to generate DROP statements for a specific user? Start with the below anonymous block and add to it.
--Generate DROP statements for all tables for a user.
declare
v_owner varchar2(128) := user;
begin
for objects in
(
select distinct 'DROP TABLE "'||owner||'"."'||object_name||'"' v_sql
from all_objects
where object_type = 'TABLE'
and owner = v_owner
order by v_sql
) loop
dbms_output.put_line(objects.v_sql);
end loop;
end;
/

Get all ids in a database

I want to get all the ids in a database from all columns that have an ID field.
My script so far is:
BEGIN
FOR tname IN (select table_name from all_tab_columns where column_name = 'ID' and owner='PACC_USER') LOOP
EXECUTE IMMEDIATE
'select unique id from ' || tname;
END LOOP;
End;
I get the error PLS-00306: wrong number or types of arguments in call to '||'. What is the problem exactly? Any help is welcomed :)
In your code tname is a record for referencing the cursor result set i.e. a namespace not an attribute. Fix it like this:
BEGIN
FOR tname IN (select table_name
from all_tab_columns
where column_name = 'ID' and owner='PACC_USER')
LOOP
EXECUTE IMMEDIATE
'select unique id from ' || tname.table_name ;
END LOOP;
End;
One would hope that a column called ID would return unique rows without needing the unique keyword but we live in troubled times.
Your code needs to select results into something: PL/SQL is not T-SQL, it requires target variables. So let's improve your code a bit more.
declare
ids_nt sys.dbms-debug_vc2coll;
BEGIN
FOR tname IN (select table_name
from all_tab_columns
where column_name = 'ID' and owner='PACC_USER')
LOOP
EXECUTE IMMEDIATE
'select unique id from ' || tname.table_name
bulk collect into ids_nt;
dbms_output.put_line('IDS for table '|| tname.table_name);
for idx in ids_nt.first() .. ids_nt.last loop
dbms_output.put_line(ids_nt(idx));
end loop;
END LOOP;
End;
Maybe this isn't the kind of thing you want to do with the IDs. If so please edit your question to clarify your intent.
Select column_name,table name from all_tab_column where upper(column_name)=upper('Id') ;

Postgres - Run same query on all databases (same schemas)

hoping this is a pretty straightforward question.
I have a straightforward SELECT query (with a few sub-queries built in). I have over 40 DBs and I need to run this query for all DBs (all have same schema) and return the result in a big table.
I'm imagining a loop sequence (like with javascript's i=0; i < 40; i++) with a defined variable that will automatically stop once it's run all the DBs.
(I am working in Navicat, tho that probably doesn't matter)
Thank you!
In case someone needs a more involved example on how to do cross-database queries, here's an example that cleans up the databasechangeloglock table on every database that has it:
CREATE EXTENSION IF NOT EXISTS dblink;
DO
$$
DECLARE database_name TEXT;
DECLARE conn_template TEXT;
DECLARE conn_string TEXT;
DECLARE table_exists Boolean;
BEGIN
conn_template = 'user=myuser password=mypass dbname=';
FOR database_name IN
SELECT datname FROM pg_database
WHERE datistemplate = false
LOOP
conn_string = conn_template || database_name;
table_exists = (select table_exists_ from dblink(conn_string, '(select Count(*) > 0 from information_schema.tables where table_name = ''databasechangeloglock'')') as (table_exists_ Boolean));
IF table_exists THEN
perform dblink_exec(conn_string, 'delete from databasechangeloglock');
END IF;
END LOOP;
END
$$
It is possible to accomplish this in a single query using an Postgres' extension called dblink. This extension becomes available after you install postgresql-contrib package.
To be able to access it, you must add it in one of your databases.
CREATE EXTENSION IF NOT EXISTS dblink;
-- or
CREATE EXTENSION IF NOT EXISTS dblink WITH SCHEMA schema_name_here;
Using dblink function
dblink(conn_str, sql_query) you can execute dynamically generated SQL statements. The user you will use in the connection string matters, so, choose a user that can access all databases, schemas and tables involved.
As an example, the following SQL queries all databases for the table names from schemata table, in information_schema schema, filtering by columns named data_type.
select datname,
schema_name,
unnest(table_names) as table_name
from (select datname,
schema_name,
(select table_names
from dblink(
'dbname=' || datname || ' user=postgres password=postgres',
'select array_agg(table_name) as table_names from ' || schema_name || '.columns where column_name = ''data_type''')
as p(table_names character varying array)) as table_names
from (select datname,
unnest(schema_name_arr) as schema_name
from (select datname,
(select schema_name_arr
from dblink(
'dbname=' || datname || ' user=postgres password=postgres',
'select array_agg(distinct nspname) as schema_name_arr from pg_catalog.pg_namespace where nspname like ''information_schema''')
as t(schema_name_arr character varying array)) as schema_name_arr
from pg_catalog.pg_database
where datistemplate is false) q
where schema_name_arr is not null
) r
) s;
The main query here is this: select array_agg(table_name) as table_names from ' || schema_name || '.columns where column_name = ''data_type''.
Since dblink is being used in the SELECT clause, it is restricted to return only one column. That's why I'm using the combo ARRAY_AGG + UNNEST.
If you added dblink module into a schema, remember to use schema_name.dblink when calling that function.
Hope it helps. Happy coding! :)

SAP HANA execute query generated within a procedure

I am new with SAP HANA, I am trying to generate a query and execute it within a stored procedure.
I got an error and I am not sure that HANA can do something like that.
Here my code
CREATE PROCEDURE "PROCEDURE_IBA_TESTCSV"(
IN SCHEMA_NAME VARCHAR(100))
LANGUAGE SQLSCRIPT SQL SECURITY INVOKER AS
BEGIN
DECLARE T VARCHAR(1000);
DECLARE TA VARCHAR(1000);
SELECT
' SELECT ' || MAX(C_1) || IFNULL(MAX(C_2),'')|| IFNULL(MAX(C_3),'') ||' AS STATEMENT FROM ' || SCHEMA_NAME || '.' || TABLE_NAME || ' ' INTO T
FROM (
SELECT POSITION, DATA_TYPE_ID, COLUMN_NAME ,SCHEMA_NAME, TABLE_NAME ,
CASE WHEN POSITION = 1 THEN
' CASE WHEN ( '|| COLUMN_NAME ||' IS NULL ) THEN '''' ELSE REPLACE(TO_CHAR(' || COLUMN_NAME || '),''.'','','') END' END AS C_1,
CASE WHEN POSITION = 2 THEN '||''#''|| CASE WHEN ( '|| COLUMN_NAME ||' IS NULL ) THEN '''' ELSE TO_NVARCHAR('||COLUMN_NAME||') END' END AS C_2,
CASE WHEN POSITION = 3 THEN '||''#''|| CASE WHEN ( '|| COLUMN_NAME ||' IS NULL ) THEN '''' ELSE TO_NVARCHAR('||COLUMN_NAME||') END' END AS C_3
FROM (
select SCALE,SCHEMA_NAME,position,TABLE_NAME,column_name, data_type_id from TABLE_COLUMNS where
schema_name ='IMPORT_KT_STAMM_IK_348BA_20160706' AND TABLE_NAME='CLS_220_KTHISTORIE')) group by SCHEMA_NAME,TABLE_NAME;
execute immediate :T ;
INSERT INTO Test SELECT :T from DUMMY;
END;
With execute :T I get this output
I would like to store SUM(length) of this output into a variable within the same procedure.
Is that possible ? Any help ?
thanks in advance
Ok, now I understand, where this is going.
As you want to work with tables of different shape, you won't be able to avoid dynamic SQL altogether.
But since you always melt it into a single column, you could simply store that transformed data into, say a temporary table, and run the SUM(LENGTH(()) on that.
Not sure though why you want to go through this rather painful exercise, instead of simply exporting the data into some folder and checking the resulting size there.
I don't quite get why you use dynamic SQL here. With dynamic SQL (exec/execute immediate) you don't get access to the result set.
Alternatively you can use cursors.
You can provide parameters for SCHEMA_NAME and TABLE_NAME and be 'dynamic' that way.
I guess this question is related to SAP HANA getting csv data size right?
I modified my code:
I write "insert into Table SELECT " instead "SELECT" in line 8
and now I get data in table

Oracle SQL: Update with variable table name and fixed column name

I am a beginner with SQL and have a problem:
I have a DB with a big number of tables. Some of the tables have a column with the name "lab".
In this colums are values I need to be changed.
So I managed to get the names of the tables via
SELECT CNAME,TNAME FROM SYSTEM.COL WHERE CNAME = 'LAB';
And I know my update command
update TNAME set LAB='VALUE' WHERE LAB='OLDVALUE'
But I can not manage to connect both statements via a variable TNAME or something. I tried to use execute immediate, but that did me no good.
If its Oracle, something like this should do it:
BEGIN
FOR cur_tabs_cols IN ( SELECT CNAME,TNAME FROM SYSTEM.COL WHERE CNAME = 'LAB'; )
LOOP
EXECUTE IMMEDIATE 'UPDATE ' || cur_tabs_cols.TNAME || ' SET LAB = ''VALUE'' WHERE LAB = ''OLDVALUE''';
END LOOP;
COMMIT;
END;
You would need to write pl/sql for this.
The first thing is, please don't use SYSTEM.COL. Instead use the data dictionary view USER_TAB_COLS or USER_TAB_COLUMNS. (or ALL_TAB_COLS if in other schema)
EXECUTE IMMEDIATE would be what you want here.
BEGIN
FOR i IN (SELECT table_name
FROM user_tab_cols
WHERE column_name = 'LAB')
LOOP
EXECUTE IMMEDIATE
'UPDATE ' || i.table_name || ' set LAB = :value where LAB = :oldvalue'
USING 'value', 'oldvalue';
END LOOP;
END;
You can (and should) use a bind variable for value and oldvalue, just not for the table name.