Oracle PL/SQL Variable in HAVING clause - oracle

I'm having some troubles with putting a variable in a HAVING clause, the problem is that I need COUNT(*) to be greater than the variable.
I'm using ORACLE Database XE 11.2
DECLARE
cnt1 NUMBER;
cnt2 NUMBER;
res NUMBER;
BEGIN
SELECT COUNT(*)
INTO cnt1
FROM BESTELLING;
SELECT COUNT(*)
INTO cnt2
FROM ARTIKEL;
res := cnt1 / cnt2;
END;
/
SELECT A.Naam, COUNT(*) AS HOEVEEL_VERKOCHT
FROM Artikel A, Winkelwagen W
WHERE A.Artikel_ID = W.Artikel_ID
AND W.Datum_Besteld IS NOT NULL
GROUP BY A.Naam
HAVING COUNT(*) > ?res?
ORDER BY COUNT(*) DESC;

Place queries from PL/SQL directly in your main query, as subqueries:
.......
HAVING COUNT(*) >
( SELECT COUNT(*) FROM BESTELLING ) /
( SELECT COUNT(*) FROM ARTIKEL )
ORDER BY COUNT(*) DESC;

such way wont work ?
DECLARE
cnt1 NUMBER;
cnt2 NUMBER;
result NUMBER;
BEGIN
SELECT COUNT(*)
INTO cnt1
FROM BESTELLING;
SELECT COUNT(*)
INTO cnt2
FROM ARTIKEL;
result := cnt1 / cnt2;
SELECT A.Naam, res, COUNT(*) AS HOEVEEL_VERKOCHT
FROM Artikel A, Winkelwagen W
WHERE A.Artikel_ID = W.Artikel_ID
AND W.Datum_Besteld IS NOT NULL
GROUP BY A.Naam, res
HAVING COUNT(*) > result
ORDER BY COUNT(*) DESC;
END;
/
I change variable res to result , because it seems you have column called res

Related

SELECT records only once within a stored procedure and operate on selected records

I am selecting same set of records twice, once to return IN REF_CURSOR and then selecting same records to build a CSV so that i can update all records in IN clause .... Can i some how change my procedure to SELECT only once instead of selecting same records twice
PROCEDURE LOADBATCH(
inBUCKET_SIZE IN NUMBER,
OUTCURSOR OUT REF_CURSOR )
AS
V_HANDLE VARCHAR2(2000);
V_LOCK_RESULT INTEGER;
IDs VARCHAR2(2000);
BEGIN
BEGIN
V_HANDLE := GET_LOCK_HANDLE('BATCH');
V_LOCK_RESULT := DBMS_LOCK.REQUEST(V_HANDLE, TIMEOUT => 1);
DBMS_OUTPUT.PUT_LINE(V_LOCK_RESULT);
IF V_LOCK_RESULT <> 1 THEN
OPEN OUTCURSOR FOR SELECT BATCH_ID,INSTRUCTION_ID,INSTRUCTION_DUMP,BATCH_MSG_TYPE,BATCH_AMOUNT,BATCH_CURRENCY,RECIEVED_DATETIME,MODIFIED_DATETIME,SETTLEMENT_DATE,BATCH_STATUS,FROM_MMBID,BATCH_DATE,MODIFICATION_DATETIME,PARENTBATCH_ID,INSTR_REASON FROM
( SELECT DISTINCT BI.*,
BM.*,
BM.AMOUNT AS BATCH_AMOUNT,
BM.CURRENCY AS BATCH_CURRENCY,
BI.PARENT_BATCH_ID AS PARENTBATCH_ID,
BI.REASON AS INSTR_REASON
FROM ACT.BATCH_INSTRUCTIONS BI
INNER JOIN ACT.BATCH_MESSAGES BM
ON BI.BATCH_ID =BM.ID
WHERE (BI.STAGE = 'NEW'
OR (BI.STAGE = 'PICKED'
AND (SYSDATE > (BI.LAST_PICKED_AT + interval '65' second))))
AND (BM.STAGE <> 'COMPLETED')
ORDER BY LAST_PICKED_AT ASC
) WHERE ROWNUM <=inBUCKET_SIZE ;
SELECT listagg(INSTRUCTION_ID, ',') WITHIN GROUP (
ORDER BY INSTRUCTION_ID) AS concatenation
INTO IDs
FROM
(SELECT DISTINCT *
FROM ACT.BATCH_INSTRUCTIONS BI
INNER JOIN ACT.BATCH_MESSAGES BM
ON BI.BATCH_ID =BM.ID
WHERE (BI.STAGE = 'NEW'
OR (BI.STAGE = 'PICKED'
AND (SYSDATE > (BI.LAST_PICKED_AT + interval '65' second)))
)
AND (BM.STAGE <> 'COMPLETED')
ORDER BY LAST_PICKED_AT ASC
)
WHERE ROWNUM <=inBUCKET_SIZE ;
DBMS_OUTPUT.PUT_LINE('IDs are:');
DBMS_OUTPUT.PUT_LINE(IDs);
IF( IDs IS NOT NULL) THEN
UPDATE ACT.BATCH_INSTRUCTIONS
SET LAST_PICKED_AT =sysdate,
STAGE = 'PICKED'
WHERE INSTRUCTION_ID IN
(SELECT INSTRUCTION_ID
FROM ACT.BATCH_INSTRUCTIONS
WHERE INSTRUCTION_ID IN
(SELECT regexp_substr(IDs,'[^,]+', 1, level)
FROM dual
CONNECT BY regexp_substr(IDs, '[^,]+', 1, LEVEL) IS NOT NULL
)
);
COMMIT;
END IF;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
END;
-- DBMS_OUTPUT.PUT_LINE('releasing lock:');
V_LOCK_RESULT := DBMS_LOCK.RELEASE(V_HANDLE);
END LOADBATCH;
Wont work with one select because "select into" does not accept dynamic SQL nor cursors.

How to change alias to table name automatically in case of oracle?

We are currently analyzing hundreds of queries.
E.g
SELECT a.id,
a.name,
a.hobby,
b.desc
FROM tablename a,
table2 b
WHERE a.id = b.id;
or
SELECT id,
NAME,
hobby
FROM tablename;
above these can be change like below?
SELECT tablename.id,
tablename.name,
tablename.hobby,
table2.desc
FROM tablename tablename,
table2 table2
WHERE tablename.id = table2.id;
or
SELECT tablename.id,
tablename.name,
tablename.hobby
FROM tablename tablename;
Do you know any tools or methods that have the ability to change them?
Here is the Ananymous block for another query :
declare
v_query varchar2(4000) :='SELECT a.id,
a.name,
a.hobby,
b.desc
FROM tablename a,
table2 b
WHERE a.id = b.id';
tab_list varchar2(4000);
v_newquery varchar2(4000);
v_tab_alias varchar2(100);
v_tabname varchar2(500);
v_alias varchar2(40);
tab_cnt NUMBER :=0;
begin
-- table list
select replace(REGEXP_SUBSTR(regexp_replace(v_query,'FROM|WHERE','#'),'[^#]+',1,2)||chr(10),chr(10),null)
into tab_list
from dual;
-- no of tables
select REGEXP_COUNT(TRIM(REGEXP_SUBSTR(regexp_replace(v_query,'FROM|WHERE','#'),'[^#]+',1,2)),',')+1
into tab_cnt
from dual;
For i in 1..tab_cnt
LOOP
select TRIM(REGEXP_SUBSTR(tab_list,'[^,]+',1,i))
into v_tab_alias
from dual;
-- replace alias tablename for the column list
select TRIM(REGEXP_SUBSTR(v_tab_alias,'[^ ]+',1,1))
into v_tabname
from dual;
select TRIM(REGEXP_SUBSTR(v_tab_alias,'[^ ]+',1,2))
into v_alias
from dual;
select regexp_replace(v_query,v_alias||'\.',v_tabname||'.')
into v_newquery
from dual;
v_query:= v_newquery;
-- replace alias tablename in FROM clause
select regexp_replace(v_query,v_alias||'\,',v_tabname||',')
into v_query
from dual;
END LOOP;
-- replace alias last tablename before WHERE clause
select regexp_replace(v_query,v_tab_alias,v_tabname||' '||v_tabname)
into v_query
from dual;
DBMS_OUTPUT.PUT_LINE(v_query);
END;

query to find number of empty tables in my schema name HR

I want to find the number of empty tables in my schema hr and i have written a code for it:
set serveroutput on;
Declare
d Number;
c varchar2(25);
cursor c1 is SELECT DISTINCT OWNER, OBJECT_NAME FROM DBA_OBJECTS WHERE OBJECT_TYPE = 'TABLE' AND OWNER = 'HR';
Begin
for r in c1
Loop
select count(*) into d
from (r.object_name);
if (d = 0) then
dbms_output.put_line(r.object_name||'is Empty');
end if;
end loop;
end;
but its throwing error at this line from (r.object_name). can anyone help me in this?
Querying dba_tables.num_rows is not reliable as that is an estimate. If the table has never been analyzed, it might not reflect the correct row count.
Dynamic SQL is also not required for this. Based on my answer that calculates the row count for all tables, you can simply add a where condition:
select table_name
from (
select table_name,
to_number(extractvalue(xmltype(dbms_xmlgen.getxml('select count(*) c from '||owner||'.'||table_name)),'/ROWSET/ROW/C')) as row_count
from all_tables
where owner = 'HR'
)
where row_count = 0;
You need dynamic SQL (execute immediate) for that:
declare
d number;
v_sql varchar2(1000);
cursor c1 is select object_name
from all_objects
where object_type = 'TABLE' and owner = 'HR';
begin
for r in c1 loop
v_sql := 'select count(*) from HR.'||r.object_name;
execute immediate v_sql into d;
if d = 0 then
dbms_output.put_line(r.object_name||' is empty');
end if;
end loop;
end;
In Oracle , you can use below query.
select count(*) from dba_tables where OWNER = 'XXXX' and num_rows =0;

oracle read column names from select statement

I wonder how read column names in oracle. Ok, I know that there is table named USER_TAB_COLUMNS which gives info about it, but if I have 2 or 3 level nested query and I don't know column names. Or I just have simple query with join statement and i want to get column names. How to do that? any idey?
select * from person a
join person_details b where a.person_id = b.person_id
thanks
I would go for:
select 'select ' || LISTAGG(column_name , ',') within group (order by column_id) || ' from T1'
from user_tab_columns
where table_name = 'T1';
to get a query from database. To get columns with types to fill map you can use just:
select column_name , data_type
from user_tab_columns
where table_name = 'T1';
I assume you are looking for this:
DECLARE
sqlStr VARCHAR2(1000);
cur INTEGER;
columnCount INTEGER;
describeColumns DBMS_SQL.DESC_TAB2;
BEGIN
sqlStr := 'SELECT a.*, b.*, SYSDATE as "Customized column name"
FROM person a JOIN person_details b
WHERE a.person_id = b.person_id';
cur := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(cur, sqlStr, DBMS_SQL.NATIVE);
DBMS_SQL.DESCRIBE_COLUMNS2(cur, columnCount, describeColumns);
FOR i IN 1..columnCount LOOP
DBMS_OUTPUT.PUT_LINE ( describeColumns(i).COL_NAME );
END LOOP;
DBMS_SQL.CLOSE_CURSOR(cur);
END;

compare xtable count with table in oracle procedure

(I have several tables and for each table I have corresponding xtable.
what I am trying to do is create oracle procedure that will compare counts between xtable and table,
if (xtable >= table)
do something...
type namesarray IS VARRAY(5) OF VARCHAR2(10);
tbl_names namesarray;
xtbl_names namesarray;
total integer;
BEGIN
tbl_names := namesarray('tbl1', 'tbl2', 'tbl3', 'tbl4');
xtbl_names := namesarray('xtbl1', 'xtbl2', 'xtbl3', 'xtbl4');
total := tbl_names.count;
FOR i in (
SELECT COUNT(*) as TBL_COUNTS FROM tbl_names(i);
SELECT COUNT(*) as XTBL_COUNTS FROM xtbl_names(i)
)
LOOP
IF(i.XTBL_COUNTS >= i.TBL_COUNTS )
THEN
-- print to console
END IF;
END LOOP;
END;
This is all I got so far.
Can someone help?
How about this?
if (xtable >= table)
do something...
type namesarray IS VARRAY(5) OF VARCHAR2(10);
tbl_names namesarray;
xtbl_names namesarray;
total integer;
v_count_1 integer;
v_count_2 integer;
BEGIN
tbl_names := namesarray('tbl1', 'tbl2', 'tbl3', 'tbl4');
xtbl_names := namesarray('xtbl1', 'xtbl2', 'xtbl3', 'xtbl4');
total := tbl_names.count;
FOR i in (
select
t1.table_name as tab_1_name, t2.table_name as tab_2_name
from ( select table_name from user_tables where table_name in (...) ) t1
,( select 'x'||table_name as table_name from user_tables where table_name in (...) ) t2
where 'x'||t1.table_name = t2.table_name
)
LOOP
execute immediate 'select count(*) from '||i.tab_1_name into v_count_1;
execute immediate 'select count(*) from '||i.tab_2_name into v_count_2;
IF( v_count_1 >= v_count_2 )
THEN
-- print to console
END IF;
END LOOP;
END;

Resources