Oracle: if TABLE_A exists, return count(*), else return 0; - sql

I want to use only one SQL statement, I have tried following one but failed:
SELECT decode(TABLE_COUNT, 0, 0, SELECT COUNT(*) FROM TABLE_A) FROM
(
SELECT COUNT(*) AS TABLE_COUNT FROM USER_TABLES WHERE TABLE_NAME = 'TABLE_A'
)

An example of the dbms_xmlgen approach that #RaymondNijland referred to in a comment:
create table table_a (id) as select level from dual connect by level <= 10;
select nvl(max(to_number(
xmlquery('/ROWSET/ROW/C/text()'
passing xmltype(dbms_xmlgen.getxml('select count(*) as c from ' || table_name))
returning content)
)), 0) as count
from user_tables
where table_name = 'TABLE_A';
COUNT
----------
10
drop table table_a purge;
select nvl(max(to_number(
xmlquery('/ROWSET/ROW/C/text()'
passing xmltype(dbms_xmlgen.getxml('select count(*) as c from ' || table_name))
returning content)
)), 0) as count
from user_tables
where table_name = 'TABLE_A';
COUNT
----------
0
You can easily extend this to query multiple tables at once, or all tables in a schema, etc., by changing the filter and adding a group-by clause.

The WITH clause may be of use in this case if the feature is available in the version of Oracle you are using. The following is what the code would look like.
NOTE: This is a SELECT statement even though it looks like PL/SQL code. The WITH clause supports the use of a PL/SQL declaration within it.
WITH
FUNCTION getCount(p_table_name IN VARCHAR2)
RETURN NUMBER IS
v_count NUMBER;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM '||p_table_name INTO v_count;
RETURN mycount;
EXCEPTION
WHEN OTHERS THEN
RETURN 0;
END;
SELECT getCount('hr.employee') FROM DUAL;
The WHEN OTHERS traps the error when the table does not exist and returns 0. If the table exists, it returns the count from the table.
Hope this works for you.

Related

How to merge two tables with different column number in Snowflake?

I am querying TABLE_SCHEMA,TABLE_NAME,CREATED,LAST_ALTERED columns from Snowflake information schema. VIEWS. Next, I would like to MERGE that table with row count for the view. Below are my queries I am running in Snowflake my issue is I am not sure how to combine these two table in 1 table ?
Note: I am new to Snowflake. Please provide code with explanation.
Thanks in advance for help!
Query 1
SELECT TABLE_SCHEMA,TABLE_NAME,CREATED,LAST_ALTERED FROM DB.SCHEMA.VIEWS
WHERE TABLE_SCHEMA="MY_SHEMA" AND TABLE_NAME IN ('VIEW_TABLE1','VIEW_TABLE2','VIEW_TABLE3')
Query 2
SELECT COUNT(*) FROM DB.SCHEMA.VIEW_TABLE1
UNION ALL SELECT COUNT(*) FROM DB.SCHEMA.VIEW_TABLE2
To get result of the COUNT(*) needs to be built dynamically and attached to the "driving query".
Sample data:
CREATE VIEW VIEW_TABLE1(c)
AS
SELECT 1;
CREATE VIEW VIEW_TABLE2(e)
AS
SELECT 2 UNION ALL SELECT 4;
CREATE VIEW VIEW_TABLE3(f)
AS
SELECT 3;
Full query:
DECLARE
QUERY STRING;
RES RESULTSET;
BEGIN
SELECT
LISTAGG(
REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
$$SELECT '<TABLE_SCHEMA>' AS TABLE_SCHEMA,
'<TABLE_NAME>' AS TABLE_NAME,
'<CREATED>' AS CREATED,
'<LAST_ALTERED>' AS LAST_ALTERED,
COUNT(*) AS cnt
FROM <tab_name>
$$,
'<TABLE_SCHEMA>', v.TABLE_SCHEMA),
'<TABLE_NAME>', v.TABLE_NAME),
'<CREATED>', v.CREATED),
'<LAST_ALTERED>', v.LAST_ALTERED),
'<tab_name>', CONCAT_WS('.', v.table_catalog, v.table_schema, v.table_name)),
' UNION ALL ') WITHIN GROUP (ORDER BY CONCAT_WS('.', v.table_catalog, v.table_schema, v.table_name))
INTO :QUERY
FROM INFORMATION_SCHEMA.VIEWS v
WHERE TABLE_SCHEMA='PUBLIC'
AND TABLE_NAME IN ('VIEW_TABLE1','VIEW_TABLE2','VIEW_TABLE3');
RES := (EXECUTE IMMEDIATE :QUERY);
RETURN TABLE(RES);
END;
Output:
Rationale:
The ideal query would be(pseudocode):
SELECT TABLE_SCHEMA,TABLE_NAME,CREATED,LAST_ALTERED,
EVAL('SELECT COUNT(*) FROM ' || view_name) AS row_count
FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_SCHEMA='MY_SHEMA'
AND TABLE_NAME IN ('VIEW_TABLE1','VIEW_TABLE2','VIEW_TABLE3');
Such construct EVAL(dynamic query) at SELECT list does not exist as it would require building a query on the fly and execute per each row. Though for some RDBMSes are workaround like dbms_xmlgen.getxmltype
Include table/view names as string in your count(*) queries and then you can join.
Example below -
select * from
(SELECT TABLE_SCHEMA,TABLE_NAME,CREATED FROM information_schema.tables
WHERE TABLE_SCHEMA='PUBLIC' AND TABLE_NAME IN ('D1','D2')) t1
left join
(
SELECT 'D1' table_name, COUNT(*) FROM d1
UNION ALL SELECT 'D2',COUNT(*) FROM d2) t2
on t1.table_name = t2.table_name ;
TABLE_SCHEMA
TABLE_NAME
CREATED
TABLE_NAME
COUNT(*)
PUBLIC
D1
2022-04-06 14:24:56.224 -0700
D1
12
PUBLIC
D2
2022-04-06 14:25:27.276 -0700
D2
5

Query to count all rows in all Snowflake views

I'm trying to get an count of all the rows in a set of views in my Snowflake database.
The built-in row_count from information_schema.tables is not present in information_schema.views, unfortunately.
It seems I'd need to count all rows in each view, something like:
with view_name as (select table_name
from account_usage.views
where table_schema = 'ACCESS' and RIGHT(table_name,7) = 'CURRENT'
)
select count (*) from view_name;
But that returns only one results, instead of one for each line
If I change the select to include the view name, i.e.
select concat('Rows in ', view_name), count (*) from view_name;
…it returns the error "invalid identifier 'VIEW_NAME' (line 5)"
How can I show all results and include the view name?
You can create a query that looks at the information_schema to create a query that will go view by view getting its count:
select listagg(xx, ' union all ')
from (
select 'select count(*) c, \'' || x || '\' v from ' || x as xx
from (
select TABLE_CATALOG ||'.'|| TABLE_SCHEMA ||'."'||TABLE_NAME||'"' x
from KNOEMA_FORECAST_DATA_ATLAS.INFORMATION_SCHEMA.VIEWS
where table_schema='FORECAST'
)
)
See also How to find the number of rows for all views in a schema?

Check SQL SYS_REFCURSOR without changing the cursor position

I have a requirement to check if a cursor was able to get some rows from table A. If yes, then do nothing else pull rows from table B.
Currently there are two stored procedures for now.
I am trying to do this in one stored procedure.
I tried using %ROWCOUNT but it doesn't work(because it will return 0) without changing the location of the cursor.
The issue is that my output is the cursor so I don't want to make any changes to.
If I do a fetch, then it shows error also that the return type has changed.
Any idea how to do this, like even if create a copy the cursor so that the fetch and row count could be done on the copy instead of the output cursor.
Pseudo Example
create or replace PROCEDURE "proc"
(
output OUT SYS_REFCURSOR,
)
.
BEGIN
.
.
OPEN output for select * from A
END
BEGIN
//check if output was empty then
OPEN output for select * from B
.
.
END
Update:
I did as suggested
....
BEGIN
...
OPEN output for
with
A as (select ...),
,B as (select ...),
,C as (select ...),
,D as (select ...)
select * from A
union
select * from B where not exists(select null from A)
union
select * from C where not exists(select null from B)
union
select * from D where not exists(select null from C)
END;
Since I know for sure that either one these tables will have data, I also tried the below
....
BEGIN
...
OPEN output for
with
A as (select ...)
,B as (select ...)
,C as (select ...),
,D as (select ...)
select * from A
union
select * from B
union
select * from C
union
select * from D
END;
But it gives me error now that
Error(64,7): PL/SQL: ORA-01789: query block has incorrect number of result columns
The table structure foe these 4 is diff. So they might return diff columns.
Would join make sense if 3 out of 4 are empty?
It's much better to implement your requirement in the same cursor, because it will use the same point-in-time read consistency: in your approach second open opens cursor for a different time than your first cursor and really data in A and B can change already.
This approach is better:
create or replace PROCEDURE "proc"( output OUT SYS_REFCURSOR,...)
....
BEGIN
...
OPEN output for
with
A as (select ...)
,B as (select ...)
select * from A
union all
select * from B where not exists(select null from A)
END;
Another possible solution is to create pipelined table instead, like:
create or replace function ... return {collection type} PIPELINED as
...flag boolean := true;
begin
....
for i in (select * from A) loop
flag:=false;
pipe row(...)
end loop;
if flag then
for i in (select * from B) loop
flag:=false;
pipe row(...)
end loop;
end if;
end;
/
But as you can see both query are opened at different time too.

Select Query in if else in Postgres Sql

something like ths
if(1=1)
select * from Table_a
else
slect * from Table_b
without using functions
I am trying something like this
DO $$
DECLARE
a integer := 10;
b integer := 20;
BEGIN
IF a >b THEN
select * from online.fandi_workflow_options ;
else
select * from online.credit_workflow_options ;
END IF;
END
$$;
Can anyone help me here
select * from online.fandi_workflow_options
where a > b
union
select * from online.credit_workflow_options
where a <= b
You can usually replace a logical "if" with a "where" clause; in your case, you're selecting from two different tables, so you have to use a union. This query only works if both tables have the same columns - if not, you can select explicit column names, and add "bogus" columns to each select statement to make them identical.

Oracle SQL : Retrieving non-existing values from IN clause

Having following query:
select table_name
from user_tables
where table_name in ('A','B','C','D','E','F');
Assuming only user_tables records B,C, and F exist, I want to retrieve the non-existing values A,D and E. This is a simple example, on real world the list can be huge.
A good way to generate fake rows is with a standard collection such as sys.odcivarchar2list:
select
tables_to_check.table_name,
case when user_tables.table_name is null then 'No' else 'Yes'end table_exists
from
(
select column_value table_name
from table(sys.odcivarchar2list('does not exist', 'TEST1'))
) tables_to_check
left join user_tables
on tables_to_check.table_name = user_tables.table_name
order by tables_to_check.table_name;
TABLE_NAME TABLE_EXISTS
---------- ------------
TEST1 Yes
does not exist No
if you have list of all those tables to be checked in Table1 then you can use NOT EXISTS clause
select name
from Table1 T1
where not exists ( select 1 from
user_tables U
where T1.name = U.table_name)
Only way is to use NOT EXISTS by converting the IN clause String into a Table of values.(CTE)
This is not a clean solution though. As The maximum length of IN clause expression is going to be 4000 only, including the commas..
WITH MY_STRING(str) AS
(
SELECT q'#'A','B','C','D','E','F'#' FROM DUAL
),
VALUES_TABLE AS
(
SELECT TRIM(BOTH '''' FROM REGEXP_SUBSTR(str,'[^,]+',1,level)) as table_name FROM MY_STRING
CONNECT BY LEVEL <= REGEXP_COUNT(str,',')
)
SELECT ME.* FROM VALUES_TABLE ME
WHERE NOT EXISTS
(SELECT 'X' FROM user_tables u
WHERE u.table_name = ME.table_name);
You can't. These values have to be entered into a temporary table at the least to do the desired operation. Also Oracle's IN clause list cannot be huge (i.e, not more than 1000 values).
Are you restricted to receiving those values as a comma delimited list?
instead of creating a comma delimited list with the source values, populate an array (or a table).
pass the array into a pl/sql procedure (or pull a cursor from the table).
loop through the array(cursor) and use a dynamic cusror to select count(table_name) from user_tables where table_name = value_pulled.
insert into table B when count(table_name) = 0.
then you can select all from table B
select * from tab1;
------------------
A
B
C
D
E
F
Create or replace procedure proc1 as
cursor c is select col1 from tab1;
r tab1.col1%type;
i number;
begin
open c;
loop
fetch c into r;
exit when c%notfound;
select count(tname) into i from tab where tname = r;
if i = 0 then
v_sql := 'insert into tab2 values ('''||r||''');
execute immediate v_sql;
commit;
end if;
end loop;
close c;
end proc1;
select * from tab2;
------------------
A
D
E
if this is not a one-off, then having this proc on hand will be handy.