COUNT(USER_ID) Without GROUP BY - sql

Table_A Data :
NAME USER_ID V_NAME NUMBER
AUTO 1 HOME 123
CAT 2 HAT 456
DOT 3 789
FAN 4 REG
I was looking for output like
No of distinct users no.of ID's no.of V_Name no.of users with no.of
NUMBER column data users without NUMBER column data
4 4 3 3 1
Query :
SELECT
count(distinct NAME) AS No_of_Names,
count (distinct ID) AS No_of_ID,
(select count(distinct ID) from TABLE_A where NUMBER is not null) as No_of_users_with_Number_Data,
(select count(distinct ID) from TABLE_A where NUMBER is null) as No_of_users_without_Number_Data,
count (distinct V_NAME) as No_of_V_NAME FROM TABLE_A;
But with above query I am getting error :
ORA-00937: not a single-group group function.
Please help in getting the data in above format. Thanks for your help in advance

You can use:
SELECT COUNT(DISTINCT NAME) AS No_of_Names,
COUNT(DISTINCT ID) AS No_of_ID,
COUNT(DISTINCT V_NAME) as No_of_V_NAME,
COUNT(DISTINCT CASE WHEN "NUMBER" IS NOT NULL THEN ID END)
AS No_of_users_with_Number_Data,
COUNT(DISTINCT CASE WHEN "NUMBER" IS NULL THEN ID END)
as No_of_users_without_Number_Data
FROM table_a;
Which, for the sample data:
CREATE TABLE table_a (name, id, v_name, "NUMBER") as
SELECT 'AUTO', 1, 'HOME', 123 FROM DUAL UNION ALL
SELECT 'CAT', 2, 'HAT', 456 FROM DUAL UNION ALL
SELECT 'DOT', 3, NULL, 789 FROM DUAL UNION ALL
SELECT 'FAN', 4, 'REG' , NULL FROM DUAL;
(Note: You should not name your columns NUMBER as that is a data type.)
Outputs:
NO_OF_NAMES
NO_OF_ID
NO_OF_V_NAME
NO_OF_USERS_WITH_NUMBER_DATA
NO_OF_USERS_WITHOUT_NUMBER_DATA
4
4
3
3
1
If you then add extra rows:
INSERT INTO table_a VALUES ('DOT', 3, NULL, NULL);
INSERT INTO table_a VALUES ('FAN', 4, 'XYZ', 321);
Then the query output is:
NO_OF_NAMES
NO_OF_ID
NO_OF_V_NAME
NO_OF_USERS_WITH_NUMBER_DATA
NO_OF_USERS_WITHOUT_NUMBER_DATA
4
4
4
4
2
(Note: the output in the last two columns is a count of distinct users and not a count of rows.)
db<>fiddle here

Here's one option:
SQL> with table_a (name, user_id, v_name, num) as
2 (select 'auto', 1, 'home', 123 from dual union all
3 select 'cat' , 2, 'hat' , 456 from dual union all
4 select 'dot' , 3, null , 789 from dual union all
5 select 'fan' , 4, 'reg' , null from dual
6 )
7 select
8 count(distinct name) cnt_1,
9 count(distinct user_id) cnt_2,
10 sum(case when v_name is not null then 1 else 0 end) cnt_3,
11 sum(case when num is not null then 1 else 0 end) cnt_4,
12 sum(case when num is not null then 0 else 1 end) cnt_5
13 from table_a;
CNT_1 CNT_2 CNT_3 CNT_4 CNT_5
---------- ---------- ---------- ---------- ----------
4 4 3 3 1
SQL>

Related

Oracle how to return rows based on condition?

I am trying to return id and name based on flag column. If id has a rows with flag = 1 my query should only return these rows. If it hasn't flag=1 value it should return rows with flag = 0. What is the best way for it ? Here is sample data :
id name flag
5 aa 1
5 bb 0
6 cc 1
10 dd 0
11 ee 1
11 ee 0
Expected output is :
id name flag
5 aa 1
6 cc 1
10 dd 0
11 ee 1
Assuming flag column contains only 0 or 1, select rows whose flag is equal to maximal value of flags of given id:
select id, name, flag
from (
select id, name, flag, max(flag) over (partition by id) as m
from your_table
) x
where x.flag = x.m
You can use the keep dense_rank aggregating function to acheive that like below.
with t (id, name, flag) as (
select 5 , 'aa', 1 from dual union all
select 5 , 'bb', 0 from dual union all
select 6 , 'cc', 1 from dual union all
select 10, 'dd', 0 from dual union all
select 11, 'ee', 1 from dual union all
select 11, 'ee', 0 from dual
)
select id
, max(name)keep(dense_rank last order by id, flag) name
, max(flag)keep(dense_rank last order by id, flag) flag
from t
where flag in (0, 1)
group by id
order by id
;

Filter based on condition in WHERE clause

I have a table where I have to pick one of two if it is present. For example if a ID has ACCEPTED and SETTLED , I have to only pick SETTLED else the remaining. Only ACCEPTED/SETTLED always comes as duplicates
Input:
Output:
Query Tried:
SELECT * FROM TABLE
WHERE CASE WHEN "Status" IN ('ACCEPTED','SETTLED') THEN 'SETTLED'
WHEN "Status" IN ('ACCEPTED') THEN 'ACCEPTED'
ELSE "Status" END In ('SETTLED','ACCEPTED')
If your groups are defined by ID and Amount, you could do something like:
SELECT
t.ID,
MAX(t.Status),
t.Amount
FROM t
GROUP BY t.ID, t.Amount
ORDER BY t.ID
db<>fiddle
This is one option (sample data in lines #1 - 7; query begins at line #8). It ranks statuses so that SETTLED comes first, and then the rest of them.
SQL> with test (id, status, amount) as
2 (select 1, 'ACCEPTED', 13 from dual union all
3 select 1, 'SETTLED' , 13 from dual union all
4 select 2, 'SETTLED' , 155 from dual union all
5 select 3, 'ACCEPTED', 123 from dual union all
6 select 4, 'REJECTED', 140 from dual
7 )
8 select id, status, amount
9 from (select id, status, amount,
10 row_number() over (partition by id
11 order by case when status = 'SETTLED' then 1 else 2 end) rn
12 from test
13 )
14 where rn = 1;
ID STATUS AMOUNT
---------- -------- ----------
1 SETTLED 13
2 SETTLED 155
3 ACCEPTED 123
4 REJECTED 140
SQL>

Get last value from a certain group (Oracle)

I have something like this
Date Group ID
11/01 'A' 1
12/01 'A' 2
13/01 'B' 3
14/01 'B' 4
What i basically want is to get for example the latest from group 'A'
Date Group ID LatestID_from_GROUP_A_ordered_by_recent_date
11/01 'A' 1 2
12/01 'A' 2 2
13/01 'B' 3 2
14/01 'B' 4 2
or at least something like this
Date Group ID LatestID_from_GROUP_A_ordered_by_recent_date
11/01 'A' 1 null
12/01 'A' 2 null
13/01 'B' 3 2
14/01 'B' 4 2
How about this:
with demo (somedate, somegroup, id) as
( select date '2018-01-11', 'A', 1 from dual union all
select date '2018-01-12', 'A', 2 from dual union all
select date '2018-01-13', 'B', 3 from dual union all
select date '2018-01-14', 'B', 4 from dual union all
select date '2018-01-15', 'A', 5 from dual -- example from comments
)
select somedate, somegroup, id
, ( select max(id) keep (dense_rank last order by somedate)
from demo
where somegroup = 'A' ) as last_a
from demo;
SOMEDATE SOMEGROUP ID LAST_A
----------- --------- ---------- ----------
11/01/2018 A 1 5
12/01/2018 A 2 5
13/01/2018 B 3 5
14/01/2018 B 4 5
15/01/2018 A 5 5
Note the max(id) is only a tiebreaker in the event of multiple rows with the last date.
Gordon was almost there.
You want to create a window over your whole query, but only pick the biggest value of 'A':
select
t.*,
max(case when group = 'A' then id end) over (partition by 1) as latest_from_a
from t
'partition by 1' will create a window of your complete result set because it only groups by a single static value: 1.
The logic seems to be:
select t.*,
max(case when group = 'A' then id end) over (order by date) as latest_from_a
from t;
The above gets the cumulative maximum up to each date. If you want the overall maximum:
select t.*,
max(case when group = 'A' then id end) over () as latest_from_a
from t;

ORACLE get rows with condition value equals something but not equals to anything else

I have rows that look like .
OrderNo OrderStatus SomeOtherColumn
A 1
A 1
A 3
B 1 X
B 1 Y
C 2
C 3
D 2
I want to return all orders that have only one possible value of orderstatus. For e.g Here order B has only order status 1 SO result should be
B 1 X
B 1 Y
Notes:
Rows can be duplicated with same order status. For e.g. B here.
I am interested in the order having a very peculiar status for e.g. 1 here and not having any other status. So if B had a status of 3 at any point of time it is disqualified.
You can use not exists:
select t.*
from t
where not exists (select 1
from t t2
where t.orderno = t2.orderno and t.OrderStatus = t2.OrderStatus
);
If you just want the orders where this is true, you can use group by and having:
select orderno
from t
group by orderno
having min(OrderStatus) = max(OrderStatus);
If you only want a status of 1 then add max(OrderStatus) = 1 to the having clause.
Here is one way to do it. It does not handle the case where the status can be NULL; if that is possible, you will need to explain how you want it handled.
SQL> create table test_data ( orderno, status, othercol ) as (
2 select 'A', 1, null from dual union all
3 select 'A', 1, null from dual union all
4 select 'A', 3, null from dual union all
5 select 'B', 1, 'X' from dual union all
6 select 'B', 1, 'Y' from dual union all
7 select 'C', 2, null from dual union all
8 select 'C', 3, null from dual union all
9 select 'D', 2, null from dual
10 );
Table created.
SQL> variable input_status number
SQL> exec :input_status := 1
PL/SQL procedure successfully completed.
SQL> column orderno format a8
SQL> column othercol format a8
SQL> select orderno, status, othercol
2 from (
3 select t.*, count(distinct status) over (partition by orderno) as cnt
4 from test_data t
5 )
6 where status = :input_status
7 and cnt = 1
8 ;
ORDERNO STATUS OTHERCOL
-------- ---------- --------
B 1 X
B 1 Y
One way to handle NULL status (if that may happen), if in that case the orderno should be rejected (not included in the output), is to define the cnt differently:
count(case when status != :input_status or status is null then 1 end)
over (partition by orderno) as cnt
and in the outer query change the WHERE clause to a single condition,
where cnt = 0
Count distinct OrderStatus partitioned by OrderNo and show only rows where number equals one:
select OrderNo, OrderStatus, SomeOtherColumn
from ( select t.*, count(distinct orderstatus) over (partition by orderno) cnt
from t )
where cnt = 1
SQLFiddle demo
Just wanted to add something to Gordon's answer, using a stats function:
select orderno
from t
group by orderno
having variance(orderstatus) = 0;

Get distinct rows based on priority?

I have a table as below.i am using oracle 10g.
TableA
------
id status
---------------
1 R
1 S
1 W
2 R
i need to get distinct ids along with their status. if i query for distinct ids and their status i get all 4 rows.
but i should get only 2. one per id.
here id 1 has 3 distinct statuses. here i should get only one row based on priority.
first priority is to 'S' , second priority to 'W' and third priority to 'R'.
in my case i should get two records as below.
id status
--------------
1 S
2 R
How can i do that? Please help me.
Thanks!
select
id,
max(status) keep (dense_rank first order by instr('SWR', status)) as status
from TableA
group by id
order by 1
fiddle
select id , status from (
select TableA.*, ROW_NUMBER()
OVER (PARTITION BY TableA.id ORDER BY DECODE(
TableA.status,
'S',1,
'W',2,
'R',3,
4)) AS row_no
FROM TableA)
where row_no = 1
This is first thing i would do, but there may be a better way.
Select id, case when status=1 then 'S'
when status=2 then 'W'
when status=3 then 'R' end as status
from(
select id, max(case when status='S' then 3
when status='W' then 2
when status='R' then 1
end) status
from tableA
group by id
);
To get it done you can write a similar query:
-- sample of data from your question
SQL> with t1(id , status) as (
2 select 1, 'R' from dual union all
3 select 1, 'S' from dual union all
4 select 1, 'W' from dual union all
5 select 2, 'R' from dual
6 )
7 select id -- actual query
8 , status
9 from ( select id
10 , status
11 , row_number() over(partition by id
12 order by case
13 when upper(status) = 'S'
14 then 1
15 when upper(status) = 'W'
16 then 2
17 when upper(status) = 'R'
18 then 3
19 end
20 ) as rn
21 from t1
22 ) q
23 where q.rn = 1
24 ;
ID STATUS
---------- ------
1 S
2 R
select id,status from
(select id,status,decode(status,'S',1,'W',2,'R',3) st from table) where (id,st) in
(select id,min(st) from (select id,status,decode(status,'S',1,'W',2,'R',3) st from table))
Something like this???
SQL> with xx as(
2 select 1 id, 'R' status from dual UNION ALL
3 select 1, 'S' from dual UNION ALL
4 select 1, 'W' from dual UNION ALL
5 select 2, 'R' from dual
6 )
7 select
8 id,
9 DECODE(
10 MIN(
11 DECODE(status,'S',1,'W',2,'R',3)
12 ),
13 1,'S',2,'W',3,'R') "status"
14 from xx
15 group by id;
ID s
---------- -
1 S
2 R
Here, logic is quite simple.
Do a DECODE for setting the 'Priority', then find the MIN (i.e. one with Higher Priority) value and again DECODE it back to get its 'Status'
Using MOD() example with added values:
SELECT id, val, distinct_val
FROM
(
SELECT id, val
, ROW_NUMBER() OVER (ORDER BY id) row_seq
, MOD(ROW_NUMBER() OVER (ORDER BY id), 2) even_row
, (CASE WHEN id = MOD(ROW_NUMBER() OVER (ORDER BY id), 2) THEN NULL ELSE val END) distinct_val
FROM
(
SELECT 1 id, 'R' val FROM dual
UNION
SELECT 1 id, 'S' val FROM dual
UNION
SELECT 1 id, 'W' val FROM dual
UNION
SELECT 2 id, 'R' val FROM dual
UNION -- comment below for orig data
SELECT 3 id, 'K' val FROM dual
UNION
SELECT 4 id, 'G' val FROM dual
UNION
SELECT 1 id, 'W' val FROM dual
))
WHERE distinct_val IS NOT NULL
/
ID VAL DISTINCT_VAL
--------------------------
1 S S
2 R R
3 K K
4 G G