Oracle SQL: Select with variable and condition - sql

I'm creating a query with a condition depending on the result of a variable.
If PROD = 'alpha' I need to show only models 2,3,4,5 and 6;
if PROD = 'beta' that only models 0 and 1 appear.
DEFINE PROD = 'ALPHA'
SELECT CAR_CODE, CAR, MODELS
FROM TB_CAR_MODELS
WHERE (
CASE PROD
WHEN 'ALPHA' THEN MODELS IN (2,3,4,5,6)
WHEN 'BETA' THEN MODELS IN (0,1)
END
);
I created this query but it's not working. Returns the error:
00000 - "missing keyword"
Can you help me please? Thanks

A few sample rows:
SQL> select * from tb_car_models;
CAR_CODE CAR MODELS
---------- ---- ----------
1 BMW 2 -- ALPHA
2 Audi 5 -- ALPHA
3 Opel 0 -- BETA
SQL>
As I'm running it in SQL*Plus, I'm using a substitution variable:
SQL> select *
2 from tb_car_models
3 where models in (select models
4 from tb_car_models
5 where models in (2, 3, 4, 5, 6)
6 and '&&prod' = 'ALPHA'
7 union all
8 select models
9 from tb_car_models
10 where models in (0, 1)
11 and '&&prod' = 'BETA'
12 );
Enter value for prod: ALPHA
CAR_CODE CAR MODELS
---------- ---- ----------
1 BMW 2
2 Audi 5
SQL> undefine prod
SQL> /
Enter value for prod: BETA
CAR_CODE CAR MODELS
---------- ---- ----------
3 Opel 0
SQL>
Depending on tool you use, that might be e.g. ... and :prod = 'ALPHA' or similar.

There's a good answer here that I think will help you:
Missing Keyword (ORA-00905) - Oracle SQL Case Statement
Check that out.

You can modify your sql statement as following:
SELECT CAR_CODE, CAR,
(CASE S.PROD
WHEN CASE S.PROD = 'ALPHA' THEN MODELS IN (2,3,4,5,6)
WHEN CASE S.PROD = 'BETA' THEN MODELS IN (0,1)
END
) MODELS
FROM TB_CAR_MODELS S

Related

Oracle SQL : Updating a column with count query of another table

I have two tables.
The first one "DISORDERS" with the fields: "ID" number, "Description" varchar2, "MINIMUM_SYMPTOMS" number
with these data:
The second table is "SYMPTOMS" with the fields: "ID" number, "SYMPTOM" varchar2, "DISORDER_ID" number (FK_DISORDERS_ID)
In my second table i have these data:
With this query i can find the minimum symptoms (records from the second table) in order for my patient to have the disorder:
select DISORDERS.DISORDER as DISORDER, ROUND((COUNT(SYMPTOMS.ID) / 2) ,0) as MINIMUM_SYMPTOMS
from SYMPTOMS SYMPTOMS,
DISORDERS DISORDERS
where SYMPTOMS.DISORDER_ID=DISORDERS.ID
group by DISORDERS.DISORDER
and i am getting correctly this result:
I want to update in my first table the field "MINIMUM_SYMPTOMS" and put the MINIMUM_SYMPTOMS value from the last query.
I didn't feel like typing that much so I created smaller sample data set.
SQL> select * from disorders order by id;
ID DISO MINIMUM_SYMPTOMS
---------- ---- ----------------
64 OCRD 0
65 OCD 0
248 GPD 0
SQL> select * from symptoms order by disorder_id, id;
ID SYMPTO DISORDER_ID
---------- ------ -----------
2 test 3 64
3 test 1 64
4 test 5 64
5 test 2 64
7 test 4 64
3 test 1 65
5 test 2 65
7 test 4 65
3 test 1 248
4 test 3 248
5 test 2 248
11 rows selected.
Query - similar to yours - but based only on the symptoms table. Why? You don't need disorders now; this query will be used a little bit later, in merges using clause:
SQL> select s.disorder_id,
2 round((count(s.id) / 2), 0) minsym
3 from symptoms s
4 group by s.disorder_id;
DISORDER_ID MINSYM
----------- ----------
248 2
64 3
65 2
SQL>
OK, let's now merge those results with the disorders table:
SQL> merge into disorders d
2 using (select s.disorder_id,
3 round((count(s.id) / 2), 0) minsym
4 from symptoms s
5 group by s.disorder_id
6 ) x
7 on (d.id = x.disorder_id)
8 when matched then update set
9 d.minimum_symptoms = x.minsym;
3 rows merged.
Result:
SQL> select * from disorders order by id;
ID DISO MINIMUM_SYMPTOMS
---------- ---- ----------------
64 OCRD 3
65 OCD 2
248 GPD 2
SQL>
So, yes - that's "how" you'll do that (at least, one option).
You can use update with a correlated subquery:
update disorders d
set minimum_symptoms = (select round(count(*) / 2)
from symptoms s
where s.disorder_id = d.id
);
Your example is updating all rows in disorders. If you only wanted to update rows that have a match in symptoms:
update disorders d
set minimum_symptoms = (select round(count(*) / 2)
from symptoms s
where s.disorder_id = d.id
)
where exists (select 1
from symptoms s
where s.disorder_id = d.id
);

How to update a value within a column with another value- Oracle

I have a static table that I want to replace a particular value with a string. In other words, in my table, I want to replace the value dog in the type_1 column with 'primate'.
Here is my table:
t_id|type_1|count
1, dog, 22
2, cat, 55
3, bird, 12
Here is my expected output:
t_id|type_1|count
1, primate, 22
2, cat, 55
3, bird, 12
As you can see, I just want to replace the value dog with the value primate.
Here is my code so far:
SELECT REPLACE(t.type_1, 'dog', 'primate')
FROM table_1 t
where t.type_1 = 'dog' and t.t_id='1'
I am new to oracle sql so the syntax is a bit confusing to me. Any ideas or suggestions would help.
If you just want a query, use a case expression:
select t.t_id,
(case when t.type_1 = 'dog' then 'primate' else t.type_1 end) as type_1,
t.count
from table_1 t;
If you want to change the value in the table (i.e. permanently), use update:
update table_1
set type_1 = 'primate'
where type_1 = 'dog';
Looks like update.
This is what you have:
SQL> select * from animal;
T_ID TYPE_1 CNT
---------- ---------- ----------
1 dog 22
2 cat 55
3 bird 12
Change "dog" to "primate":
SQL> update animal set type_1 = 'primate' where type_1 = 'dog';
1 row updated.
Result:
SQL> select * from animal;
T_ID TYPE_1 CNT
---------- ---------- ----------
1 primate 22
2 cat 55
3 bird 12
SQL>
Or, if you just need a select statement, then case might do:
SQL> select t_id,
2 case when type_1 = 'dog' then 'primate'
3 else type_1
4 end as type,
5 cnt
6 from animal;
T_ID TYPE CNT
---------- ---------- ----------
1 primate 22
2 cat 55
3 bird 12
SQL>

How to get the difference between two rows in a table

I have a query which returns the below data set.
Sample:
SELECT *
FROM table_name
WHERE rank_code IN (15, 22);
The output of the above query gives the below result.
RANK
RANK_CODE
RANK_DESC
3
15
XYZ
5
22
ABC
Now, I would like to get the difference of Rank i.e., 5-3 = 2 or 3-5 = 2 in a single query. Only the difference. Is there a way to achieve the same using a SQL Query so that I may pass the rank codes as input parameter.
And if any row is not found or cannot be fetched the final result will be 0. Can we put this little check as well?
Any help will be appreciated.
SELECT coalesce(max(rank) - min(rank), 0)
FROM table_name
WHERE rank_code IN (15, 22);
If - in real life - that table contains many rows and you actually want to find difference between every two consecutive rows, consider one of analytic functions (LAG or LEAD). For example:
SQL> WITH
2 test (RANK, rank_code, rank_desc)
3 AS
4 (SELECT 3, 15, 'XYZ' FROM DUAL
5 UNION ALL
6 SELECT 5, 22, 'ABC' FROM DUAL)
7 SELECT RANK,
8 LEAD (RANK) OVER (ORDER BY RANK) next_rank,
9 RANK - LEAD (RANK) OVER (ORDER BY RANK) diff,
10 --
11 rank_code,
12 rank_desc
13 FROM test;
RANK NEXT_RANK DIFF RANK_CODE RAN
---------- ---------- ---------- ---------- ---
3 5 -2 15 XYZ
5 22 ABC
SQL>

Oracle Forms Secuences Or Autoincrement or an other option

I need to put the number 2 in the company that is CODCIA, the sequence is 1,2,3,4 etc. and when putting the number 4 the sequence is reset to 1.
I only have 1 table with a codcia field.
I have it this way:
CODCIA NRODOCTO
------ --------
2 1
2 2
2 3
4 4
4 5
4 6
WITH THIS CODE:
IF (:E.NRODOCTO in (2)) then
BEGIN
SELECT NVL(MAX(NRODOCTO),1200)+1
INTO :E.NRODOCTO
FROM PS91;
EXCEPTION WHEN OTHERS THEN :E.NRODOCTO := 1200;
END;
END IF;
and I need this:
CODCIA NRODOCTO
------ --------
2 1
2 2
2 3
4 1
4 2
4 3
It seems that quite a lot of information is missing in your question; not that you should post absolutely all information, but oversimplifying it also doesn't help.
Anyway: I'd say that you might be using the POST-INSERT trigger and update NRODOCTO column value, because - doing that in a form would require looping through the whole block, paying attention to CODCIA item value and incrementing the appropriate NRODOCTO value, and doing it for all distinct CODCIA values. That's somewhat dull as nobody prevents user from entering mixed CODCIA values.
Here's a SQL*Plus-based example (as I'm not going to create a form for it). The PS91 table most probably doesn't look as simple as that (re-read my first sentence), but that's what you told us.
SQL> create table ps91 (codcia) as
2 (select 2 from dual union all
3 select 2 from dual union all
4 select 2 from dual union all
5 --
6 select 4 from dual union all
7 select 4 from dual
8 );
Table created.
SQL> alter table ps91 add nrodocto number;
Table altered.
SQL> -- POST-INSERT form trigger
SQL> merge into ps91 p
2 using (select rowid rid, row_number() over (partition by codcia order by null) rn
3 from ps91
4 ) x
5 on (p.rowid = x.rid)
6 when matched then update set p.nrodocto = x.rn;
5 rows merged.
SQL> select * from ps91
2 order by codcia, nrodocto;
CODCIA NRODOCTO
---------- ----------
2 1
2 2
2 3
4 1
4 2
SQL>

Haven't been able to solve this:

Can't solve this and just haven't been able to wonder what's going wrong:
SELECT
CODE, NAME,
BRANCH, SALESP,
CASE
WHEN SUM(S1) = 0 THEN NULL
ELSE SUM(S1)
END as S1,
CASE
WHEN SUM(V1) = 0 THEN NULL
ELSE SUM(V1)
END as V1,
CASE
WHEN SUM(CHANGE) = 0 THEN NULL
ELSE SUM(CHANGE)
END as CHANGE
FROM
SAESQL7_E03.[dbo].[p1]
WHERE
S1 <> 0
OR CHANGE <> 0
GROUP BY
CODE, NAME, BRANCH, SALESP
Resulting in:
code name branch salesp s1 v1 change
-------------------------------------------------
1 Max A S 20 10 -5
2 Ben A 10 3 5
2 Ben B 20 5 10
The problem is in the 3rd column which is always wrong if a customer is in more than one branch.
(Max's example when customer only in one branch, always right; Ben's example case with two or more branches, first branch always right, following branches always wrong)
The table of origin has the correct data being:
code name branch salesp S1 V1 V2 change
---------------------------------------------------
1 Max A S 20 5 10 -5
2 Ben A 10 8 3 5
2 Ben B 20 10 5 5
CHANGE column comes from:
SUM (V1-V2) AS CHANGE
As far as I can tell, your query is correct and gives the correct answers! Something is wrong; we'll try to find out what. It looks like you're running on Microsoft SQL Server, so that is what I used for my tests.
Here is the origin table:
1> select * from p1;
2> go
code name branch salesp s1 v1 v2 change
----- ---- ------ ------ ---- ---- ---- --------
1 Max A S 20 5 10 -5
2 Ben A NULL 10 8 3 5
2 Ben B NULL 20 10 5 5
(3 rows affected)
Here is your query:
1> SELECT
2> CODE, NAME,
3> BRANCH, SALESP,
4> CASE
5> WHEN SUM(S1) = 0 THEN NULL
6> ELSE SUM(S1)
7> END as S1,
8> CASE
9> WHEN SUM(V1) = 0 THEN NULL
10> ELSE SUM(V1)
11> END as V1,
12> CASE
13> WHEN SUM(CHANGE) = 0 THEN NULL
14> ELSE SUM(CHANGE)
15> END as CHANGE
16> FROM
17> p1
18> WHERE
19> S1 <> 0
20> OR CHANGE <> 0
21> GROUP BY
22> CODE, NAME, BRANCH, SALESP
23> ;
24> go
... and the results:
CODE NAME BRANCH SALESP S1 V1 CHANGE
------ ---- ------ ------ ----- ---- --------
1 Max A S 20 5 -5
2 Ben A NULL 10 8 5
2 Ben B NULL 20 10 5
(3 rows affected)
So something is wrong. Could you try doing a select from the origin table again, please? Does it contain any other data? If not, then
select * from SAESQL7_E03.[dbo].[p1]
would be best.
If there is a lot more data, do this to make sure you include the same rows as used in the query above:
select
code,
name,
branch,
salesp,
s1,
v1,
v2,
change
from
SAESQL7_E03.dbo.p1
where
s1 <> 0
or change <> 0;
V1 in the query seems to return the values from v2 in the source. Is that a clue? Although you are aggregating, there is never more than one value in each group in the source data. Is this just a test with 1 row per group, with the intention to increase the number of rows per group?