In the following code, why can't I use count () for each row in the table? The count() must return the number of colours of each row, which all become one, but it does not. If I use group by, it will.
select colour,count(*)
from bricks;
updated:
another example:
select b.*,
(select count(*) from bricks where colour=b.colour)
from bricks b;
The above example used grouping function without group by and it works without any errors
That's just the way SQL demands it. You can't select a discrete column and and aggregate function without listing the discrete columns in a group by statement.
Your second example runs a subquery for each record returned from the main bricks query. This won't perform as well as the first query using a group by.
And you best alias your subquery table - I've seen circumstances where the parser decides you're essentially saying b.colour = b.colour, which will give you the same count(*) for each record in bricks.
I don't have those bricks, but - I do have Scott's sample EMP table with jobs instead.
This is what you tried (and it doesn't work):
SQL> select job, count(*)
2 from emp
3 order by 1, 2;
select job, count(*)
*
ERROR at line 1:
ORA-00937: not a single-group group function
This is what works, but - that's not what you want:
SQL> select job, count(*)
2 from emp
3 group by job
4 order by 1, 2;
JOB COUNT(*)
--------- ----------
ANALYST 2
CLERK 4
MANAGER 3
PRESIDENT 1
SALESMAN 4
This is what you want (and it works), but - doesn't look pretty:
SQL> select e.job,
2 (select count(*) from emp a where a.job = e.job) cnt
3 from emp e
4 order by 1, 2;
JOB CNT
--------- ----------
ANALYST 2
ANALYST 2
CLERK 4
CLERK 4
CLERK 4
CLERK 4
MANAGER 3
MANAGER 3
MANAGER 3
PRESIDENT 1
SALESMAN 4
SALESMAN 4
SALESMAN 4
SALESMAN 4
14 rows selected.
Good news for you - use count function in its analytic form:
SQL> select job, count(*) over (partition by job order by null) cnt
2 from emp
3 order by 1, 2;
JOB CNT
--------- ----------
ANALYST 2
ANALYST 2
CLERK 4
CLERK 4
CLERK 4
CLERK 4
MANAGER 3
MANAGER 3
MANAGER 3
PRESIDENT 1
SALESMAN 4
SALESMAN 4
SALESMAN 4
SALESMAN 4
14 rows selected.
SQL>
Related
I am making a join between 2 tables, where they bring me number_phone that have a relationship and I bring the times that these are repeated, however, I am trying to make a condition to the count, so that it only lists those that are repeated more than 4 times, I tried with having account and it brings me the counter all in null.
It is worth mentioning that I did not occupy group by for the count because it brought me wrong values.
SELECT
REPLACE(REPLACE(t.id_contrato,'0999',''),'0998','')as contrato ,
t.num_telefono,
conc.valor_actual,
(
SELECT COUNT('x')
FROM TBL_TELEFONO ct
WHERE ct.num_telefono = t.num_telefono
AND ct.origen_tel='TELEFONO CONTRATO'
-- HAVING COUNT(*) > 4
) as counter
FROM TBL_TELEFONO t
INNER JOIN CAM_TBL_ALERTA_CONCENTRADO conc ON t.num_telefono = conc.valor_actual
WHERE id_contrato IS NOT NULL
AND id_contrato NOT IN ('N/A')
ORDER BY 4 DESC
How can I list only those that are repeated more than 4 times?
I don't like debugging code with no sample data, so I'll try to illustrate it on Scott's sample EMP table. "Jobs" will act like your "telephone numbers".
SQL> select deptno, ename, job
2 from emp
3 order by job;
DEPTNO ENAME JOB
---------- ---------- ---------
20 SCOTT ANALYST --> 2 analysts
20 FORD ANALYST
10 MILLER CLERK --> 4 clerks
30 JAMES CLERK
20 SMITH CLERK
20 ADAMS CLERK
30 BLAKE MANAGER --> 3 managers
20 JONES MANAGER
10 CLARK MANAGER
10 KING PRESIDENT --> 1 president
30 TURNER SALESMAN --> 4 salesmen
30 MARTIN SALESMAN
30 WARD SALESMAN
30 ALLEN SALESMAN
14 rows selected.
SQL>
According to that, we'd like to fetch all clerks and salesmen as there are 4 (or more) of them.
Instead of count aggregate function, use count in its analytic form:
SQL> select deptno, ename, job,
2 count(*) over (partition by job) cnt
3 from emp
4 order by job;
DEPTNO ENAME JOB CNT
---------- ---------- --------- ----------
20 SCOTT ANALYST 2
20 FORD ANALYST 2
10 MILLER CLERK 4
30 JAMES CLERK 4
20 SMITH CLERK 4
20 ADAMS CLERK 4
30 BLAKE MANAGER 3
20 JONES MANAGER 3
10 CLARK MANAGER 3
10 KING PRESIDENT 1
30 TURNER SALESMAN 4
30 MARTIN SALESMAN 4
30 WARD SALESMAN 4
30 ALLEN SALESMAN 4
14 rows selected.
SQL>
Now things become easier: use that query as a CTE (or a subquery), and apply where clause:
SQL> with temp as
2 (select deptno, ename, job,
3 count(*) over (partition by job) cnt
4 from emp
5 )
6 select deptno, ename, job
7 from temp
8 where cnt >= 4
9 order by job;
DEPTNO ENAME JOB
---------- ---------- ---------
10 MILLER CLERK
30 JAMES CLERK
20 SMITH CLERK
20 ADAMS CLERK
30 TURNER SALESMAN
30 MARTIN SALESMAN
30 WARD SALESMAN
30 ALLEN SALESMAN
8 rows selected.
SQL>
Applied to your query (again, can't test it without any sample data):
with temp as
(select
replace(replace(t.id_contrato,'0999',''),'0998','')as contrato ,
t.num_telefono,
conc.valor_actual,
count(*) over (partition by t.num_telefono) as counter
from tbl_telefono t
inner join cam_tbl_alerta_concentrado conc on t.num_telefono = conc.valor_actual
where id_contrato is not null
and id_contrato not in ('N/A')
)
select contrato, num_telefono, valor_actual
from temp
where counter >= 4;
Join to the selection that have more than 4
SELECT
REPLACE(REPLACE(t.id_contrato,'0999',''),'0998','')as contrato ,
t.num_telefono,
conc.valor_actual,
ct.counter
FROM TBL_TELEFONO t
INNER JOIN (
SELECT num_telefono, COUNT(*) AS counter
FROM TBL_TELEFONO
WHERE origen_tel='TELEFONO CONTRATO'
GROUP BY num_telefono
HAVING COUNT(*) > 4
) ct
ON ct.num_telefono = t.num_telefono
INNER JOIN CAM_TBL_ALERTA_CONCENTRADO conc
ON t.num_telefono = conc.valor_actual
WHERE id_contrato IS NOT NULL
AND id_contrato NOT IN ('N/A')
ORDER BY ct.counter DESC
Wrap the query in another to return only where count > 4
select *
from (
<your query, but without order by>
) x
where count > 4
order by count desc
This is a common question I saw in many places, but don't know yet it possible or not. I'm trying to select rows between 2 and 5 in following way using oracle sql developer tool.
As of result this query, this should select 3rd and 4th query according to below query
SELECT * FROM MyTable
WHERE ROWNUM > 2 AND ROWNUM < 5
but it's not selecting the 3rd and 4th rows,
Then I tried the following query
SELECT * FROM MyTable
WHERE RN BETWEEN 2 AND 5
This also syntactically and progrmatically correct, but not selecting the exact columns.
Use a subquery:
SELECT t.*
FROM (SELECT t.*, ROWNUM as rn
FROM MyTable t
) t
WHERE rn > 2 AND rn < 5;
Note that tables represent unordered sets. There is no such thing as a first or second row. You should have an ORDER BY clause to specify the ordering.
The reason that your version doesn't work is that rownum starts at 1 when the first row is put into the result set. If no row is put in, the value never increments. So, it never hits 2 or 3.
I should also note that between in SQL is inclusive. So >= and <= are more appropriate.
EDIT:
I should note that Oracle 12+ supports FETCH/OFFSET:
select t.*
from mytable t
offset 2 -- start on the third row
fetch first 2 rows only -- fetch two rows in total
An order by is still recommended in this case.
A little bit of analytics.
Salaries in the EMP table, sorted by $$$, look like this:
SQL> select ename, sal
2 from emp
3 order by sal;
ENAME SAL
---------- ----------
SMITH 800
JAMES 950 2 you want to return James ...
WARD 1250 3
MARTIN 1250 4
MILLER 1300 5 ... to Miller
TURNER 1500
ALLEN 1600
CLARK 2450
BLAKE 2850
JONES 2975
FORD 3000
KING 5000
12 rows selected.
SQL>
If you do it as follows, you'd get what you wanted:
SQL> select ename, sal, rn
2 from (select ename, sal, row_number() over (order by sal) rn
3 from emp
4 )
5 where rn between 2 and 5;
ENAME SAL RN
---------- ---------- ----------
JAMES 950 2
WARD 1250 3
MARTIN 1250 4
MILLER 1300 5
SQL>
However, as you can see, Ward and Martin earn the same $1250. So, should we count them as having the same salary and include Turner into the list, or not? Yet two another analytic functions might help you decide: RANK and DENSE_RANK:
SQL> select ename, sal,
2 row_number() over (order by sal) rn,
3 rank() over (order by sal) rnk,
4 dense_rank() over (order by sal) drnk
5 from emp
6 order by sal;
ENAME SAL RN RNK DRNK
---------- ---------- ---------- ---------- ----------
SMITH 800 1 1 1
JAMES 950 2 2 2 2nd isn't questionable, but ...
WARD 1250 3 3 3
MARTIN 1250 4 3 3
MILLER 1300 5 5 4 ... which one is 5th? Miller (RN and RNK), ...
TURNER 1500 6 6 5 ... or Turner (DRNK column)?
ALLEN 1600 7 7 6
CLARK 2450 8 8 7
BLAKE 2850 9 9 8
JONES 2975 10 10 9
FORD 3000 11 11 10
KING 5000 12 12 11
12 rows selected.
SQL>
To be fair, DENSE_RANK is probably the best option in such cases:
SQL> select ename, sal, drnk
2 from (select ename, sal, dense_rank() over (order by sal) drnk
3 from emp
4 )
5 where drnk between 2 and 5;
ENAME SAL DRNK
---------- ---------- ----------
JAMES 950 2
WARD 1250 3
MARTIN 1250 3
MILLER 1300 4
TURNER 1500 5
SQL>
Now you have several options; pick the one that suits you best.
Output of
Query 1:
select id from users
order by case when DEVIATION_LEVEL=2863 then 1 else 2 end
is
800019
800030
800040
800003
800007
800015
800025
800026....etc
Output of
Query 2:
select id from
(select id from users
order by case when DEVIATION_LEVEL=2863 then 1 else 2 end)
where rownum<=16;
is
800019
800030
800028
800020
800021
800018
800012
800161...etc
Why the order changes in 2nd query? Please suggest correct solution to just limit the size of the first query result.
When you perform a SELECT query without an ORDER BY clause the order of the result is undetermined. If you want or need to have a consistent ordering behavior then use the ORDER BY clause at the top level SELECT.
There is however the exception in oracle when you are limiting the rows with the ROWNUM field. In that case the ROWNUM filter would reduce the result set before applying the order by clause, thus removing rows that should have come first.
select id from users
order by case when DEVIATION_LEVEL=2863 then 1 else 2, id;
and
select id from
(select id from users
order by case when DEVIATION_LEVEL=2863 then 1 else 2 end, id)
where rownum<=16;
The reason is that ORDER BY cannot guarantee the ordering on duplicate values.
In your query, put the DEVIATION_LEVEL in the column list of the select and then you will understand that the order is just not same when they are duplicate values.
For example,
Query 1
SQL> SELECT empno, deptno FROM emp ORDER BY CASE WHEN deptno=10 THEN 1 ELSE 2 END;
EMPNO DEPTNO
---------- ----------
7782 10
7839 10
7934 10
7566 20
7654 30
7698 30
7900 30
7788 20
7369 20
7844 30
7876 20
7521 30
7499 30
7902 20
14 rows selected.
Query 2
SQL> SELECT empno, deptno
2 FROM
3 (SELECT empno, deptno FROM emp ORDER BY CASE WHEN deptno=10 THEN 1 ELSE 2 END
4 )
5 WHERE rownum<=5;
EMPNO DEPTNO
---------- ----------
7782 10
7934 10
7839 10
7369 20
7654 30
SQL>
So, the ordering in the second case when ROWNUM is applied, it is picked randomly among the similar values.
Look at the first three ordered rows:
Output 1
EMPNO DEPTNO
---------- ----------
7782 10
7839 10
7934 10
Output 2
EMPNO DEPTNO
---------- ----------
7782 10
7934 10
7839 10
ORDER BY deptno will not guarantee same order every time. In above query, if you want a particular order, then make an ORDER BY on another column too, i.e. empno.
ORDER BY empno, deptno
If you compare both the outputs, there is no guarantee that ordering will be same always since the deptno is same as 10 in all three rows. When you have similar values, and if you order them, it is just like picking them in random.
ORDER BY in a subquery does not guarantee the results; SELECT * FROM table ORDER BY 1; is not same as SELECT * FROM (SELECT * FROM table ORDER BY 1);
I have a case in which I need to order the query result in a customized order like the following :
the DEPARTEMENT_ID needs to be in this order ( 10 then 50 then 20 )
is there a way to get this result ?
You could use CASE expression in the ORDER BY clause.
I answered a similar question here, https://stackoverflow.com/a/26033176/3989608, you could just tweak it to have your customized conditions in the CASE expression.
For example,
SQL> SELECT ename,
2 deptno
3 FROM emp
4 ORDER BY
5 CASE deptno
6 WHEN 20 THEN 1
7 WHEN 10 THEN 2
8 WHEN 30 THEN 3
9 END
10 /
ENAME DEPTNO
---------- ----------
SMITH 20
FORD 20
ADAMS 20
JONES 20
SCOTT 20
CLARK 10
KING 10
MILLER 10
ALLEN 30
TURNER 30
WARD 30
MARTIN 30
JAMES 30
BLAKE 30
14 rows selected.
SQL>
One way you can do it is to have an other column DISPLAY_ORDER having serial number data in the order that you want it.
so the sql will be
select JOB_ID, DEPARTMENT_ID
from EMPLOYEES
order by DISPLAY_ORDER;
You can use the DECODE to accomplish this.
SELECT JOB_ID,DEPARTMENT_ID
FROM YOURTABLE
ORDER BY DECODE(DEPARTEMENT_ID, 10, 1, 50, 2, 20, 3,4)
Refer to these threads below for more information.
Custom Order in Oracle SQL
DECODE
What is the difference between ROWNUM and ROW_NUMBER ?
ROWNUM is a "pseudocolumn" that assigns a number to each row returned by a query:
SQL> select rownum, ename, deptno
2 from emp;
ROWNUM ENAME DEPTNO
---------- ---------- ----------
1 SMITH 99
2 ALLEN 30
3 WARD 30
4 JONES 20
5 MARTIN 30
6 BLAKE 30
7 CLARK 10
8 SCOTT 20
9 KING 10
10 TURNER 30
11 FORD 20
12 MILLER 10
ROW_NUMBER is an analytic function that assigns a number to each row according to its ordering within a group of rows:
SQL> select ename, deptno, row_number() over (partition by deptno order by ename) rn
2 from emp;
ENAME DEPTNO RN
---------- ---------- ----------
CLARK 10 1
KING 10 2
MILLER 10 3
FORD 20 1
JONES 20 2
SCOTT 20 3
ALLEN 30 1
BLAKE 30 2
MARTIN 30 3
TURNER 30 4
WARD 30 5
SMITH 99 1
From a little reading, ROWNUM is a value automatically assigned by Oracle to a rowset (prior to ORDER BY being evaluated, so don't ever ORDER BY ROWNUM or use a WHERE ROWNUM < 10 with an ORDER BY).
ROW_NUMBER() appears to be a function for assigning row numbers to a result set returned by a subquery or partition.
Apart from the other differences mentioned in answers, you should also consider performance. There is a non-authoritative but very interesting report here, comparing various means of pagination, among which the use of ROWNUM compared to ROW_NUMBER() OVER():
http://www.inf.unideb.hu/~gabora/pagination/results.html
rownum is a pseudocolumn which can be added to any select query, to number the rows returned (starting with 1). They are ordered according to when they were identified as being part of the final result set. (#ref)
row_number is an analytic's function, which can be used to number the rows returned by the query in an order mandated by the row_number() function.
Rownum starts with 1 ..increases after condition evaluated results to true .
Hence rownum >=1 returns all rows in table