Oracle-Making condition optional in Select statement - sql

How to make a condition optional?
That is, if a condition of the where clause is FALSE, how to ignore the condition in a simplest way?
The expected result of my code is,
RR
--
100
200
My Code is,
WITH DATASET AS (
SELECT 1 A, 10 B, 100 RR FROM DUAL
UNION
SELECT NULL A, 10 B, 200 RR FROM DUAL
)
SELECT RR
FROM DATASET
WHERE
A=2 -- How to make this condition optional(that is, as 2 is not found then,ignoring this condition)
AND
B=10
;

I understood that if not exists 2 in all data you do not want to filter but if there is a 2 in A column then you want to it.
so try this:
SOLUTION 1:
SELECT * FROM
(
WITH DATASET AS (
SELECT 1 A, 10 B, 100 RR FROM DUAL
UNION
SELECT NULL A, 10 B, 200 RR FROM DUAL
)
SELECT
RR
FROM
DATASET
WHERE B=10
AND EXISTS
(
SELECT 1 FROM DATASET WHERE A=2
)
)
UNION
SELECT * FROM
(
WITH DATASET AS (
SELECT 1 A, 10 B, 100 RR FROM DUAL
UNION
SELECT NULL A, 10 B, 200 RR FROM DUAL
)
SELECT
RR
FROM
DATASET
WHERE B=10
AND NOT EXISTS
(
SELECT 1 FROM DATASET WHERE A=2
)
)
SOLUTION2 :
WITH DATASET AS (
SELECT 1 A, 10 B, 100 RR FROM DUAL
UNION
SELECT NULL A, 10 B, 200 RR FROM DUAL
)
,SUB AS (
SELECT DISTINCT 1 A_EXISTS,RR
FROM DATASET
WHERE A=2
),
SUB2 AS (
SELECT DISTINCT
CASE
WHEN SUB.A_EXISTS IS NOT NULL AND DATASET.A=2
THEN SUB.RR
WHEN SUB.A_EXISTS IS NOT NULL AND DATASET.A<>2
THEN NULL
ELSE DATASET.RR
END RR
FROM
DATASET
FULL OUTER JOIN SUB ON A_EXISTS=1
WHERE DATASET.B=10
)
SELECT
*
FROM SUB2
WHERE RR IS NOT NULL

Related

I want to achieve following results in SQL using just select statement (no package or function). How can I do it?

I want to achieve following results in SQL using just select statement (no package or function). How can I do it?
C is a calculation column i.e C=A*B and it is dependent on A(where type = X)
OP Typ A B C
10 X 0.5 2 1
10 X 2 4 8
10 X 1.5 2 3
10 Y 12 2 24 <----If Typ = Y then A = (1+8+3)
20 X 2 3 6
20 X 3 1.5 4.5
20 Y 46.6 1 46.5 <----If Typ = Y then A = (1+8+3+24+6+4.5)
30 X 1 1 1
30 X 2 1.5 3
30 Y 96 0.5 48 <----If Typ = Y then A = (1+8+3+24+6+4.5+46.5+1+3)
If Typ = X then A will be from database
Data is sorted by Op,Type ascending
You need SUM() OVER () Analytic function with conditional for the column A, assuming you have an ID column(Opr_seq) mentioned as operation sequence :
SELECT Op, Typ,
CASE WHEN Typ='Y'
THEN
SUM(C) OVER (ORDER BY Opr_seq) - C
ELSE
A
END AS A,
B, C
FROM t
ORDER BY Opr_seq
Demo
SQL statements where rows reference rows, which reference rows, can be solved with either a recursive common table expression, a CONNECT BY statement, or a MODEL expression. A CONNECT BY solution would probably be smaller, and a MODEL solution would probably be faster, but both would also probably be even more cryptic, so below is a solution using recursive common table expressions.
with results(op, typ, a, b, c, runningTotal, rownumber) as
(
select op, typ, a, b, c,
case when typ = 'X' then c else 0 end runningTotal,
rownumber
from
(
select op, typ, a, b,
case when typ = 'X' then a*b else null end c,
row_number() over (order by op, typ) rownumber
from sample_data
)
where rownumber = 1
union all
select
next_row.op,
next_row.typ,
case when next_row.typ = 'Y' then runningTotal else next_row.a end a,
next_row.b,
case when next_row.typ = 'Y' then runningTotal else next_row.a end * next_row.b c,
runningTotal + case when next_row.typ = 'Y' then runningTotal else next_row.a end * next_row.b runningTotal,
next_row.rownumber
from results
join
(
select op, typ, a, b,
case when typ = 'X' then a*b else null end c,
row_number() over (order by op, typ) rownumber
from sample_data
) next_row
on results.rownumber+1 = next_row.rownumber
)
select op, typ, A, B, C from results;
I used t his sample table:
-- drop table sample_data;
create table sample_data as
select 10 op, 'X' Typ, 0.5 A, 2 B from dual union all
select 10 op, 'X' Typ, 2 A, 4 B from dual union all
select 10 op, 'X' Typ, 1.5 A, 2 B from dual union all
select 10 op, 'Y' Typ, null A, 2 B from dual union all
select 20 op, 'X' Typ, 2 A, 3 B from dual union all
select 20 op, 'X' Typ, 3 A, 1.5 B from dual union all
select 20 op, 'Y' Typ, null A, 1 B from dual union all
select 30 op, 'X' Typ, 1 A, 1 B from dual union all
select 30 op, 'X' Typ, 2 A, 1.5 B from dual union all
select 30 op, 'Y' Typ, null A, 0.5 B from dual;
My results are slightly different than yours, but I think that's only because your sample has a few minor addition errors. Here's a SQL Fiddle.

Oracle: Analytical functions Sub totals after each change in value

I have the following data (order of records as in the example):
A B
1 10
1 20
1 30
1 40
2 50
2 65
2 75
1 89
1 100
from SQL:
with x as (
select A, B
from (
select 1 as A, 10 as B from dual
union all
select 1 as A, 20 as B from dual
union all
select 1 as A, 30 as B from dual
union all
select 1 as A, 40 as B from dual
union all
select 2 as A, 50 as B from dual
union all
select 2 as A, 65 as B from dual
union all
select 2 as A, 75 as B from dual
union all
select 1 as A, 89 as B from dual
union all
select 1 as A, 100 as B from dual
)
)
select A, B
from X
I want to group the data for each change of value in column A,
I want to get the following result:
A MIN(B) MAX(B)
1 10 40
2 50 75
1 89 100
How to get such a result in the ORACLE 11. I would expect a simple implementation...
This is a gaps and islands problem, solved using row_number analytic function
SELECT a,
MIN(b),
MAX(b)
FROM (
SELECT x.*,
ROW_NUMBER() OVER(
ORDER BY b
) - ROW_NUMBER() OVER(
PARTITION BY a
ORDER BY b
) AS seq
FROM x
)
GROUP BY a,
seq;
Demo

oracle hierarchy query error ORA-01436: CONNECT BY loop in user data

i have a table called abc
columns and data are like:-
a b c
------------
ab 1 2
bc 2 3
cd 3 4
de 4 5
maybe there is data for cyclic like-
ef 5 1
i want output as:-
A b d
------------
ab 1 5/4/3/2/1
bc 2 5/4/3/2
cd 3 5/4/3
de 4 5/4
ef 5 5
please help me out find a query to retrive data like this
with
my_data(a,b,c) AS (
select 'ab', 1, 2 from dual union all
select 'bc', 2, 3 from dual union all
select 'cd', 3, 4 from dual union all
select 'de', 4, 5 from dual union all
select 'ea', 5, 1 from dual
)
select a, b, sys_connect_by_path(b, '/') d
from my_data t
connect by nocycle c = prior b
start with c = 1
order by a;
You may use listagg with select level from dual connect by level <= ? statement, and do not need column c( and column a is only needed for output ), as in the following routine :
create table tab( a varchar2(2), b int );
insert all
into tab values('ab',1)
into tab values('bc',2)
into tab values('cd',3)
into tab values('de',4)
into tab values('ef',5)
select * from dual;
select a, b,
( select listagg(6-level,'/') within group (order by level)
from dual
connect by level <= 6-b ) as d
from tab t;
SQL Fiddle Demo
You may use a recursive CTE with a CYCLE clause to avoid cyclic data
WITH cte(a, b, c, d) AS
( SELECT a,
b,
c,
to_char(b) AS d
FROM abc
WHERE c = 1
UNION ALL SELECT t.a,
t.b,
t.c,
cte.d ||'/'||t.b
FROM abc t
JOIN cte ON (cte.b = t.c) )
CYCLE b SET CYCLE TO 1 DEFAULT 0
SELECT a,
b,
d
FROM cte
WHERE CYCLE = 0
ORDER BY b ;
Demo

Cannot figure out logic to update a table

I have below table.
TABLE: ABCD
B column have value 1 whenever there is a change in A column. Now I have to update the table like below. How can I do that?
You can do this using a correlated subquery:
update t
set b = (select sum(t2.b) from t t2 where t2.A <= t.A);
This is standard SQL and should work in either Oracle or Teradata.
Lets have a slightly more complicated example (where the changes in B are not correlated to the changes in A):
Oracle Setup:
CREATE TABLE ABCD( A, B ) AS
SELECT 1, 0 FROM DUAL UNION ALL
SELECT 1, 0 FROM DUAL UNION ALL
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 1 FROM DUAL UNION ALL
SELECT 2, 0 FROM DUAL UNION ALL
SELECT 3, 0 FROM DUAL UNION ALL
SELECT 3, 1 FROM DUAL UNION ALL
SELECT 3, 0 FROM DUAL;
Update:
UPDATE ABCD t1
SET b = (
SELECT sm
FROM (
SELECT ROWID AS id,
SUM( b ) OVER ( ORDER BY a, ROWNUM ) AS sm
FROM ABCD
) t2
WHERE t1.ROWID = t2.ID
);
Output:
SELECT * FROM ABCD;
A B
- -
1 0
1 0
1 1
2 2
2 2
3 2
3 3
3 3
(Note: This is an Oracle solution; I have no idea if it will or won't work in Teradata.)

Use a calculated field in the where clause

Is there a way to use a calculated field in the where clause?
I want to do something like
SELECT a, b, a+b as TOTAL FROM (
select 7 as a, 8 as b FROM DUAL
UNION ALL
select 8 as a, 8 as b FROM DUAL
UNION ALL
select 0 as a, 0 as b FROM DUAL
)
WHERE TOTAL <> 0
;
but I get ORA-00904: "TOTAL": invalid identifier.
So I have to use
SELECT a, b, a+b as TOTAL FROM (
select 7 as a, 8 as b FROM DUAL
UNION ALL
select 8 as a, 8 as b FROM DUAL
UNION ALL
select 0 as a, 0 as b FROM DUAL
)
WHERE a+b <> 0
;
Logically, the select clause is one of the last parts of a query evaluated, so the aliases and derived columns are not available. (Except to order by, which logically happens last.)
Using a derived table is away around this:
select *
from (SELECT a, b, a+b as TOTAL FROM (
select 7 as a, 8 as b FROM DUAL
UNION ALL
select 8 as a, 8 as b FROM DUAL
UNION ALL
select 0 as a, 0 as b FROM DUAL)
)
WHERE TOTAL <> 0
;
This will work...
select *
from (SELECT a, b, a+b as TOTAL FROM (
select 7 as a, 8 as b FROM DUAL
UNION ALL
select 8 as a, 8 as b FROM DUAL
UNION ALL
select 0 as a, 0 as b FROM DUAL)
) as Temp
WHERE TOTAL <> 0;