Not sure if the this is the right place to ask but i have a question with beginner sql.
I have the table dept and emp which include:
SQL> desc dept;
Name Null? Type
----------------------------------------- -------- ----------------------
DEPTNO NUMBER(2)
DNAME VARCHAR2(14)
LOC NOT NULL VARCHAR2(13)
SQL> desc emp;
Name Null? Type
----------------------------------------- -------- ----------------------
EMPNO NUMBER(4)
ENAME VARCHAR2(10)
JOB VARCHAR2(9)
MGR NUMBER(4)
HIREDATE DATE
SAL NUMBER(7,2)
COMM NUMBER(7,2)
DEPTNO NUMBER(2)
I need to search for all jobs that are in department 30 and to Include the location of department 30.
What im trying is this:
SQL> select emp.job, dept.deptno, dept.loc
2 from emp, dept
3 where emp.deptno = dept.deptno
4 where deptno = '30';
where deptno = '30'
*
ERROR at line 4:
ORA-00933: SQL command not properly ended
But as you can see its not working and i have tried different variations but still no luck. Am i on the right track? How would I solve this?
It sounds like you want something like this. When you have multiple conditions in the where clause, you only specify where once and combine them with and or or conditions.
select emp.job, dept.deptno, dept.loc
from emp, dept
where emp.deptno = dept.deptno
and dept.deptno = 30;
Unless there is some reason that you really need to use the old join syntax, you probably ought to start with the SQL 99 syntax. It makes it much easier to move between databases, it makes your queries easier to read by separating join and filter conditions, and it makes life much easier when you start working on outer joins.
select emp.job, dept.deptno, dept.loc
from emp
join dept
on( emp.deptno = dept.deptno )
where dept.deptno = 30;
Related
I got two challenges on my college exam to do.
The first one is to Show data for employees (name, salary, and department) who are paid less than the average salary for their department.
I tried this:
SELECT ename, sal, deptno
FROM Emp
WHERE sal < (SELECT AVG(sal) FROM EMP)
GROUP BY deptno;
But i get the ORA-00979: not a GROUP BY expression error.
The other one i didn't started but is quite the same, Show the data of employees (name, commission and department) who receive commission greater than the average commission in their department.
ps: I'm starting with SQL so the topic isn't very deep yet.
You can use a window function for this
SELECT ename, sal, deptno
FROM (
SELECT *,
AVG(sal) OVER (PARTITION BY deptno) AS AvgPerDept
FROM Emp
) AS Emp
WHERE sal < AvgPerDept
Your attempt to the first one is not correct
The expression SELECT AVG(sal) FROM EMP will give the average salary of everyone, regardless which department they're in.
To get the average for each deparment, you will need to GROUP BY dept_no
SELECT Emp.* , dept_salary.average_sal
FROM Emp
INNER JOIN
-- join with an average salary for each department
(
SELECT deptno, AVG(sal) as average_sal
FROM Emp
GROUP BY deptno
) dept_salary
ON Emp.deptno = dept_salary.deptno
WHERE Emp.dept_no = dept_salary.deptno AND Emp.sal < dept_salary.average_sal;
I need to modify my script using rowcount to check if the data in table or not?. Here, i write the query to select a data for last 5 days from current system date. But sometimes there is no data in table for 5 days. So i need to fetch for 10 day or more.
Query:
Select ep.ENTERPRISE_NAME||'|'||s.id||'|'||s.SUBMISSION_DATE||'|'||E.VALUE
from JOB_SUMMARY_EXT e, ob_summary s, enterprise ep
where e.id = s.id and e.name_res_key = 'Model'
and s.job_id in (select id from job_summary where
trunc(start_date) > trunc(sysdate) -10 and service_name ='Model2' )
I don't know how to modify my Query using rowcount. If rowcount is 0 then i want select data for 10 days.Otherwise it should to fetch for 5 days automatically. I want this to be done as single query.
It looks that you want to select the last 5 "days" from that table. So, why would you anchor to SYSDATE if there aren't rows for each of those days? I'd suggest another approach: literally, select last 5 days. Here's how.
As I don't have your tables, I'm using Scott's EMP table which contains information about employees. It is an ancient one so HIREDATE column is set to 1980s, but never mind that. Sorting employees by HIREDATE in descending order shows:
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
SQL> select ename, hiredate from emp order by hiredate desc;
ENAME HIREDATE
---------- ----------
ADAMS 12.01.1983 1.
SCOTT 09.12.1982 2.
MILLER 23.01.1982 3.
FORD 03.12.1981 4.
JAMES 03.12.1981 4.
KING 17.11.1981 5. --> I want to fetch rows up to KING
MARTIN 28.09.1981
TURNER 08.09.1981
CLARK 09.06.1981
BLAKE 01.05.1981
JONES 02.04.1981
WARD 22.02.1981
ALLEN 20.02.1981
SMITH 17.12.1980
14 rows selected.
SQL>
As you can see, the 4th date is shared by two employees so I want to include them both. DENSE_RANK analytic function helps:
SQL> with last5 as
2 (select ename,
3 job,
4 sal,
5 hiredate,
6 dense_rank() over (order by hiredate desc) rnk
7 from emp
8 )
9 select ename, job, sal, hiredate
10 from last5
11 where rnk <= 5;
ENAME JOB SAL HIREDATE
---------- --------- ---------- ----------
ADAMS CLERK 1100 12.01.1983
SCOTT ANALYST 3000 09.12.1982
MILLER CLERK 1300 23.01.1982
JAMES CLERK 950 03.12.1981
FORD ANALYST 3000 03.12.1981
KING PRESIDENT 5000 17.11.1981
6 rows selected.
SQL>
What does it do? The LAST5 CTE sorts employees (as above), DENSE_RANK ranks them; finally, the last SELECT (which begins at line #9) fetches desired rows.
In your case, that might look like this:
with last5 as
(select id,
dense_rank() over (order by start_date desc) rnk
from job_summary
where service_name = 'Model2'
)
select ep.enterprise_name,
s.id,
s.submission_date,
e.value
from job_summary_ext e
join ob_summary s on e.id = s.id
join last5 t on t.id = s.id
join enterprise ep on <you're missing join condition for this table>
where e.name_res_key = 'Model';
Note that you're missing join condition for the ENTERPRISE table; if that's really so, no problem - you'd use cross join for that table, but I somehow doubt that you want that.
Finally, as you use SQL*Plus, perhaps you don't need to concatenate all columns and separate them by the pipe | sign - set it as a column separator, e.g.
SQL> set colsep '|'
SQL>
SQL> select deptno, dname, loc from dept;
DEPTNO|DNAME |LOC
----------|--------------|-------------
10|ACCOUNTING |NEW YORK
20|RESEARCH |DALLAS
30|SALES |CHICAGO
40|OPERATIONS |BOSTON
SQL>
If you want to
return 10 last days if select count(*) returns 0, or
return 5 last days if select count(*) returns a positive number
then something like this might help (again based on Scott's EMP table):
with
tcnt as
-- count number of rows; use your own requirement, I'm checking
-- whether someone got hired today. In Scott's EMP table, nobody was
-- so CNT = 0
(select count(*) cnt
from emp
where hiredate >= trunc(sysdate)
)
select e.ename, e.job, e.sal, e.hiredate
from emp e cross join tcnt c
where e.hiredate >= case when c.cnt = 0 then trunc(sysdate) - 10
else trunc(sysdate) - 5
end;
Apply it to your tables; I don't know which of those 3 tables' count you want to check.
Tried to add in comments but it was too long for comments and Not clear on count based on but here is case in where clause substitute your count statement with nvl function
SELECT ep.ENTERPRISE_NAME||'|'||s.id||'|'||s.SUBMISSION_DATE||'|'||E.VALUE
FROM JOB_SUMMARY_EXT e,
ob_summary s,
enterprise ep
WHERE e.id = s.id
AND e.name_res_key = 'Model'
AND s.job_id IN
(SELECT id
FROM job_summary
WHERE service='Model'
AND trunc(start_date) >
CASE WHEN
(WRITE your SELECT COUNT criteria WITH NVL FUNCTION)<=0 THEN
trunc(sysdate) -10
ELSE trunc(sysdate)-5
END )
How to create a temporary table in oracle without knowing the number and name of columns.
For example:
Select columnA,columnB,* into temp_table from tableA.
Here,tableA maynot be a simple tablename but maybe derived from many queries.
How can this be achieved?Is there any alternative to this?
In Oracle, you have to first create a table, then insert into it. Or, create it directly (as in my example).
Note that I've created a "normal" table; if it were temporary, you could have chosen between a global or private (depending on database version you use).
For this discussion, I guess that it is just fine:
SQL> create table temp_table as
2 select a.*
3 from (select d.deptno, d.dname, e.ename --> this SELECT is your "tableA"
4 from emp e join dept d
5 on e.deptno = d.deptno
6 where job = 'CLERK'
7 ) a;
Table created.
SQL> select * from temp_table;
DEPTNO DNAME ENAME
---------- -------------------- ----------
10 ACCOUNTING MILLER
20 RESEARCH SMITH
20 RESEARCH ADAMS
30 SALES JAMES
SQL>
Alternatively, create a view and work with it:
SQL> create or replace view v_temp as
2 select d.deptno, d.dname, e.ename
3 from emp e join dept d
4 on e.deptno = d.deptno
5 where job = 'CLERK'
6 ;
View created.
SQL> select * from v_temp;
DEPTNO DNAME ENAME
---------- -------------------- ----------
10 ACCOUNTING MILLER
20 RESEARCH SMITH
20 RESEARCH ADAMS
30 SALES JAMES
SQL>
This statement creates temp_table which contains all columns and data from tableA and two other empty columns, varchar and numeric.
create table temp_table as
select cast (null as varchar2(10)) columnA,
cast (null as number(6)) columnB,
tableA.*
from tableA
If you need only structure, no data, then add:
where 1 = 0
I am not sure why you want to have a temp table as you do not know columns, but you can think of dynamic SQL to create table depending on columns required during your process and then drop it again. From my point of view I think it is not a good design.
I can suggest to think on using collection with 'x' number of columns with datatype as VARCHAR2. During transaction you can populate and process according to you need and it will also remain for that session.
I have used mysql database in my application, but I want to migrate to Oracle.
The problem is in that query:
select * from users limit ?,1;"
That query returns every row one by one depending on ?.
How can i do that in oracle?
On Oracle 12c, you could use the row limiting feature using FETCH FIRST clause.
SQL> SELECT empno, sal, deptno FROM emp ORDER BY empno DESC
2 FETCH FIRST 1 ROWS ONLY;
EMPNO SAL DEPTNO
---------- ---------- ----------
7934 1300 10
SQL>
Prior 12c solution is ROWNUM, however, if you want the row to be first sorted, then you need to do it in a sub-query -
SQL> SELECT empno, sal, deptno FROM
2 ( SELECT * FROM emp ORDER BY empno DESC
3 ) WHERE ROWNUM = 1;
EMPNO SAL DEPTNO
---------- ---------- ----------
7934 1300 10
SQL>
If the order doesn't matter to you, if you just want any random row, simply use ROWNUM.
Depending on your requirement, you could also use ANALYTIC functions such as ROW_NUMBER, RANK, DENSE_RANK.
select * from (select rownum r, u.* from users u ) where r=1;
or if you want it sorted (replace x by columnnumber or columnname):
select * from (select rownum r, u.* from users u order by x) where r=1;
I have two tables and I need to Update the second table with a value from first table based on a common column.
I used the below statement
UPDATE emp
2 SET ename = ( SELECT dname
3 FROM dept
4 WHERE emp.deptno = dept.deptno)
5 WHERE EXISTS
6 ( SELECT dname
7 FROM dept
8 WHERE emp.deptno = dept.deptno);
But am getting the error
ORA-01427 - Single row subquery returns more than one row.
Can you plese help me out?
SELECT dname
FROM dept
WHERE emp.deptno = dept.deptno
query must return single record.
check with
SELECT count(*), dname
FROM dept
group by dname
having count(*) > 1
order by 1 desc
or use
SELECT dname
FROM dept
WHERE emp.deptno = dept.deptno
and rownum = 1
You need to check if 1st subquery returns only 1 value bcz if this subquery returns more then 1 row you want to update 1 field ename with 2 values from row and that's why i guess you have this error
Your subquery to dept table is probably not returning one row. Add min or max operation to get one row from dept for each row from emp. Each setting value must be nonambigous.
UPDATE emp
SET ename = ( SELECT min dname
FROM dept
WHERE emp.deptno = dept.deptno)
WHERE EXISTS
( SELECT dname
FROM dept
WHERE emp.deptno = dept.deptno);
Remark1: in default SCOTT schema deptno is primary key, so no error would be encountered.
Remark2: shouldn't your question to be placed in serverfault.com?