Unable to display the employees whose salary is grater than 1300 - oracle

I'm using Oracle DB. The question is
Select all employee who jobs are either CLERK or SALESMAN and have salary greater than 1300..
Hence my answer is
select ENAME from EMP where JOB='CLERK' OR JOB='SALESMAN' AND SAL>1300;
The problem here is even though I have mentioned as salary to be greater than 1300, it is listing employee names with salary 800/1100 etc. What is wrong with my query?

The documentation list condition precedence form high to low. That shows that AND is evaluated before OR. With no parentheses your query:
select ENAME from EMP where JOB='CLERK' OR JOB='SALESMAN' AND SAL>1300;
is evaluated as
select ENAME from EMP where JOB='CLERK' OR (JOB='SALESMAN' AND SAL>1300);
so the 1300 limit is only applied to salesmen, and you will see clerks with any salary. To change that default implied precedence you need to supply parentheses, as #user7294900 showed:
select ENAME from EMP where (JOB='CLERK' OR JOB='SALESMAN') AND SAL>1300;
You could also use an IN() condition here:
select ENAME from EMP where JOB IN ('CLERK', 'SALESMAN') AND SAL>1300;

Add parentheses between AND conditions
select ENAME from EMP where (JOB='CLERK' OR JOB='SALESMAN') AND SAL>1300;

Related

order by What are 1, 2 in alignment?

select empno , deptno , sal from emp
order by 1,2
What are 1, 2 in alignment?
What is one and two? Why don't you write the column name?
The query language defines this as a shortcut.
select empno , deptno , sal from emp
order by 1,2
select empno , deptno , sal from emp
order by empno, deptno
mean precisely the same thing. The numbers refer to the column numbers in your SELECT, counting from 1.
The shortcut comes in handy if you have stuff like
select CONCAT(surname, ', ', givenname) name, empno , deptno , sal from emp
order by 1
because it saves typing. In standard SQL you'd have to write that query
select CONCAT(surname, ', ', givenname) name, empno , deptno , sal from emp
order by CONCAT(surname, ', ', givenname)
Use whichever one you please. But be careful; it makes your ORDER BY clause dependent on the order of items in your SELECT clause. The next person to work on your code may not be expecting such a dependency. Especially if that next person is you.
It works just the same, whether you use
order by empno, deptno
or
order by 1, 2
Note that - although positional sorting requires less typing, you have to synchronize such an order by clause with every select column list rearrangement. I never use it (except for quick & dirty testing queries).

Oracle Grand Total

I am having a query where I am selecting 4 columns. And, I want to put a grand total at the bottom of one of the columns, and not do any grouping:
SELECT customer_id, email, total_amount, order_date
FROM...................
I want to do a grand total of TOTAL_AMOUNT at the bottom, but not worry about any grouping. I'm not seeing how to do this using GROUPING or ROLLUP. I'm hoping not to have this as any running total in another oolumn, but as a grand total at the bottom.
Many thanks.
You can add a grand total row with a UNION ALL and a column to track if the row is for the grand total.
select customer_id, email, total_amount, order_date, 0 is_grand_total
from orders
union all
select null, null, sum(total_amount), null, 1 is_grand_total
from orders
order by is_grand_total, customer_id;
SQL Fiddle example.
(In my opinion, this is often a good way to add summary logic to queries. I'd rather have a slightly more complicated solution with one language (SQL), than a solution that involves two or more languages or applications.)
A simple SQL*Plus option:
SQL> break on report
SQL> compute sum of sal on report
SQL>
SQL> select deptno, ename, sal
2 from emp
3 where deptno = 10;
DEPTNO ENAME SAL
---------- ---------- ----------
10 CLARK 2450
10 KING 5001
10 MILLER 1300
----------
sum 8751
SQL>

Hide column from Query

I don't want rank column but still, want the data in the same format by applying dense rank.
select ename,position,deptno,dense_rank() over(partition by deptno order by ename asc) as rank from emp where deptno in ('10','30');
Why do you need the rank just order by deptno first then ename.
SELECT ename,position,deptno,
FROM emp
WHERE deptno in ('10','30')
ORDER BY DeptNo, Ename
Using the analytical function two options derived table or CTE
Derived table/inline view.
SELECT ename,position,deptno
FROM (select ename,position,deptno,dense_rank() over(partition by deptno order by ename asc) as rank
from emp
where deptno in ('10','30')) Z
ORDER BY deptNo, rank
Common Table Expression (CTE):
with Z AS (SELECT ename,position,deptno
, dense_rank() over(partition by deptno order by ename asc) as rank
FROM emp
WHERE deptno in ('10','30'))
SELECT ename,position,deptno
FROM z
ORDER BY deptno, rank
Both these last 2 techniques simply avoid exposing the rank function to the outer query in which the results are returned. They are "Tricks" and sub-optimal execution time. unless there's a specific reason to have the rank data; I'd not use it.

How to compare two tables in oracle when number of columns in tables differs

I have two tables in my database they are EXP1 and EXP2. I tried with the below query, this query is working when both the tables have same number of columns but my table EXP1 has 1000 columns n EXP2 has 1000+4.
select *
from
(
(select * from exp1
minus
select * from exp2)
union all
(select * from exp2
minus
select * from exp1)
);
INTRO: Below I show how one can do "by hand" what the tools (SQL Developer for example) can do much faster and much better. My interest in this (and yours!) is two-fold: learn and use some ideas that can help in many other problems; and understand what those tools do under the hood in the first place.
OK. Suppose you have two tables, and they have many columns in common (possibly not in the same order) and a few columns may be different - there may be a handful of columns in one table but not in the other. First you want to be able to look just at the common columns.
Then, suppose that's done. Now what's left of the two tables has many rows in common, but there are a few that are different. A row may exist in one table but not in the other, or two rows, one from each table, may be very similar but they may differ in just one or a small number of column values. Logically these are still one row in the first table but not the second, and the other row only in the second table but not in the first. However, let's say both tables have the same PK column - then you may have the same PK value in both tables, but at least one of the OTHER columns has different values for that PK value in the two tables. And, you want to find these differences between the two tables.
In what follows I will assume that if two columns, in the two tables, have the same name, they will also have the same data type. If that is not guaranteed in your case, it can be fixed with a little more work in the part where I identify the "common columns" - instead of matching them just by name, from the catalog views, they would have to be matched also by data type.
When you get to comparing rows in the two tables in the final step, (A minus B) union all (B minus A) works, but is not very efficient. Each table is read twice, and minus is an expensive operator. The more efficient solution, which I illustrate below, was discussed in a long thread on AskTom several years ago. Namely: collect all the rows from both tables (with union all), group by all the columns, and disregard the groups that have a count of 2. This means rows that were found in both tables, so they are duplicates in the union all! Actually, you will see a small additional trick to identify from which table the "non-duplicated" rows come. Add a column for "table_name" and in the final select, after grouping and keeping the groups with count(*) = 1, select max(table_name). You need an aggregate function (like max()) because you are grouping, but for these rows each group only has one row, so the max() is really just the table name.
The beauty of this approach is that it can be used to identify the common columns, too! In that case, we will compare rows from the USER_TAB_COLS view - we select column names that appear in either of the tables, and keep only the column names that are duplicates (so the column names appear in both tables). In that part of the solution, I also retrieve column_id, which is used to order the columns. Don't worry if you are not familiar with keep (dense_rank first...) - it's not really that complicated, but it's not that important either.
First let's set up a test case. I copy the EMP table from the SCOTT schema to my own schema, I replicate it (so now I have two copies, named EMP1 and EMP2), and I modify them slightly. I delete a different column from each, I delete a few (different) rows from each, and I modify one salary in one table. I will not show the resulting (slightly different) tables, but if you are following along, just select * from both and compare them before you continue reading.
Create the tables:
create table EMP1 as select * from scott.emp;
Table EMP1 created.
select * from EMP1;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
----- ---------- --------- ---- ------------------- ----- ------ -------
7369 SMITH CLERK 7902 1980-12-17 00:00:00 800 20
7499 ALLEN SALESMAN 7698 1981-02-20 00:00:00 1600 300 30
7521 WARD SALESMAN 7698 1981-02-22 00:00:00 1250 500 30
7566 JONES MANAGER 7839 1981-04-02 00:00:00 2975 20
7654 MARTIN SALESMAN 7698 1981-09-28 00:00:00 1250 1400 30
7698 BLAKE MANAGER 7839 1981-05-01 00:00:00 2850 30
7782 CLARK MANAGER 7839 1981-06-09 00:00:00 2450 10
7788 SCOTT ANALYST 7566 1987-04-19 00:00:00 3000 20
7839 KING PRESIDENT 1981-11-17 00:00:00 5000 10
7844 TURNER SALESMAN 7698 1981-09-08 00:00:00 1500 0 30
7876 ADAMS CLERK 7788 1987-05-23 00:00:00 1100 20
7900 JAMES CLERK 7698 1981-12-03 00:00:00 950 30
7902 FORD ANALYST 7566 1981-12-03 00:00:00 3000 20
7934 MILLER CLERK 7782 1982-01-23 00:00:00 1300 10
Modify them slightly:
create table EMP2 as select * from EMP1;
Table EMP2 created.
alter table emp1 drop column hiredate;
Table EMP1 altered.
alter table emp2 drop column comm;
Table EMP2 altered.
delete from EMP1 where ename like 'A%';
2 rows deleted;
delete from EMP2 where sal >= 3000;
3 rows deleted
update EMP2 set sal = 2950 where empno = 7698;
1 row updated
commit;
At this point you would do well to select * from EMP1; and select * from EMP2; and compare.
Now let's find out what columns the two tables have left in common.
select column_name,
min(column_id) keep(dense_rank first order by table_name) as col_id
from user_tab_cols
where table_name in ('EMP1', 'EMP2')
group by column_name
having count(*) = 2
order by col_id;
COLUMN_NAME COL_ID
----------- ------
EMPNO 1
ENAME 2
JOB 3
MGR 4
SAL 5
DEPTNO 7
6 rows selected
Perfect, so now we can compare the two tables, but only after we "project" them along the common columns only.
select max(table_name) as table_name, EMPNO, ENAME, JOB, MGR, SAL, DEPTNO
from (
select 'EMP1' as table_name, EMPNO, ENAME, JOB, MGR, SAL, DEPTNO from EMP1
union all
select 'EMP2' as table_name, EMPNO, ENAME, JOB, MGR, SAL, DEPTNO from EMP2
)
group by EMPNO, ENAME, JOB, MGR, SAL, DEPTNO
having count(*) = 1
order by EMPNO, ENAME, JOB, MGR, SAL, DEPTNO, table_name;
TABLE_NAME EMPNO ENAME JOB MGR SAL DEPTNO
---------- ----- ---------- --------- ------ ------ --------
EMP2 7499 ALLEN SALESMAN 7698 1600 30
EMP1 7698 BLAKE MANAGER 7839 2850 30
EMP2 7698 BLAKE MANAGER 7839 2950 30
EMP1 7788 SCOTT ANALYST 7566 3000 20
EMP1 7839 KING PRESIDENT 5000 10
EMP2 7876 ADAMS CLERK 7788 1100 20
EMP1 7902 FORD ANALYST 7566 3000 20
7 rows selected
The output is pretty much what we needed. Notice the first column, which tells us where the "unpaired" row comes from; and note BLAKE, who has different salary in the two tables (and the first column helps us to see what salary he has in which table).
This looks perfect so far, but what to do when you have 1000 columns? You could put it together in C or Java etc., using the result from the "common columns" query above - or you could do it all in Oracle, with dynamic SQL.
As far as I know, there is no set limit on the length of the text of an SQL statement in Oracle; the documentation says "The limit on how long a SQL statement can be depends on many factors, including database configuration, disk space, and memory" (and probably on your Oracle version, which they didn't mention). In any case, it will be more than 4000 characters, so we need to work with CLOB. In particular, we can't use listagg() - we need a workaround. I use xmlagg() below. Then, the documentation says if you concatenate text and at least one operand is CLOB the result will be CLOB; if that doesn't work for you, you may have to wrap the smaller text fragments within to_clob(). The "dynamic SQL" query below will produce the full text of the query I used above; you will simply copy it and paste it back into your front-end and execute it. You may have to delete wrapping double-quotes or such, depending on your front-end and settings.
First here is how we can create a (potentially very long) string, the list of common column names, which is repeated five times in the final query - just look again at the "final query" we used to compare the two tables above.
with
common_cols ( column_name, col_id ) as (
select column_name,
min(column_id) keep(dense_rank first order by table_name) as col_id
from user_tab_cols
where table_name in ('EMP1', 'EMP2')
group by column_name
having count(*) = 2
),
col_string ( str ) as (
select rtrim(xmlcast(xmlagg(xmlelement(e, column_name, ', ') order by col_id)
as clob), ', ') from common_cols
)
select * from col_string;
STR
-----------------------------------
EMPNO, ENAME, JOB, MGR, SAL, DEPTNO
And finally the full dynamic SQL query (the result is exactly the query I used to compare EMP1 and EMP2 on their common columns earlier):
with
common_cols ( column_name, col_id ) as (
select column_name,
min(column_id) keep(dense_rank first order by table_name) as col_id
from user_tab_cols
where table_name in ('EMP1', 'EMP2')
group by column_name
having count(*) = 2
),
col_string ( str ) as (
select rtrim(xmlcast(xmlagg(xmlelement(e, column_name, ', ') order by col_id)
as clob), ', ') from common_cols
)
select 'select max(table_name) as table_name, ' || str || chr(10) ||
'from (' || chr(10) ||
' select ''EMP1'' as table_name, ' || str || ' from EMP1' || chr(10) ||
' union all' || chr(10) ||
' select ''EMP2'' as table_name, ' || str || ' from EMP2' || chr(10) ||
' )' || chr(10) ||
'group by ' || str || chr(10) ||
'having count(*) = 1' || chr(10) ||
'order by ' || str || ', table_name;' as comp_sql_str
from col_string;

How can i select just one row in Oracle by row id?

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;

Resources