Count occurences of values in table, when I treat one value as occurence of all other values - sql

I have a table with one column (just to simplify the problem) with values 0-23 or *.
I want to count occurrences of each value 0-23, but treat * as occurrence of all other values
for example:
column_name
-------------
3
4
5
6
7
*
4
4
3
*
I want to get something like that:
column_name | count
--------------------
1 | 2
2 | 2
3 | 4
4 | 5
5 | 3
6 | 3
7 | 3
.....
I tried experimenting with different count and "group by" methods, but always getting very strange results. Basically the main problem here is to how count rows when I need to have one value in all other groups.

You could use analytic function that counts values where * is replaced by actual value between 0 and 23:
SELECT DISTINCT n.RN "COL_1", Count(REPLACE(t.COL_1, '*', n.RN)) OVER(Partition By n.RN) "CNT"
FROM tbl t
INNER JOIN ( Select To_Char(LEVEL - 1) "RN" From Dual Connect By LEVEL <=24 ) n ON(n.RN = REPLACE(t.COL_1, '*', n.RN))
WHERE n.RN IN(SELECT COL_1 FROM tbl)
ORDER BY To_Number(n.RN)
which with your sample data:
WITH
tbl (COL_1) AS
(
Select '3' From Dual Union All
Select '4' From Dual Union All
Select '5' From Dual Union All
Select '6' From Dual Union All
Select '7' From Dual Union All
Select '*' From Dual Union All
Select '4' From Dual Union All
Select '4' From Dual Union All
Select '3' From Dual Union All
Select '*' From Dual Union All
Select '3' From Dual
)
... results as:
COL_1 CNT
---------------------------------------- ----------
3 5
4 5
5 3
6 3
7 3
... and if you exclude the Where clause you will get all the rows (0 - 23) with number of occurances counted by REPLACE of * with any of the numbers
COL_1 CNT
---------------------------------------- ----------
0 2
1 2
2 2
3 5
4 5
5 3
6 3
7 3
8 2
9 2
10 2
11 2
12 2
13 2
14 2
15 2
16 2
17 2
18 2
19 2
20 2
21 2
22 2
23 2

You can do it using successive WITH's :
First one to calculate number of occurrence of *.
And the second is to calculate number of occurrence of each number.
with cte as (
select count(1) as c
from mytable
where column_name = '*'
),
cte2 as (
select column_name, count(1) as c
from mytable, cte
group by column_name
)
select column_name, cte.c + cte2.c
from cte2, cte;

You can with nested statements too,
SELECT ID,(count_ + (
SELECT COUNT(ID) FROM sql_test_a
WHERE ID = '*')) as count_
FROM (
SELECT ID,COUNT(ID) as count_
FROM sql_test_a WHERE ID != '*' GROUP BY ID);

Related

Oracle SQL - Create identifier for couple of values

I'm struggling with the following problem, I have the follwing data in a table:
Param ID
Param Val
Other Cols
1
15
XXX
1
15
XXX
1
16
XXX
1
16
XXX
2
21
XXX
2
21
XXX
2
22
XXX
2
22
XXX
I would like to select a new colum in order to create 4 sets of data to have all the possible combination between the values of parameter 1 and 2; so I would like to obtain something like this:
Set
Param ID
Param Val
Other Cols
1
1
15
XXX
2
1
15
XXX
3
1
16
XXX
4
1
16
XXX
1
2
21
XXX
3
2
21
XXX
2
2
22
XXX
4
2
22
XXX
So for example for the Set 1 I will have the Couple of values 15 and 21, for the set 2 the values 15 and 22 etc etc.
I tried using different analytic functions, but I was not able to have what I need.
Thanks in advance.
Despite a good hint on the MODEL clause, I guess I will go for a solution combining cross join and unpivot, maybe it is not the best, but it fit my needs.
WITH tbl AS
(
Select 1 "ID", 15 "VAL" From Dual Union All
Select 1 "ID", 16 "VAL" From Dual Union All
Select 2 "ID", 21 "VAL" From Dual Union All
Select 2 "ID", 22 "VAL" From Dual )
SELECT *
FROM (
SELECT ROWNUM AS SET_ID,
id1,
id4
FROM (
SELECT
CASE a.id
WHEN 1 THEN a.val
ELSE 0
END AS id1,
CASE a.id
WHEN 2 THEN a.val
ELSE 0
END AS id2,
CASE b.id
WHEN 1 THEN b.val
ELSE 0
END AS id3,
CASE b.id
WHEN 2 THEN b.val
ELSE 0
END AS id4
FROM tbl a,
tbl b)
WHERE id2 = 0
AND id3 = 0) UNPIVOT (VAL FOR ID IN (id1 AS '1',
id4 AS '2'))
that results in :
SET_ID
ID
VAL
1
1
15
1
2
21
2
1
15
2
2
22
3
1
16
3
2
21
4
1
16
4
2
22
tried with all the combination of source data, and it seems work :)
Sample data:
WITH
tbl AS
(
Select 1 "ID", 15 "VAL" From Dual Union All
Select 1 "ID", 15 "VAL" From Dual Union All
Select 1 "ID", 16 "VAL" From Dual Union All
Select 1 "ID", 16 "VAL" From Dual Union All
Select 2 "ID", 21 "VAL" From Dual Union All
Select 2 "ID", 21 "VAL" From Dual Union All
Select 2 "ID", 22 "VAL" From Dual Union All
Select 2 "ID", 22 "VAL" From Dual
)
UPDATED AFTER THE COMMENT
If you want the combinations then you will have to use the MODEL clause. To do that you should prepare the data (cte named grid) a bit so you could do the addressing to the particular data and manage all the combinations you want. It looks like here:
grid AS
( Select Distinct
Sum(1) OVER(Partition By ID Order By ID ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "IDS_TOTAL_ORDER",
Sum(1) OVER(Partition By VAL Order By VAL ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "VALS_TOTAL_ORDER",
ID "ID",
VAL "VAL"
From
tbl
Order By ID
)
Select SET_ID, ID, VAL
From ( Select 0 "SET_ID", IDS_TOTAL_ORDER, VALS_TOTAL_ORDER, ID "ID", VAL "VAL"
From grid
)
MODEL
Dimension By (ID, IDS_TOTAL_ORDER, VALS_TOTAL_ORDER)
Measures(SET_ID, VAL)
RULES
(
SET_ID[1, ANY, ANY] = CV(IDS_TOTAL_ORDER),
SET_ID[2, 1, 1] = CV(IDS_TOTAL_ORDER),
SET_ID[2, 2, 2] = CV(IDS_TOTAL_ORDER) + 1,
SET_ID[2, 3, 1] = CV(IDS_TOTAL_ORDER) - 1,
SET_ID[2, 4, 2] = CV(IDS_TOTAL_ORDER)
)
Order By ID, IDS_TOTAL_ORDER
This way you can get any combination. Here is your result:
SET_ID
ID
VAL
1
1
15
2
1
15
3
1
16
4
1
16
1
2
21
3
2
21
2
2
22
4
2
22
More about MODEL clause: https://www.oracle.com/webfolder/technetwork/tutorials/obe/db/10g/r2/prod/bidw/sqlmodel/sqlmodel_otn.htm
Sets now looks like here:
SET_ID
IDS_VALS
1
1/15, 2/21
2
1/15, 2/22
3
1/16, 2/21
4
1/16, 2/22
Regards...

select data based on the next rows and ids

I have the following table and data,
TABLE - DEAL
ID DEALID
1 DEAL1
2 DEAL2
ALL DEAL2
3 DEAL2
4 DEAL5
5 DEAL5
ALL DEAL6
I want to get only the data as below
ID DEALID
1 DEAL1
4 DEAL5
5 DEAL5
ALL DEAL6
I want to select data based on the value of id column and dealid column.
If the value of id is 'ALL' and corresponding dealid repeats, omit all records with that dealid
Based on sample data you posted, see if this helps. Read comments within code.
SQL> with
2 test (id, dealid) as
3 -- sample data
4 (select '1' , 'deal1' from dual union all
5 select '2' , 'deal2' from dual union all
6 select 'ALL', 'deal2' from dual union all
7 select '3' , 'deal2' from dual union all
8 select '4' , 'deal5' from dual union all
9 select '5' , 'deal5' from dual union all
10 select 'ALL', 'deal6' from dual
11 ),
12 all2 as
13 -- DEALIDs that contain different ID values, and MAX of them is ALL
14 (select dealid
15 from test
16 group by dealid
17 having min(id) <> max(id)
18 and max(id) = 'ALL'
19 )
20 select t.id, t.dealid
21 from test t join all2 a on a.dealid <> t.dealid;
ID DEALI
--- -----
1 deal1
4 deal5
5 deal5
ALL deal6
SQL>

Oracle - generate a running number by group

I need to generate a running number / group sequence inside a select statement for a group of data.
For example
Group Name Sequence
1 a 1
1 b 2
1 c 3
2 d 1
2 e 2
2 f 3
So for each group the sequence should be a running number starting with 1 depending on the order of column"Name".
I already pleayed around with Row_Number() and Level but I couldn't get a solution.
Any idea how to do it?
Analytic functions help.
SQL> with test (cgroup, name) as
2 (select 1, 'a' from dual union all
3 select 1, 'b' from dual union all
4 select 1, 'c' from dual union all
5 select 2, 'd' from dual union all
6 select 2, 'e' from dual union all
7 select 2, 'f' from dual
8 )
9 select cgroup,
10 name,
11 row_number() over (partition by cgroup order by name) sequence
12 from test
13 order by cgroup, name;
CGROUP N SEQUENCE
---------- - ----------
1 a 1
1 b 2
1 c 3
2 d 1
2 e 2
2 f 3
6 rows selected.
SQL>
Try this
SELECT
"Group",
Name,
DENSE_RANK() OVER (PARTITION BY "Group" ORDER BY Name) AS Sequence
FROM table;

How to query for non-consecutive values?

I have a column of id: 1, 3, 4, 9, 10, 11 in the table called t_mark
How can I get the non-consecutive range? (e.g. [1, 3], [4, 9])
Alternatively, using LEAD analytic function, along with your fancy formatting. TEST CTE is what you already have; lines #9 onwards is what you need.
SQL> with test (col) as
2 (select 1 from dual union all
3 select 3 from dual union all
4 select 4 from dual union all
5 select 9 from dual union all
6 select 10 from dual union all
7 select 11 from dual
8 ),
9 temp as
10 (select col,
11 lead(col) over (order by col) lcol
12 from test
13 )
14 select '[' || col ||' - '|| lcol ||']' result
15 From temp
16 where lcol - col > 1
17 order by col;
RESULT
-------------------------------------------------------
[1 - 3]
[4 - 9]
SQL>
[EDIT: Adjusted so that you shouldn't have to think too much]
This is what you have:
SQL> select * From t_mark;
M_ID
----------
1
3
4
9
10
11
6 rows selected.
This is what you need:
SQL> with temp as
2 (select m_id,
3 lead(m_id) over (order by m_id) lm_id
4 from t_mark
5 )
6 select '[' || m_id ||' - '|| lm_id ||']' result
7 From temp
8 where lm_id - m_id > 1
9 order by m_id;
RESULT
------------------------------------------------------------------
[1 - 3]
[4 - 9]
SQL>
Basically, you should learn how to use a CTE (common table expression, a.k.a. the with factoring clause).
Assuming that by "list" you mean a table with a column, then you can do this with lag():
select prev_number, number
from (select t.*, lag(number) over (order by number) as prev_number
from t
) t
where prev_number <> number - 1;
This should do the trick :
WITH original_table(number_column) as (select 1 from dual union all
select 3 from dual union all
select 4 from dual union all
select 9 from dual union all
select 10 from dual union all
select 11 from dual),
numbers AS (
SELECT row_number() over (ORDER BY number_column ASC ) row_num,
number_column
FROM original_table
)
SELECT nb1.number_column AS lnumber,
nb2.number_column AS rnumber
FROM numbers nb1
INNER JOIN numbers nb2 ON nb1.row_num + 1 = nb2.row_num
AND nb1.number_column + 1 < nb2.number_column
Result :
| LNUMBER | RNUMBER |
|---------|---------|
| 1 | 3 |
| 4 | 9 |
Link to the dbfiddle for testing

where condition scenario in oracle

i have requirement in where condition
if my id is in 1 then it should check id 4,5,6,7 or it should check value which is in id
this id i will pass as parameter to query
select * from table_a where id
Help me in this
You can use the below
select * from individual ip
where (
( :p_prs_nat = 219 and ip.prs_nationality_id in (231,259,343) )
or (:p_prs_nat <> 219 and :p_prs_nat=ip.prs_nationality_id
))
where ip.prs_nationality_id =case when :p_prs_nat in( 219) then it shud check (231,259,343) else :p_prs_nat end how to achieve this functionality
You cannot directly use IN while returning the result in the THEN clause of CASE expression. However, you could first check the condition itself using AND operator and return TRUE whenever it matches.
For example,
SQL> WITH DATA AS
2 (
3 SELECT 1 ID, 'X' STR FROM DUAL UNION ALL
4 SELECT 2 ID, 'A' STR FROM DUAL UNION ALL
5 SELECT 3 ID ,'P' STR FROM DUAL UNION ALL
6 SELECT 4 ID ,'Q' STR FROM DUAL
7 )
8 SELECT *
9 FROM DATA
10 WHERE (
11 CASE
12 WHEN ID = 1
13 AND str IN ('A','Y','Z')
14 THEN 1
15 WHEN ID <> 1
16 THEN 1
17 END ) =1
18 /
ID S
---------- -
2 A
3 P
4 Q
SQL>
So, you did not get the row with ID = 1,since it did not match the condition AND str IN ('A','Y','Z').
If it would match, it will return those matching rows too:
SQL> WITH DATA AS
2 (
3 SELECT 1 ID, 'X' STR FROM DUAL UNION ALL
4 SELECT 2 ID, 'A' STR FROM DUAL UNION ALL
5 SELECT 3 ID ,'P' STR FROM DUAL UNION ALL
6 SELECT 4 ID ,'Q' STR FROM DUAL
7 )
8 SELECT *
9 FROM DATA
10 WHERE (
11 CASE
12 WHEN ID = 1
13 AND str IN ('X','Y','Z')
14 THEN 1
15 WHEN ID <> 1
16 THEN 1
17 END ) =1
18 /
ID S
---------- -
1 X
2 A
3 P
4 Q
SQL>