Multiple where statement returning undesired result - sql

I'm trying to retrieve employees within depts 10 and 30 with a salary greater than 1500. This is the statement I'm running.
select ename, sal
from emp
where deptno = 10
or deptno = 30
and sal > 1500;
It returns:
Name Salary
---------- --------------
NAME1 1600
NAME2 2850
NAME3 2450
NAME4 5000
NAME5 1300
When I run the query without the OR for only one dept, it returns NAME1 and NAME2, when I run it for the other dept it returns NAME3 and NAME4.
My question is, is it the OR statement that's giving me the incorrect NAME5? To me that query should work, I tried using AND instead of OR and it gives me no results. Any help would be appreciated.

AND has precedence over OR in a SQL WHERE clause, so what you have is equivalent to
where deptno = 10 or (deptno = 30 and sal > 1500)
Try putting parentheses around the OR to see if it gives you the results you expect.
where (deptno = 10 or deptno = 30) and sal > 1500
(Also, it would clarify things a little bit if you included the department number in your SELECT so you can see which department each row is coming from.)

Your problem is the lack of parentheses. But really the best way to write the query uses in:
select ename, sal
from emp
where deptno in (10, 30) and
sal > 1500;

Related

Getting percent of employee's salary within department

I need to create a report that displays information about what part of the salary in percentage terms each employee receives within the department in which they work, using analytical functions.
SELECT EMPLOYEES.FIRST_NAME, EMPLOYEES.LAST_NAME, EMPLOYEES.DEPARTMENT_ID,
DEPARTMENTS.DEPARTMENT_NAME, EMPLOYEES.SALARY,
(SALARY/SUM(SALARY)) * 100 over (partition by DEPARTMENT_ID) AS "PercentWithinDepartment"
FROM HR.EMPLOYEES
FULL JOIN HR.DEPARTMENTS ON EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
I get an "ORA-00923 FROM keyword not found where expected" error but I think it's not my only mistake within this task.
I cannot provide a code snippet of database but this can be run against the HR sample schema.
My request is to help me figure out mistake to complete this task properly.
The immediate issue is that your over clause is in the wrong place:
(SALARY/SUM(SALARY)) * 100 over (partition by DEPARTMENT_ID)
should be
(SALARY/SUM(SALARY) over (partition by DEPARTMENT_ID)) * 100
But the reference to DEPARTMENT_ID in there is ambiguous as that column is in both tables, so it should be:
(SALARY/SUM(EMPLOYEES.SALARY) over (partition by DEPARTMENTS.DEPARTMENT_ID)) * 100
You might want to consider using table aliases to make the code a bit shorter.
You may also want to round (or truncate/floor) the percentages to, say, one or two decimal places.
I'm not sure you really want a full outer join though - that will include all departments with no employees (e.g. 'Recruiting'). A left outer join looks more appropriate - so 'Kimberley Grant', who is not linked to any department, will still be included.
I have to calculate percent about what part of the salary employee get within all organization and im confused why do i get "not a single-group group function" error here
Because the version you added as a comment has:
SALARY/SUM(EMPLOYEES.SALARY)*100
which is the aggregate form of the function, not the analytic form. As you don't want to aggregate here, you still need an over clause to make it analytical, but that can be empty:
SALARY/SUM(EMPLOYEES.SALARY) over () * 100
And you can of course do both together:
select employees.first_name,
employees.last_name,
employees.department_id,
departments.department_name,
employees.salary,
salary / sum(employees.salary) over (partition by departments.department_id) * 100 as "PercentWithinDepartment",
salary / sum(employees.salary) over () * 100 as "PercentWithinOrganization"
from hr.employees
left join hr.departments on employees.department_id = departments.department_id;
FIRST_NAME LAST_NAME DEPARTMENT_ID DEPARTMENT_NAME SALARY PercentWithinDepartment PercentWithinOrganization
-------------------- ------------------------- ------------- ------------------------------ ---------- ----------------------- -------------------------
Jennifer Whalen 10 Administration 4400 100 .636389933
Michael Hartstein 20 Marketing 13000 68.4210526 1.88024299
Pat Fay 20 Marketing 6000 31.5789474 .867804455
...
Kimberely Grant 7000 100 1.01243853
You might also want to add an order by clause...
I don't have HR schema (but I have Scott) so - here's how. I'm using SQL*Plus' formatting capabilities to make it look prettier.
SQL> set numformat 999g990d00
SQL> break on deptno on dname
SQL> compute sum of pct_sal on deptno
SQL>
SQL> select e.deptno, d.dname, e.ename, e.sal,
2 sum(e.sal) over (partition by e.deptno) dept_sal,
3 --
4 round((e.sal / sum(e.sal) over (partition by e.deptno)) * 100, 2) pct_sal
5 from emp e join dept d on d.deptno = e.deptno
6 order by e.deptno, e.ename;
Result:
DEPTNO DNAME ENAME SAL DEPT_SAL PCT_SAL
----------- -------------- ---------- ----------- ----------- -----------
10,00 ACCOUNTING CLARK 2.450,00 8.750,00 28,00
KING 5.000,00 8.750,00 57,14
MILLER 1.300,00 8.750,00 14,86
*********** ************** -----------
sum 100,00
20,00 RESEARCH ADAMS 1.100,00 10.915,00 10,08
FORD 3.000,00 10.915,00 27,49
JONES 2.975,00 10.915,00 27,26
SCOTT 3.000,00 10.915,00 27,49
SMITH 840,00 10.915,00 7,70
*********** ************** -----------
sum 100,02
30,00 SALES ALLEN 1.600,00 9.400,00 17,02
BLAKE 2.850,00 9.400,00 30,32
JAMES 950,00 9.400,00 10,11
MARTIN 1.250,00 9.400,00 13,30
TURNER 1.500,00 9.400,00 15,96
WARD 1.250,00 9.400,00 13,30
*********** ************** -----------
sum 100,01
14 rows selected.
SQL>
(Sum isn't exactly 100% because of rounding.)
I might be mistaking, but it seems to me that all you need is to use ratio_to_report analytic function. Try this one please
SELECT E.FIRST_NAME, E.LAST_NAME, E.DEPARTMENT_ID,
D.DEPARTMENT_NAME, E.SALARY,
100 * ratio_to_report(e.salary) over (partition by d.department_id)
FROM HR.EMPLOYEES e
JOIN hr.departments d
on e.department_id = d.department_id
order by d.department_id;

Not a group by expression in my oracle SQL query

I have the following sql query that I m not able to process since this afternoon.
I have seen lot of threads about this issue but I m not getting it right, I believe that I m miss understanding this one topic that is shaming my day.
SELECT
cor.c_order_id, cor.totallines,cor.documentno,cbp.name
FROM
c_orderline col
LEFT JOIN c_order cor ON cor.c_order_id = col.c_order_id
LEFT join c_bpartner cbp on cbp.c_bpartner_id = cor.c_bpartner_id
WHERE
cor.issotrx = 'Y'
and cor.docstatus not in ('DR','IP')
AND cor.salesrep_id = 1037317
and col.qtyordered <> 0
and cor.dateordered between SYSDATE - 30 and SYSDATE + 30
AND col.c_orderline_id NOT IN (
SELECT
cil.c_orderline_id
FROM
c_invoiceline cil
WHERE
cil.c_orderline_id IS NOT NULL
)
group by cor.c_order_id, cor.documentno
order by cor.c_order_id, cor.documentno
What I m doing wrong ??
A simplified example, based on Scott's EMP table.
This is what you did:
SQL> select deptno, job
2 from emp
3 group by deptno;
select deptno, job
*
ERROR at line 1:
ORA-00979: not a GROUP BY expression
As you can see, Oracle marked the culprit - the JOB column.
Therefore, if you want to use a GROUP BY clause (which is correct, but - you'd get the same result using DISTINCT; these two should not be used together. GROUP BY is usually used with aggregates such as min, max, avg and such), then put all columns into the GROUP BY:
SQL> select deptno, job
2 from emp
3 group by deptno, job;
DEPTNO JOB
---------- ---------
20 CLERK
30 SALESMAN
20 MANAGER
30 CLERK
10 PRESIDENT
30 MANAGER
10 CLERK
10 MANAGER
20 ANALYST
9 rows selected.
Or, as I said - using DISTINCT:
SQL> select distinct deptno, job
2 from emp;
DEPTNO JOB
---------- ---------
20 CLERK
30 SALESMAN
20 MANAGER
30 CLERK
10 PRESIDENT
30 MANAGER
10 CLERK
10 MANAGER
20 ANALYST
9 rows selected.
SQL>
group by is used for aggregate functions like sum(), max(), min() etc. What value are you calculating in groups?
remove the group by and your query looks good besides that
To correct the error this query is producing you need to
group by cor.c_order_id, cor.totallines,cor.documentno,cbp.name
Group by all the columns that are in the select statement and are not part of aggregate function.
What else is a problem?
Here is the DEMO
In this demo you will see that the query works(with the correction I have suggested), but as I asked, what else is a problem to you. Do elaborate so we can help. Cheers!

How to replace ? value in oracle sql if used the same in multiple times in same oracle query

Example:
select * from employee, department
where
employee.empid=?
and department.empid=?
--? used to get the same value for employee.empid and department.empid.
Appreciate your answers.
Well, your query seems to be wrong and your question doesn't make much sense either. In my opinion, you should join those tables and use one parameter. For example, something like this:
select *
from employee e join department d on e.deptno = d.deptno
where d.deptno = :par_deptno
You'd want to have EMPID as a parameter; why? What is EMPID doing in the DEPARTMENT table? It just doesn't belong there. So, either example you posted is wrong, or you don't quite understand what you're doing, or ... something else.
[EDIT, after your comment]
Doesn't make much sense, but - here's how you might do that (if I understood what you're saying):
SQL> select e.deptno e_dept,
2 d.deptno d_dept,
3 d.dname,
4 e.ename
5 from emp e join dept d on e.deptno = d.deptno
6 join (select 10 params from dual) p on e.deptno = p.params
7 and d.deptno = p.params;
E_DEPT D_DEPT DNAME ENAME
---------- ---------- -------------- ----------
10 10 ACCOUNTING CLARK
10 10 ACCOUNTING KING
10 10 ACCOUNTING MILLER
SQL>
You can use a WITH clause to select your parameters, so you can access them all over your query, be it in your main query or in some sub-sub-sub query:
with params as
(
select
:emp_id as emp_id,
:dept_id as dept_id
from dual
)
select ...
from . ..
where x.emp_id = (select emp_id from params)
and y.emp_id <> (select emp_id from params)
and z.dept_id = (select dept_id from params);

adding column in Oracle SQL table that is random sample from another column/table

I have a preexisting table (MYORIGTABLE) in SQL and I would like to add a column which is a random sample from a column in another table (RANDNUM10). The other table is a 1 column sample of random numbers I've predetermined and would like to sample from; column name is RANDVAL. I've tried the following
select
(select randval from
(SELECT randval
FROM RANDNUM10
ORDER BY dbms_random.value)
where rownum=1) as mynum
from MYORIGTABLE
But unfortunately this just gives me the same random number which was sampled from RANDNUM10 repeated for the entire length of MYORIGTABLE. How can I perform the sampling so that every line of MYORIGTABLE gets a newly generated sample?
Thanks,
Christie
This is your query:
select (select randval
from (SELECT randval
FROM RANDNUM10
ORDER BY dbms_random.value
)
where rownum = 1
) as mynum
from MYORIGTABLE;
A common problem with database optimizers -- in this situation -- that that it optimizes away the multiple calls to the subquery. It ends up replacing the subquery with one call.
One way around this is to use a correlated subquery. For this, I would recommend removing one level of nesting:
select (select max(randval) keep (dense_rank first order by dbms_rnadom.value)
from randnum10
where myorigtable.id is not null -- or whatever
) as mynum
from MYORIGTABLE;
I think this will work so the subquery is called 10 times, although a more complex correlation clause might be needed.
Something like this, perhaps?
Test case:
SQL> create table myorigtable (id number);
Table created.
SQL> create table randnum10 (randval number);
Table created.
SQL> insert into myorigtable select level from dual connect by level <= 10;
10 rows created.
SQL> insert into randnum10 select round(dbms_random.value(1, 100)) from dual connect by level <= 10;
10 rows created.
SQL> alter table myorigtable add randval number;
Table altered.
Update:
SQL> update myorigtable m set
2 m.randval = (select x.randval
3 from (select randval, row_number() over (order by randval) rn
4 from randnum10
5 ) x
6 where x.rn = m.id
7 );
10 rows updated.
SQL> select * from myorigtable;
ID RANDVAL
---------- ----------
1 5
2 18
3 29
4 31
5 31
6 62
7 66
8 87
9 94
10 98
10 rows selected.
SQL>
Here is one approach. I assume your RANDNUM10 table has many rows (more rows than MYORIGTABLE), you want to sample the random numbers without replacement (select DISTINCT random values from RANDNUM10) - and therefore the random numbers table has enough rows - at least as many as MYORIGTABLE - and you need to add the column to the result set of a SELECT query, rather than to the stored table. (If you need to store the result in an additional column, then you must first add the column if it doesn't already exist, and use the SELECT query below in an UPDATE statement.)
The strategy is simple: associate arbitrary row numbers to the smaller table (with values 1, 2, ..., N where N is the row count), and associate random row numbers to the rows in the random numbers table. Both can be done with ROW_NUMBER()... - the first one can be ordered by NULL since all the randomness we need will come from the other table's row numbers anyway. Then JOIN the two tables on the row number thus added to both tables.
For illustration I created MYORIGTABLE as a copy of SCOTT.EMP, selecting only a few columns from that table. Here's what the query looks like, and the output using that as MYORIGTABLE.
Setup:
create table myorigtable as select empno, ename, job, deptno from scott.emp;
create table randnum10 ( randval ) as
select dbms_random.value() from dual connect by level <= 1000;
Query and output:
select a.empno, a.ename, a.job, a.deptno, b.randval
from (
select empno, ename, job, deptno, row_number() over (order by null) as rn
from myorigtable
) a
inner join
(
select randval, row_number() over (order by dbms_random.value()) as rn
from randnum10
) b
on a.rn = b.rn
;
EMPNO ENAME JOB DEPTNO RANDVAL
----- ---------- --------- ------- ----------
7369 SMITH CLERK 20 .991998901
7499 ALLEN SALESMAN 30 .448133242
7521 WARD SALESMAN 30 .136242235
7566 JONES MANAGER 20 .443347421
7654 MARTIN SALESMAN 30 .836931008
7698 BLAKE MANAGER 30 .361437867
7782 CLARK MANAGER 10 .433786615
7788 SCOTT ANALYST 20 .285173363
7839 KING PRESIDENT 10 .063840361
7844 TURNER SALESMAN 30 .825888729
7876 ADAMS CLERK 20 .818415041
7900 JAMES CLERK 30 .150180080
7902 FORD ANALYST 20 .442389099
7934 MILLER CLERK 10 .086995415

sql like command

Using Like operator, how to retrieve a name which is having exact 100 characters
note: I don't want to use 100 underscores _
ex:
select ename from emp where ename like '_____________';
it should not like above
is there any other way
For exactly 100 characters , do
select ename from emp where LEN(ename)=100
For greater than equal to 100 characters
select ename from emp where LEN(ename) >= 100
Many dbms products support the REPEAT function (not ANSI however...)
select ename from emp where ename like repeat('_',100)
Or use CHAR_LENGTH:
select ename from emp where CHAR_LENGTH(ename) = 100
I'm not quite sure what DBMS you using, this is should work under mysql :
select ename from emp where ename like concat('%', REPEAT('_',100),'%');
Above condition is where string have 100 UNDERSCORE whether in beginning, middle or end of string. For condition where string exactly have 100 UNDERSCORE :
select ename from emp where ename = REPEAT('_',100);
select ename from emp where LEN(ename)=100 and ename like '%_%';
LEN is used for length of value and % before and after character means any string which contains atleast one _
for more that ore equal to 100
select ename from emp where LEN(ename)>=100 and ename like '%_%';
You can use following:
WHERE column_name REGEXP [a-z0-9]{100}