MERGE Statment insert into from select pl sql - sql

How to create merge statment that insert into table from onther select statment
my example is :
MERGE INTO employees t
USING (SELECT :dept_id as dept_id FROM dual ) d
ON (t.dept_id = d.dept_id )
WHEN NOT MATCHED THEN
INSERT INTO employees (
ename,
fname )
SELECT
ename,
fname
FROM
trans_emps
where trans_id = :trans_id;

You can use INSERT INTO with a NOT EXISTS filter:
INSERT INTO employees (ename, fname)
SELECT ename, fname
FROM trans_emps
WHERE trans_id = :trans_id
AND NOT EXISTS( SELECT 1 FROM employees WHERE dept_id = :dept_id)
Or you can MERGE and move the SELECT .. FROM trans_emps into the USING clause:
MERGE INTO employees dst
USING (
SELECT ename, fname
FROM trans_emps
WHERE trans_id = :trans_id
) src
ON (dst.dept_id = :dept_id)
WHEN NOT MATCHED THEN
INSERT ( ename, fname )
VALUES ( src.ename, src.fname );

merge doesn't support such a syntax; insert clause can contain only values keyword (and list of values you're inserting into columns).
Switch to insert only (as your merge doesn't contain when matched clause anyway, so you aren't updating any rows in employees):
insert into employees (ename, fname)
select te.ename,
te.fname
from trans_emps te
where te.trans_id = :trans_id
and exists (select null
from employees a
where a.dept_id = :dept_id
);

Related

Use Output of Oracle SQL Function in WHERE condition of SQL query

I wrote a Oracle SQL Function that will return EMP IDs in the following format.
Query: select CUSFNC_GETEMPID('HIGH-RATED') from dual;
Output: ('1436','1444','1234')
I want to use this Output in SQL Query as follows:
SELECT ID, NAME, SAL
FROM EMP
WHERE ID IN CUSFNC_GETEMPID('HIGH-RATED')
I am expecting this query returns records with IDs equal to values mentioned in the above query.
But it is not working as expected.
How to achieve this by modifying output of function or any other way?
You could use a join instead of a IN clause
SELECT ID, NAME, SAL
FROM EMP
inner join (
select CUSFNC_GETEMPID('HIGH-RATED') hrated from dual;
) t on t.hrated = emp.id
and could be that your emp.id is a number so you should convert to char
SELECT ID, NAME, SAL
FROM EMP
inner join (
select CUSFNC_GETEMPID('HIGH-RATED') hrated from dual;
) t on t.hrated = to_char(emp.id)
You can use a PIPELINED TABLE FUNCTION, like this:
CREATE FUNCTION CUSFNC_GETEMPID(param1 varchar2) RETURN DBMSOUTPUT_LINESARRAY PIPELINED AS
BEGIN
FOR a_row IN
(
-- YOUR SQL WHICH RETURNS ROWS WITH ID VALUES HERE...
SELECT id
FROM
(
SELECT '1' id, 'HIGH-RATED' col FROM dual UNION
SELECT '2' id, 'HIGH-RATED' col FROM dual UNION
SELECT '3' id, 'LOW-RATED' col FROM dual
)
WHERE col = param1 -- FILTER BASED ON INPUT PARAM...
)
LOOP
PIPE ROW(a_row.id); -- PUT THE ID COLUMN HERE...
END LOOP;
END;
/
And then call it in your SQL like this:
SELECT ID, NAME, SAL
FROM EMP
WHERE ID IN (SELECT column_value FROM TABLE(CUSFNC_GETEMPID('HIGH-RATED')))
SELECT ID, NAME, SAL
FROM EMP
WHERE ID IN (SELECT CUSFNC_GETEMPID('HIGH-RATED')AS ID FROM DUAL)

SQL Get Duplicated based on all columns [duplicate]

This question already has answers here:
SQL query to find duplicate rows, in any table
(4 answers)
Closed 4 years ago.
SELECT FirstName, LastName, MobileNo, COUNT(1) as CNT
FROM CUSTOMER
GROUP BY FirstName, LastName, MobileNo;
Something like this will produce duplicates of the table Customer based on FirstName, LastName and MobileNo. However, I would like to produce a list of duplicates based on ALL columns (which are unknown). How would I accomplish this?
You could use checksum(*)
sqlfiddle.com/#!18/0a33d/4
Ex.
CREATE TABLE TEST_DATA
( Field1 VARCHAR(10),
Field2 VARCHAR(10)
);
INSERT INTO TEST_DATA VALUES ('1','1');
INSERT INTO TEST_DATA VALUES ('1','1');
INSERT INTO TEST_DATA VALUES ('2','2');
INSERT INTO TEST_DATA VALUES ('2','2');
INSERT INTO TEST_DATA VALUES ('2','2');
INSERT INTO TEST_DATA VALUES ('3','3');
SELECT TD1_CS.*
FROM (SELECT TD1.*,
CHECKSUM(*) CS1
FROM TEST_DATA TD1
) TD1_CS
INNER
JOIN (SELECT CHECKSUM(*) CS2
FROM TEST_DATA TD2
GROUP
BY CHECKSUM(*)
HAVING COUNT(*) > 1
) TD2_CS
ON TD1_CS.CS1 = TD2_CS.CS2
SELECT FirstName, LastName, MobileNo, COUNT(*)
FROM CUSTOMER
GROUP BY FirstName, LastName, MobileNo
HAVING COUNT(*) > 1

Order by clause with union in SQL select query

Is there a way to order the union of two select all statements based on table column values.
My sample code is:
SELECT * FROM emp WHERE mgr='7839'
UNION
SELECT * FROM emp WHERE NOT EXISTS (SELECT * FROM emp WHERE mgr='7839')
AND empno='7839'
order by ename;
This code is showing error as:invalid identifier 'ENAME'.
I am not using specific columns in select statement instead of * since there are more than 10 columns in the table and the code looks so big.
But ename is a column in emp.
will you consider small change in your query.
SELECT * FROM
(SELECT * FROM emp WHERE mgr='7839'
UNION
SELECT * FROM emp WHERE NOT EXISTS (SELECT * FROM emp WHERE mgr='7839')
AND empno='7839') AA
order by AA.ename;
Why would use use union for this? Just do:
SELECT e.*
FROM emp e
WHERE e.mgr = '7839' OR
(NOT EXISTS (SELECT 1 FROM emp e2 WHERE e2.mgr = '7839') AND
empno = '7839'
)
ORDER BY ename;

how to get second highest salary department wise without using analytical functions?

Suppose we have 3 employees in each department.we have total 3 departments . Below is the sample source table
Emp deptno salary
A 10 1000
B 10 2000
C 10 3000
D 20 7000
E 20 9000
F 20 8000
G 30 17000
H 30 15000
I 30 30000
Output
B 10 2000
F 20 8000
G 30 17000
With using analytic function dense_rank we can achive the second highest salary dept wise.
Can we achieve this without using ANY analytic function ???
Is Max() is also analytic function ??
It is a pain, but you can do it. The following query gets the second highest salary:
select t.deptno, max(t.salary) as maxs
from table t
where t.salary < (select max(salary)
from table t2
where t2.deptno = t.deptno
)
group by t.deptno;
You can then use this to get the employee:
select t.*
from table t join
(select t.deptno, max(t.salary) as maxs
from table t
where t.salary < (select max(salary)
from table t2
where t2.deptno = t.deptno
)
group by t.deptno
) tt
on t.deptno = tt.deptno and t.salary = tt.maxs;
Create table and insert dummy data
CREATE TABLE #Employee
(
Id Int,
Name NVARCHAR(10),
Sal int,
deptId int
)
INSERT INTO #Employee VALUES
(1, 'Ashish',1000,1),
(2,'Gayle',3000,1),
(3, 'Salman',2000,2),
(4,'Prem',44000,2)
Query to get result
;WITH cteRowNum AS (
SELECT *,
DENSE_RANK() OVER(PARTITION BY deptId ORDER BY Sal DESC) AS RowNum
FROM #Employee
)
SELECT *
FROM cteRowNum
WHERE RowNum = 2;
This will give you 2nd highest salary in each department:
SELECT a.Emp, a.deptno, a.salary
FROM Emp a
WHERE 1 = (SELECT COUNT(DISTINCT salary)
FROM Emp b
WHERE b.salary > a.salary AND a.deptno = b.deptno)
group by a.deptno
Solution using Correlated Subquery:
SELECT * FROM emp e1 WHERE 2 = (SELECT COUNT(DISTINCT sal)
FROM emp e2
WHERE e1.sal <= e2.sal
AND e1.deptno = e2.deptno
);
Quite straightforward and declarative, but slow
select
t1.*
from
#tmp t1
inner join #tmp h1 on h1.dept = t1.dept and h1.emp <> t1.emp
left outer join #tmp h2 on h2.dept = h1.dept and h2.salary > h1.salary
left outer join #tmp t2 on t1.dept = t2.dept and t2.salary > t1.salary and t2.emp <> h1.emp
where
t2.emp is null and h2.emp is null
You can find 2nd highest salary something like this:
select max(a.Salary),a.Deptno from Employee a join (select MAX(salary) salary
from Employee group by Deptno) b on a.Salary < b.salary group by a.Deptno
And no MAX() is not an analytic function.
Reference
On MySQL this how you can get second highest salary, given table name is salaries:
By Nested Queries: (where you can change offset 0/1/2 for first, second and third place respectively)
select
*
from
salaries as t1
where
t1.salary = (select
salary
from
salaries
where
salaries.deptno = t1.deptno ORDER by salary desc limit 1 offset 1);
or might be by creating rank: (where you can change rank= 1/2/3 for first, second and third place respectively)
SET #prev_value = NULL;
SET #rank_count = 0;
select * from
(SELECT
s.*,
CASE
WHEN #prev_value = deptno THEN #rank_count := #rank_count + 1
WHEN #prev_value := deptno THEN #rank_count := 1
ELSE #rank_count := 1
END as rank
FROM salaries s
ORDER BY deptno, salary desc) as t
having t.rank = 2;
SQL Query:
select TOP 2 max(salary),Emp from EMployee where deptno='your_detpno'
Very simple logic.
Please try:
SELECT dept as dd, ( SELECT ee.salary FROM `employees` as ee WHERE ee.dept=dd
ORDER BY ee.salary DESC LIMIT 1,1 ) as sndHigh
FROM `employees`
WHERE 1
GROUP BY dept
select min(salary),deptno from
(SELECT distinct top 2 salary,deptno from table ORDER BY salary DESC)
as a
group by deptno
CREATE TABLE Employee
([Name] varchar(1), [Dept] varchar(1), [Salary] int)
;
INSERT INTO Employee
([Name], [Dept], [Salary])
VALUES
('a', 'M', 20),
('b', 'M', 25),
('c', 'M', 30),
('d', 'C', 44),
('e', 'C', 45),
('f', 'C', 46),
('g', 'H', 20)

I want to make an insert using union all which has a column getting values from a sequence

I tried
INSERT INTO my_test_one (rollno,name, sirname, Dept)
(select rollno_seq.nextval,'name1','sirname1', Dept
FROM my_test_one_backup
WHERE dept = 500
UNION ALL
select rollno_seq.nextval,'name1','sirname1', Dept
FROM my_test_one_backup
WHERE dept = 501 );
While doing this I am getting the error
Error report:
SQL Error: ORA-02287: sequence number not allowed here
02287. 00000 - "sequence number not allowed here"
Don't use a UNION but a single SELECT and OR in this case:
SELECT rollno_seq.nextval,'name1','sirname1', Dept
FROM my_test_one_backup
WHERE dept = 500 OR dept = 501
Try:
INSERT INTO my_test_one
(rollno, name, sirname, Dept)
SELECT rollno_seq.nextval,
name1,
sirname1,
dept
FROM (select 'name1' as name1,'sirname1' as sirname1, Dept
FROM my_test_one_backup
WHERE dept = 500
UNION ALL
select 'name1','sirname1', Dept
FROM my_test_one_backup
WHERE dept = 501 );
Edit: Better still, use an OR like CodeBrickie says or and IN statement.
WHERE dept IN (500, 501);
Edit2:
Currently you are selecting 'name1', 'sirname1' as literals so each row returned will insert the next sequence number, 'name1', 'sirname1' and whatever the value of DEPT column is.
If your table has columns called name1 and sirname1 then you'll need to remove the single quotes (and you wouldn't need the column alias either) e.g.:
INSERT INTO my_test_one
(rollno, name, sirname, Dept)
SELECT rollno_seq.nextval,
name1,
sirname1,
dept
FROM (select name1, sirname1, Dept
FROM my_test_one_backup
WHERE dept = 500
UNION ALL
select name1, sirname1, Dept
FROM my_test_one_backup
WHERE dept = 501 );
Or
INSERT INTO my_test_one
(rollno, name, sirname, Dept)
SELECT rollno_seq.nextval,
name1,
sirname1,
dept
FROM my_test_one_backup
WHERE dept IN (500, 501);
You can't use a sequence in unioned selects, so you'll need to put the union in a sub-query and the sequence in the outer query:
INSERT INTO my_test_one (rollno,name, sirname, Dept)
select rollno_seq.nextval, name1, sirname1, dept
from (SELECT 'name1' as name1,'sirname1' as sirname1, Dept
FROM my_test_one_backup
WHERE dept = 500
UNION ALL
SELECT 'name1','sirname1', Dept
FROM my_test_one_backup
WHERE dept = 501 );
You should also note that, in SQL, double quotes indicate an object name and single quotes denote a string, so 'name1' and 'sirname1' will be static strings, not column references.