inserting in table before declaring cursor - sql

Can I use the insert statement with values coming from a select statement on another table in EXECUTE IMMEDIATE?
Like :
execute immediate('insert into table values(select rowid from table where column='NEW')');
if there is no possibility of this can I put an insert statement before the declaration of a cursor in a procedure.
thanks a lot.

Yes you can but you'd need to sort out your syntax and quotes:
execute immediate('insert into table (column_name) select rowid from table where column=''NEW''');
EDIT:
Better still, use a bind variable instead of the hard coded reference to the column name.

What results did you get when you tested it?
Your syntax is a little off, but, as you can see below, it's definitely doable.
SQL> CREATE TABLE emp2 AS SELECT * FROM emp WHERE 1 = 0;
Table created.
SQL> BEGIN
2 EXECUTE IMMEDIATE 'INSERT INTO emp2 (empno, ename, job) SELECT e.empno, e.ename, e.job FROM emp e WHERE e.deptno = 10';
3 END;
4 /
PL/SQL procedure successfully completed.
SQL> SELECT * FROM emp2;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------------------- ---------- ---------- ----------
7782 CLARK MANAGER
7839 KING PRESIDENT
7934 MILLER CLERK
SQL>

Related

SELECT STATEMENT WITH ORDER BY

I have a simple select statement with joins in oracle I have one agregate function like sum(COLUMN) , my query works fine and it return the result , Now I am adding one-two more columns from same table to which I have a join , I am requiring simple column values no agregate function when I add first column it ask me that it needed to be in group by as it is not agregate function I did that , but after doing that i am getting more number of rows which i were getting previously what could be the reason, when I add one more columns than again number of rows increased like with my first query without adding these two columns the result was 544 and now it is 766
select distinct 'carTypes' as type, mi.WOMENNAME as "mother name", mi.womencnic as "mother CNIC", '' as BFORMNO, '' as child_gender, mi.PROVINCE, mi.district, mi.tehsil, mi.HUSBANDNAME, mi.PHONENO contact, mi.address, SUM(p.amount) paid_amount
from MINFORMATION mi
inner join PAYMENTINFORMATION p on p.BENEFICIARYID=mi.WOMENID and p.BANKSTATUS='Successfull'
where mi.month IS NOT NULL
and p.generationdate between '24-JUN-21' and '01-JAN-22'
group by mi.WOMENNAME, mi.womencnic,mi.PROVINCE, mi.district, mi.tehsil, mi.HUSBANDNAME, mi.PHONENO , mi.address
above is my query
That's how it goes. When there's an aggregate function in the select column list all non-aggregated columns must be included in the group by clause (group by, not order by as your title suggests; order by is irrelevant in this matter).
For example, as of Scott's emp table: if you want to compute sum of salaries, the result is only one row:
SQL> select sum(sal)
2 from emp;
SUM(SAL)
----------
29025
If you add deptno column, it means you want to compute sum of salaries per each department. As there are employees from 3 departments, it results in 3 rows:
SQL> select deptno, sum(sal)
2 from emp
3 group by deptno;
DEPTNO SUM(SAL)
---------- ----------
30 9400
20 10875
10 8750
Adding yet another column - job - means that you want to calculate sum of salaries per each department, and per each job within that department; all that increases number of resulting rows:
SQL> select deptno, job, sum(sal)
2 from emp
3 group by deptno, job
4 order by deptno, job;
DEPTNO JOB SUM(SAL)
---------- --------- ----------
10 CLERK 1300
10 MANAGER 2450
10 PRESIDENT 5000
20 ANALYST 6000
20 CLERK 1900
20 MANAGER 2975
30 CLERK 950
30 MANAGER 2850
30 SALESMAN 5600
9 rows selected.
Therefore, what kind of output would you want to get? If you could explain it, maybe we could suggest approach that would make it happen.

Oracle sqlplus functions shows as ###

After submitting a select query, which contains functions such as row_number() or any other function the result is ####. If I use rownum as something, it shows ### if I use just rownum everything is ok.
I had this problem with other generic columns but I could simply use column columnname format 9999;
The problem is not that there is not enough space, there is only one digit per record.
I have googled all over the internet and nothing has answered my problem yet.
Does anyone know what could be the problem with functions and how to format them?
select ROW_NUMBER() OVER (ORDER BY student_surname) "Nr.",
student_surname || ' ' || student_name "Student",
NVL(sum(m.stipend),0) + NVL(sum(m.compensation),0) "Total"
from student s
inner join money m on m.student_id=s.id_student
group by student_surname, student_name;
Have tried clear columns, did not work.
It looks as if you formatted numeric column with a wrong format, which doesn't allow all digits to be displayed.
For example, this is someone's salary - looks OK:
SQL> select sal from emp where rownum = 1;
SAL
----------
920
But, if you formatted it as follows (i.e. saying that I want to have only two digits (99) for that column), the result is ###:
SQL> col sal format 99
SQL> select sal from emp where rownum = 1;
SAL
---
###
SQL>
What to do? The simplest way is to clear formatting:
SQL> col sal clear
SQL> select sal from emp where rownum = 1;
SAL
----------
920
SQL>
Another possibility is that numformat (in general) is wrongly set; all these columns are NUMBER, and all of them are affected with this set numformat:
SQL> set numformat 9
SQL> select empno, mgr, sal, deptno from emp where rownum = 1;
EMPNO MGR SAL DEPTNO
---------- ---------- ---------- ----------
########## ########## ########## ##########
How to fix that? Exit SQL*Plus and reconnect.

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.

How to remove duplicate column results, and include a count of number of columns in each table?

some quick questions.
I have this query:
SELECT u.Table_Name, u.Column_Name
FROM User_Tab_Columns u;
Which gives me these results:
TABLE_NAME COLUMN_NAME
------------------------------ -----------
BONUS ENAME
BONUS JOB
BONUS SAL
BONUS COMM
DEPT DEPTNO
DEPT DNAME
DEPT LOC
DUMMY DUMMY
EMP EMPNO
EMP ENAME
EMP JOB
EMP MGR
EMP HIREDATE
EMP SAL
EMP COMM
EMP DEPTNO
SALGRADE GRADE
SALGRADE LOSAL
SALGRADE HISAL
I'm unsure how to make it so that the table_name results do not repeat (so one of each result) and to have the number of columns that each table has in place of the column_name.
Thanks in advance for any help.
Using the GROUP BY clause, you can group your tables so that you can run an aggregate function, in this case COUNT, on the columns you wish to count.
SELECT u.Table_Name, COUNT(1) AS Column_Count FROM User_Tab_Columns u GROUP BY u.Table_Name

Can you use multiple columns for a not in query?

I recently saw someone post this as part of an answer to an SO query question:
SELECT DISTINCT a, b, c
FROM t1
WHERE (a,b,c) NOT IN
( SELECT DISTINCT a,b,c FROM t2 )
I'm a bit confused, as I always thought that you can't use multiple columns for "NOT IN" ("where(a,b,c)", etc.). Is this correct SQL syntax? And how about MySQL?
Googling it suggests that it will work on some databases but not others. You can use this instead:
SELECT DISTINCT a, b, c
FROM t1
WHERE NOT EXISTS
(SELECT 1 FROM t2
WHERE t1.a = t2.a AND t1.b = t2.b AND t1.c = t2.c)
It's a SQL extension. Oracle, PostgreSQL and MySQL have it. SQL Server 2005 does not have it. I'm not sure about others.
It certainly does work in Oracle. Quick contrived example:
SQL> select ename, job, deptno from emp
2 where (ename, deptno) in
3 ( select ename, deptno from emp
4 where job = 'MANAGER'
5 );
ENAME JOB DEPTNO
---------- --------- ----------
JONES MANAGER 20
CLARK MANAGER 10
PARAG MANAGER 30
This also works:
SQL> select ename, job, deptno from emp
2 where (ename, deptno) in (('JONES',20),('CLARK',10));
ENAME JOB DEPTNO
---------- --------- ----------
JONES MANAGER 20
CLARK MANAGER 10
NOT IN too:
SQL> select ename, job, deptno from emp
2 where (ename, deptno) not in
3 ( select ename, deptno from emp
4 where job = 'MANAGER'
5 );
ENAME JOB DEPTNO
---------- --------- ----------
SMITH CLEANER 99
SCOTT ANALYST 20
KING PRESIDENT 10
FORD ANALYST 20
MILLER CLERK 10
Not that I'm aware of, but if thy're character type (or can be converted to char types), you can fake it:
SELECT DISTINCT a, b, c
FROM t1
WHERE a+b+c NOT IN
( SELECT DISTINCT a+b+c FROM t2 )
Try this
SELECT DISTINCT a, b, c
FROM t1,
(SELECT DISTINCT a,b,c FROM t2) as tt
WHERE t1.a NOT IN tt.a
AND t1.b NOT IN tt.b
AND t1.c NOT IN tt.c
Note: This has not been tested, it hasn't even been proven correct.
Others have already answered the question, but as a performance suggestion, if you're dealing with data of any significant size always use the EXISTS statement rather than IN. It will be faster in almost every case.
http://decipherinfosys.wordpress.com/2007/01/21/32/