add value to local variable oracle PL/SQL - oracle

DECLARE
var1 INTEGER :=0;
var2 INTEGER :=0;
BEGIN
SELECT DISTINCT
<whatever here>
CASE
WHEN trunc(thisDate) - TRUNC(thatDate) BETWEEN 0 AND 10 THEN var1+1
WHEN trunc(thisDate2) - TRUNC(thatDate2) BETWEEN 11 AND 20 THEN var2+1
ELSE 0
END
FROM
<Rest of query here>
basically what I want to be able to do is to add 1 to the local variable then print out the value of that variable as part of my select statement using a count (or sum of the count) or something everytime the difference in the ages falls within those categories
I'm not sure how to add to the local variable basically.

SELECT
sum(CASE WHEN trunc(thisDate) - TRUNC(thatDate) BETWEEN 0 AND 10 THEN 1 ELSE 0 END) + var1,
sum(CASE WHEN trunc(thisDate2) - TRUNC(thatDate2) BETWEEN 11 AND 20 THEN 1 ELSE 0 END) + var2
into var1, var2
FROM
<Rest of query here>

Try analytic functions
BEGIN
SELECT DISTINCT
<whatever here>
sum(CASE
WHEN trunc(thisDate) - TRUNC(thatDate) BETWEEN 0 AND 10 THEN 1
end) over() var1,
sum(CASE
WHEN trunc(thisDate2) - TRUNC(thatDate2) BETWEEN 11 AND 20 THEN 1
END) over() var2
FROM
<Rest of query here>

Given a comment by Allan, maybe you are looking for something like than instead:
SELECT V.*,
case when t1 is not null then
count(t1) over (ORDER BY n ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
when t2 is not null then
count(t2) over (ORDER BY n ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
else
0
end varX
FROM (
SELECT DISTINCT
<whatever here>
ROWNUM n,
CASE WHEN trunc(thisDate) - TRUNC(thatDate) BETWEEN 0 AND 10 THEN 1 END t1,
CASE WHEN trunc(thisDate) - TRUNC(thatDate) BETWEEN 11 AND 20 THEN 1 END t2
FROM
<Rest of query here>
) V
Some example for you to check if this is what you are looking for: http://sqlfiddle.com/#!4/2de0d/2
The inner query is basically yours, plus
a ROWNUM column (maybe not necessary if you somehow ORDER BY your rows)
a marker set to 1 if the row is in the first range, NULL otherwise
a marker set to 1 if the row is in the second range, NULL otherwise
The outer query use the analytic function COUNT() OVER(...) to respectively count the numbers of markers between the current row and the first of the result set. The row number n is used here. Replace that by something more relevant if your data are already ordered.

I don't think you can update some variable while executing a query. All you can do is fetch the result value into some variable.
Slight variation over Multisync's answer, using COUNT instead of SUM:
SELECT
count(CASE WHEN trunc(thisDate) - TRUNC(thatDate) BETWEEN 0 AND 10 THEN 1 END) ,
count(CASE WHEN trunc(thisDate2) - TRUNC(thatDate2) BETWEEN 11 AND 20 THEN 1 END)
into var1, var2
FROM
<Rest of query here>
This will actually return a one-row result with the count of values is the first range in the first column, and the count of values in the second range in the second column. Using the INTO var1,var2 clause, PL/SQL will implicitly fetch those values into your local variables.

Related

Subquery as CASE WHEN condition

Below query, syntax error happens on AS PQ_COUNT
SELECT CASE WHEN
RESULTS LIKE '%PQ - Duplicate%' AND
(SELECT COUNT(*) FROM MY_TABLE WHERE ID = '998877'AND FINAL_RESULTS='FL_57') AS PQ_COUNT >= 1
THEN 'PQ count = '|| PQ_COUNT
ELSE RESULTS END AS RESULTS
If I moved AS PQ_COUNT inside select query,
(SELECT COUNT(*) AS PQ_COUNT FROM MY_TABLE WHERE ID = '998877'AND FINAL_RESULTS='FL_57') >= 1
the reference of PQ_COUNT in THEN block become invalid identifier (ORA-00904)
What might go wrong here when addressing subquery as CASE WHEN condition?
One option is to use a subquery (or a CTE, as in my example) to calculate number of rows that satisfy condition, and then - as it contains only one row - cross join it to my_table. Something like this:
SQL> WITH
2 my_table (id, final_results, results) AS
3 -- sample data
4 (SELECT '998877', 'FL_57', 'PQ - Duplicate' FROM DUAL),
5 cnt AS
6 -- calculate COUNT first ...
7 (SELECT COUNT (*) pq_count --> pq_count
8 FROM MY_TABLE
9 WHERE ID = '998877'
10 AND FINAL_RESULTS = 'FL_57')
11 -- ... then re-use it in "main" query
12 SELECT CASE
13 WHEN a.results LIKE '%PQ - Duplicate%'
14 AND b.pq_count >= 1 --> reused here
15 THEN
16 'PQ count = ' || b.PQ_COUNT --> and here
17 ELSE
18 a.results
19 END AS results
20 FROM my_table a CROSS JOIN cnt b;
RESULTS
---------------------------------------------------
PQ count = 1
SQL>
You cannot refer to an alias in the same sub-query where you create it; you need to nest sub-queries (or use a sub-query factoring clause; also called a CTE or WITH clause) and refer to it in the outer one:
SELECT CASE
WHEN results LIKE '%PQ - Duplicate%'
AND pq_count >= 1
THEN 'PQ count = '|| pq_count
ELSE results
END AS RESULTS
FROM (
SELECT results,
( SELECT COUNT(*)
FROM MY_TABLE
WHERE ID = '998877'
AND FINAL_RESULTS='FL_57'
) AS pq_count
FROM your_table
);

How to force a set of possible values in conditional aggregation?

In Oracle I have a query that uses conditional aggregation to display totals.
See http://sqlfiddle.com/#!4/ab1915/2
select ts.description,
sum(case when ta.group_number = 1 then 1 else 0 end) group_one
,sum(case when ta.group_number = 2 then 1 else 0 end) group_two
,sum(case when ta.group_number = 3 then 1 else 0 end) group_three
from ta
join ts on ta.status_id=ts.status_id
group by ts.description;
However, I need the results to also show another row for the status "cancelled", which is not currently showed because there are not any record with that status, but I need to display "cancelled" with 0 counts anyway,
Any idea how I would do that?
The DDL for this example is
create table ta (group_number number, status_id number);
create table ts (status_id number, description varchar(111));
insert into ta (group_number,status_id) values (1,1);
insert into ta (group_number,status_id) values (2,1);
insert into ta (group_number,status_id) values (3,2);
insert into ta (group_number,status_id) values (3,3);
insert into ta (group_number,status_id) values (3,3);
Use an outer join so you pick up all of the ts valueswhich means you need to reverse the order of the tables; and I'd use count() instead of sum():
select ts.description,
count(case when ta.group_number = 1 then 1 end) group_one
,count(case when ta.group_number = 2 then 1 end) group_two
,count(case when ta.group_number = 3 then 1 end) group_three
,count(case when ta.group_number = 4 then 1 end) group_four
from ts
left join ta on ta.status_id=ts.status_id
group by ts.description;
DESCRIPTION GROUP_ONE GROUP_TWO GROUP_THREE
-------------------- ---------- ---------- -----------
started 1 1 0
finished 0 0 2
cancelled 0 0 0
progressing 0 0 1
Updated SQL Fiddle
The case expression can evaluate to anything when matched, it just has to be non-null. From the docs:
If you specify expr, then COUNT returns the number of rows where expr is not null.
so where the case is not matched it evaluates to null, which is not counted. (You can have else null if you prefer to be explicit, but I tend to prefer brevity...)

PL/SQL: Need help creating calculated columns based on conditions. to be done in select query

I am trying to count all the distinct ids based on conditions. But I am unable to figure out where I am going wrong with the syntax. The logic is
COUNTD(IF ([column_name1] = 1) THEN [DATAPAGEID] END)
This is the formula I used in Tableau. However when writing it in a PL/SQL query as
Select FT.NAME, COUNT(DISTINCT FT.pageID IF FT."column_name" = 1 )
as total_expected
FROM
( Sub Query) FT
Group by FT.Name
Order by FT.Name
Needless to say its throwing errors. Now I can write separate queries which can give me each number using a where condition. For example, if I wanted a count of distinct pageid where column_name1 = 1, I would write something like this
Select FT.SITENAME, COUNT(DISTINCT DATAPAGEID) as Datapage
from
(sub query)
WHERE FT."column_name" = 1
but the problem with that is that I have other calculated columns in the query which will all need to be part of the same row. To illustrate here's what the table would look like
name Calculated_Column1 Calculated_Column2 Calculated_column3
abc 781 811 96.54%
pqr 600 800 75.00%
where calculated_column3 is the result of 781/811. Therefore I can't have a new query for each column. I thought using an if condition when calculating columns will solve this, but I can't get the syntax right somehow.
Therefore, I need to know how can I create conditional calculated columns within the select query. If I have not explained this well, please let me know and I will try to clarify further.
You can use a CASE block inside the count (DISTINCT ) as shown.
SELECT FT.NAME,
COUNT(DISTINCT
CASE
WHEN DATAPAGEID = 1
THEN 1
ELSE 0
END ) Calculated_Column1,
COUNT(DISTINCT
CASE
WHEN DATAPAGEID = 2
THEN 1
ELSE 0
END ) Calculated_Column2,
( COUNT(DISTINCT
CASE
WHEN DATAPAGEID = 1
THEN 1
ELSE 0
END ) / COUNT(DISTINCT
CASE
WHEN DATAPAGEID = 2
THEN 1
ELSE 0
END ) ) * 100||'%' Calculated_Column3
FROM
( SELECT 'abc' name, 1 DATAPAGEID FROM dual
UNION ALL
SELECT 'abc' name, 1 DATAPAGEID FROM dual
UNION ALL
SELECT 'pqr' name, 2 DATAPAGEID FROM dual
UNION ALL
SELECT 'pqr' name, 2 DATAPAGEID FROM dual
UNION ALL
SELECT 'pqr' name, 3 DATAPAGEID FROM dual
) FT
GROUP BY FT.Name
ORDER BY FT.Name;
Output is
abc 1 1 100%
pqr 1 2 50%

Oracle instr position

I have 15 char string and need to loop through pulling the position of occurrence of the letter 'a'. I was going to use a cursor to loop through the string, but wasn't sure how to save each positions occurrence.
Something like this to break the string into each character and then filter on your desired value?
-- data setup to create a single value to test
WITH dat as (select 'ABCDEACDFA' val from DUAL)
--
SELECT lvl, strchr
from (
-- query to break the string into individual characters, returning a row for each
SELECT level lvl, substr(dat.val,level,1) strchr
FROM dat
CONNECT BY level <= length(val)
) WHERE strchr = 'A';
returns:
LVL STRCHR
1 A
6 A
10 A
Here's a different method using one less select and a regex. I don't believe it will help your performance issue though. Please try it and let us know:
SQL> with tbl(str) as (
select 'Aabjggaklkjha' from dual
)
select level as position
from tbl
where upper(REGEXP_SUBSTR(str, '.', 1, level)) = 'A'
connect by level <= length(str);
POSITION
----------
1
2
7
13
SQL>

Oracle SUM calculated values

I'm trying to figure out how I can or if it's possible to sum the returned values from columns on the fly to have a total value returned for each record instead of having to calculate it again.
The query has multiple sub-queries which all calculate correctly and return an integer value, I'm trying to find a way to add these values on the fly into a Total column, is it even possible!?
SELECT (SELECT value FROM ....) AS NUM_1
, (SELECT value FROM ....) AS NUM_2
, (SELECT value FROM ....) AS NUM_3
, SUM(NUM_1 + NUM_2 + NUM_3) AS TOTAL
FROM DUAL;
This is the output I'm looking to achieve:
NUM_1 | NUM_2 | NUM_3 | Total
=============================
3 | 4 | 3 | 10
with numbers as (
SELECT (SELECT value FROM ....) AS NUM_1,
(SELECT value FROM ....) AS NUM_2,
(SELECT value FROM ....) AS NUM_3
FROM DUAL;
)
select num_1,
num_2,
num_3,
num_1 + num_2 + num_3 as total
from numbers;
You have to make sure that your (SELECT value FROM ....) queries return exactly one row otherwise you'll get an error like "Single row sub-query returns multiple rows".
Remove semi colon (;) after dual and then run as whole it will work
with numbers as (
SELECT (SELECT value FROM ....) AS NUM_1,
(SELECT value FROM ....) AS NUM_2,
(SELECT value FROM ....) AS NUM_3
FROM DUAL
)
select num_1,
num_2,
num_3,
num_1 + num_2 + num_3 as total
from numbers;

Resources