If count(column) Then Select in Oracle - sql

can I do something like this in oracle?
I want to check if the result dataset is more than 20k records then DO NOT SELECT, if less than 20k then SELECT.
DECLARE cnt number;
BEGIN
select count(*) into cnt from ir_mpn where material LIKE '%A00%';
IF cnt > 20000 THEN
select manuf_part, material, vendor, vendor_name from ir_mpn where material LIKE '%A00%';
END IF;
END;
I tried to do like this but shows error:
ORA-06550: line 6, column 4:
PLS-00428: an INTO clause is expected in this SELECT statement
Any better suggestions for me to do this?

This is PL/SQL so your SELECT statement requires an INTO clause. As you're about to return thousands of rows, you can't return them into scalar variables but e.g. collection or ref cursor.
However, if you want to do it in SQL, then see if this helps; example is based on Scott's DEPT table:
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------------- --------------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
I'd want to select rows from it if number of rows is less than 3; as number of rows is 4, nothing is returned:
SQL> select * from dept
2 where 3 >= (select count(*) from dept);
no rows selected
How about 5 rows?
SQL> select * from dept
2 where 5 >= (select count(*) from dept);
DEPTNO DNAME LOC
---------- -------------------- --------------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
In your case:
select manuf_part, material, vendor, vendor_name
from ir_mpn
where material LIKE '%A00%'
and 20000 >= (select count(*)
from ir_mpn
where material like '%A00%'
);

Yes. Depends on what do you like to achieve.
In PL/SQL you cannot use plain SELECT to return rows.
Some options are to use INTO (like you did for COUNT()) or use cursor (like below).
The simple approach is:
for r_material in (select manuf_part, material, vendor, vendor_name from ir_mpn where material LIKE '%A00%') loop
-- process each r_material record here f.e.
-- if r_material.vendor = 'ORACLE' then...
end loop;

Related

In Oracle SQL developer, how can I find the number of rows in a certain table?

I just wanted to know how do I can I find the number of rows in a certain table.
Use the COUNT aggregation function:
SELECT COUNT(*) FROM table_name;
Apart from the obvious count function, if you regularly gather schema statistics, you can - at least approximately (if tables' contents change relatively frequently) - query user_tables and check the num_rows column's contents:
SQL> exec dbms_stats.gather_schema_stats('SCOTT');
PL/SQL procedure successfully completed.
SQL> select table_name, num_rows from user_tables;
TABLE_NAME NUM_ROWS
-------------------- ----------
EMP 14
DEPT 4
BONUS 0
SALGRADE 5
INVOICE 4
ERRORS 110
DAT 0
<snip>
Is that really so? For example:
SQL> select count(*) from emp;
COUNT(*)
----------
14
SQL> select count(*) from dept;
COUNT(*)
----------
4
SQL> select count(*) from bonus;
COUNT(*)
----------
0
SQL> select count(*) from errors;
COUNT(*)
----------
110
SQL>

sql Group by does not work on selecting multiple columns

I have following query
SELECT ACTIONFINANCIALTRANSACTIONLOG.ID_CSE,
ACTIONFINANCIALTRANSACTIONLOG.DTE_PROC
FROM CSESDEV02.FINANCIAL_TRAN_LOG ACTIONFINANCIALTRANSACTIONLOG
GROUP BY ACTIONFINANCIALTRANSACTIONLOG.ID_CSE
HAVING COUNT(TXT_DESC) > 9
this results in
ORA-00979: not a GROUP BY expression
00979. 00000 - "not a GROUP BY expression"
*Cause:
*Action: Error at Line: 57 Column: 47
however, if i select only 1 column , it works
SELECT ACTIONFINANCIALTRANSACTIONLOG.ID_CSE
FROM CSESDEV02.FINANCIAL_TRAN_LOG ACTIONFINANCIALTRANSACTIONLOG
GROUP BY ACTIONFINANCIALTRANSACTIONLOG.ID_CSE
HAVING COUNT(TXT_DESC) > 9
why is that?
Although there's no aggregate function in SELECT column list, all columns that aren't aggregated should be part of the group by clause.
If there are 2 columns you're selecting, both have to be part of the group by.
BTW, table alias should be short so that it "simplifies" query and makes it easier to read. You did the opposite.
This is what works OK - only one column in select and it is part of group by:
SQL> select deptno
2 from emp
3 group by deptno
4 having count(job) > 1;
DEPTNO
----------
30
20
10
This doesn't work: two columns in select list, only one in group by:
SQL> select deptno,
2 ename
3 from emp
4 group by deptno
5 having count(job) > 1;
ename
*
ERROR at line 2:
ORA-00979: not a GROUP BY expression
But, when they both are in group by, query is OK:
SQL> select deptno,
2 ename
3 from emp
4 group by deptno, ename
5 having count(job) > 1;
no rows selected
SQL>
Read documentation.
Gist:
SelectItems in the SelectExpression with a GROUP BY clause must
contain only aggregates or grouping columns.
This is happening because GROUP by require all the column defined in SELECT clause.
The SELECT statement used in the GROUP BY clause can only be used contain column names, aggregate functions, constants and expressions.
select count(1),deptno
from emp
group by deptno
having count(1) > 1;
ACTIONFINANCIALTRANSACTIONLOG.DTE_PROC is not the product of a summary function and is not mentioned in the GROUP_BY clause so you get the error you've gotten. Perhaps you could use:
SELECT ftl.ID_CSE,
ftl.DTE_PROC
FROM (SELECT af.ID_CSE,
COUNT(af.TXT_DESC) AS TXT_DESC_COUNT
FROM CSESDEV02.FINANCIAL_TRAN_LOG af
GROUP BY af.ID_CSE
WHERE COUNT(af.TXT_DESC) > 9) iq
INNER JOIN CSESDEV02.FINANCIAL_TRAN_LOG ftl
ON ftl.ID_CSE = iq.ID_CSE
Syntax aside, think about the aggregation group you are defining with your group by clause:
GROUP BY ID_CSE
means you want one row per ID_CSE. Examples would be average salary per department, total sales per region, vaccinations per country.
In that one row for each id_cse, what dte_proc should be displayed if there is more than one distinct value?
For example, say your data contains
ID_CSE DTE_PROC
------ ----------
1 X
1 Y
2 Z
Now if you query
select id_cse, date_proc, count(*) from financial_tran_log
group by id_cse
what results do you expect to see?
ID_CSE DTE_PROC COUNT(*)
------ -------- --------
1 ??? 2
2 Z 1
It can't be done, hence the syntax error.

What is mean the condition COUNT(1) = 1 in oracle sql

I have two table and write select:
SELECT A.code, B.account, A.ano
FROM atable A, dtable B
where A.ano=B.dno
group by A.code, B.account, A.ano
having count(1)=1;
if I add condition having count(1)=1 I didn't get any result, why?
Query groups data per columns listed in the GROUP BY clause. HAVING restricts result set to those whose count (for those grouped columns) equals 1.
If query returns nothing, it means that there's not at least one combination of those columns that has only one row as a result.
For example (based on Scott's EMP table):
SQL> select job, count(*) from emp
2 group by job;
JOB COUNT(*)
--------- ----------
CLERK 4
SALESMAN 4
PRESIDENT 1 --> only one person is a president
MANAGER 3
ANALYST 2
Your query:
SQL> select job
2 from emp
3 group by job
4 having count(*) = 1;
JOB
---------
PRESIDENT
Let's set someone else to be a president:
SQL> update emp set job = 'PRESIDENT' where ename = 'ADAMS';
1 row updated.
The query doesn't return anything now (as there are 2 presidents):
SQL> select job
2 from emp
3 group by job
4 having count(*) = 1;
no rows selected
SQL>
As of count(1): no point in it, use count(*) instead.

Get Unique column value along with other. Columns when using inner join

I have following query and I need unique value for column
Select unique(t.id), log.*
from tableA log
inner join tableT t on t.id1=log.id1
where log.time>=(somedate)
and log.time<(somedate)
and ref-id=20
and ref-id=30
and t.id not in (select unique t.id
from tableA log
inner join tableT t on t.id1=log.id1
where log.time>=(somedate)
and log.time<(somedate)
and ref-id=20
and ref-id=30);
I am not getting unique values for t.id.Can anyone please help ? I am using oracle data base
Remove LOG.* from the SELECT; UNIQUE (as well as DISTINCT) is applied to all columns you select, not only to the one you put into brackets.
[EDIT]
Scott's EMP table contains employees that work in different departments. List of distinct departments is:
SQL> select distinct deptno from emp;
DEPTNO
----------
30
20
10
SQL>
If you include additional columns, such as JOB, the list is changed to
SQL> select distinct deptno, job from emp order by deptno, job;
DEPTNO JOB
---------- ---------
10 CLERK
10 MANAGER
10 PRESIDENT
20 ANALYST
20 CLERK
20 MANAGER
30 CLERK
30 MANAGER
30 SALESMAN
9 rows selected.
SQL>
It is now distinct per DEPTNO and per JOB; you can't get only distinct DEPTNO in such a query.
[EDIT #2: aggregation]
If "distinct" means distinct DEPTNO and the first job for that DEPTNO, then you might need this:
SQL> select deptno, min(job) first_job from emp
2 group by deptno
3 order by deptno;
DEPTNO FIRST_JOB
---------- ---------
10 CLERK
20 ANALYST
30 CLERK
SQL>
[EDIT #3, example based on your query]
Select t.id,
min(log.id) log_id,
min(log.time) log_time,
<the rest of LOG table columns goes here, all of them with MIN>
from tableA log
inner join table ...
<the rest of your query goes here>
group by t.id;
How about something like this?
Select t.id, log.*
from tableA log inner join
(select t.*, row_number() over (partition by id1 order by id) as seqnum
from tableT t
) t
on t.id1 = log.id1
where log.time >= (somedate) and
log.time < (somedate) and
t.seqnum = 1
ref_id = 20 and ref_id = 30 -- this is ALWAYS false, but I'm ignoring that
Notes:
I have no idea what the subquery is supposed to be doing.
The conditions on ref_id will always evaluate to FALSE, so that logic is almost certainly not what you intend.
If ref_id is in the transaction table, then the conditions should be moved to the subquery.

ORACLE SQL retrieve n rows without subqueries or derived tables

I'm doing my SQL exercises but I got stuck in one. I need to retrieve the employees with the two highest salaries, but I can't use any type of subquery or derived table. I do it with a subquery like this:
SELECT *
FROM (SELECT * FROM emp ORDER BY sal DESC) new_emp
WHERE ROWNUM < 3;
I also know that this can be achieved using the WITH clause, but I'm wondering if there is any alternative to this.
PS: I'm using Oracle 11.
If you are Oracle version 12.1 or above you can use a row limiting clause. In your case you would just use the subquery plus the row limiting clause like so:
SELECT * FROM emp
ORDER BY sal DESC
FETCH FIRST 5 ROWS ONLY;
Source: https://oracle-base.com/articles/12c/row-limiting-clause-for-top-n-queries-12cr1#top-n
This is actually a pathetic method, in my opinion, but you can use a join:
select e.col1, e.col2, . . .
from emp e left join
emp e2
on e2.salary >= e.salary
group by e.col1, e.col2, . . .
having count(distinct e2.salary) <= 2;
Note: this is really equivalent to a dense_rank(), so if there are ties, you'll get more than two rows. It is easy enough to fix this (assuming you have a unique identifier for each row), but the fix complicates the logic and hides the basic idea.
A good exercise should help to prepare to solve practical problems. So the important thing in this one is not the usage of subquery but to realize that the two highes salaries can have hunderts of employees.
While using the #MT0 view workaround this is the query
CREATE VIEW sal_ordered_emps AS
SELECT e.*,
row_number() over (order by sal desc) as RN
FROM SCOTT.emp e
ORDER BY sal DESC;
select e.* from scott.emp e join
sal_ordered_emps soe on e.sal = soe.sal and rn <= 2
;
result as explained can be more than 2 records
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- ------------------- ---------- ---------- ----------
7788 SCOTT ANALYST 7566 19.04.1987 00:00:00 3000 20
7839 KING PRESIDENT 17.11.1981 00:00:00 5000 10
7902 FORD ANALYST 7566 03.12.1981 00:00:00 3000 20
This is cheating but... instead of using a subquery you can define a view:
CREATE VIEW sal_ordered_emps AS
SELECT *
FROM emp
ORDER BY sal DESC;
Then you can do:
SELECT * FROM sal_ordered_emps WHERE ROWNUM < 3;
Alternatively, you can do it with a pipelined function...
CREATE OR REPLACE PACKAGE emp_pkg
AS
TYPE emp_table IS TABLE OF EMP%ROWTYPE;
FUNCTION get_max_sals(
n INT
) RETURN emp_table PIPELINED;
END;
/
CREATE OR REPLACE PACKAGE BODY emp_pkg
AS
FUNCTION get_max_sals(
n INT
) RETURN emp_table PIPELINED
AS
cur SYS_REFCURSOR;
in_rec EMP%ROWTYPE;
i INT := 0;
BEGIN
OPEN cur FOR SELECT * FROM EMP ORDER BY SAL DESC;
LOOP
i := i + 1;
FETCH cur INTO in_rec;
EXIT WHEN cur%NOTFOUND OR i > n;
PIPE ROW(in_rec);
END LOOP;
CLOSE cur;
RETURN;
END;
END;
/
SELECT *
FROM TABLE( emp_pkg.get_max_sals( 2 ) );
This solution use no subquery, but requires three steps:
-- Q1
select max(sal) from scott.emp;
-- Q2
select max(sal) from scott.emp where sal < {result of Q1};
select * from scott.emp where sal in ({result of Q1},{result of Q2});
General you'll need N+1 queries to get top-N salaries emps.