Query with iteration in Oracle pl/sql - oracle

I have the following table with a single column
Z_NUM
--------
34545
345
656
32
42
...
I want to build a following dependence
i | SUM(Z_NUM)
----------------
2 | 40934
3 | 51244
4 | 54793
...
based on query
SELECT SUM(z_num) FROM table WHERE z_num < i;
The variable i is a parameter and should be incremented by 1.
How to implement this query in ORACLE?
If I were doing this in MYSQL, I would write something like
SELECT
​#n := #n + 1 n,
SUM(z_num)
FROM table, (SELECT #n := 1) m
WHERE z_num < n;
But unfortunately it does not work in Oracle PL/SQL.

Use two subqueries:
one with connect by to generate numbers i from 2 to N
another dependend subquery to calculate a sum for each i
SELECT i,
( SELECT coalesce( sum(z_num), 0 )
FROM table1 WHERE z_num < i
) as myresult
FROM (
SELECT level+1 As i FROM dual
CONNECT BY LEVEL <= ( SELECT max(Z_NUM) FROM table1 )-1
)
ORDER BY i
Demo: http://sqlfiddle.com/#!4/c460c/5

use ROWNUM,
SELECT SUM(z_num)
FROM (SELECT z_num, rownum rown
FROM table)
WHERE z_num < rown;
OR
SELECT SUM(z_num)
FROM (SELECT z_num, rownum rown
FROM table)
WHERE rown < 10;

Try this:
SELECT i,
( SELECT coalesce( sum(z_num), 0 )
FROM Table_NUM WHERE ROWNUM <= i
) as SUM(Z_NUM)
FROM (
SELECT level+1 As i FROM dual
CONNECT BY LEVEL <= ( SELECT Max(ROWNUM) FROM Table_NUM )-1
)
ORDER BY i;
Result:
I SUM_RESULT
-----------------
2 34890
3 35546
4 35578
5 35620
It may be help you.

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 generate a start_no and end_no of 1000 split from a table where in it contains numbers from 944900000 to 944999999 using oracle sql

The table contains numbers from 944900000 to 944999999 and i want to split these numbers into ranges of 1000 each like
944900000 to 944900999 -- 1000
944901000 to 944901999 -- 1000
..
..
944999000 to 944999999 -- 1000
is there any way to generate this through oracle SQL not with PL/SQL
You could also use this.
--Your data
CREATE TABLE your_table(your_column) AS
SELECT 944900000 + LEVEL - 1
FROM dual
CONNECT BY 944900000 + LEVEL < 944999999 + 2
;
--from 944900000 to 944999999
WITH cte AS (
SELECT your_column, CASE WHEN MOD(your_column, 1000) = 0 THEN your_column END start_range
FROM your_table
)
SELECT start_range, end_range
FROM (
SELECT start_range, CASE WHEN start_range IS NOT NULL THEN LEAD(your_column, 999)OVER(ORDER BY your_column) END end_range
FROM cte
)T
WHERE end_range IS NOT NULL /*because of the last execution of lead function in the inline view t*/
GROUP BY start_range, end_range
ORDER BY 1, 2
;
You could use ROW_NUMBER to number all records according to increasing number value. Then, compute a DENSE_RANK using the "group" of increments of 1000 to which each record belongs.
WITH cte AS (
SELECT t.*, ROW_NUMBER OVER (ORDER BY col) rn
FROM yourTable t
),
cte2 AS (
SELECT t.*, DENSE_RANK() OVER (ORDER BY FLOOR(rn / 1000)) dr
FROM cte t
)
SELECT *
FROM cte2
WHERE dr = 2; -- e.g. for 2nd partition of 1000 records

Ora-00932 - expected NUMBER got -

I have been running the below query without issue:
with Nums (NN) as
(
select 0 as NN
from dual
union all
select NN+1 -- (1)
from Nums
where NN < 30
)
select null as errormsg, trunc(sysdate)-NN as the_date, count(id) as the_count
from Nums
left join
(
SELECT c1.id, trunc(c1.c_date) as c_date
FROM table1 c1
where c1.c_date > trunc(sysdate) - 30
UNION
SELECT c2.id, trunc(c2.c_date)
FROM table2 c2
where c2.c_date > trunc(sysdate) -30
) x1
on x1.c_date = trunc(sysdate)-Nums.NN
group by trunc(sysdate)-Nums.NN
However, when I try to pop this in a proc for SSRS use:
procedure pr_do_the_thing (RefCur out sys_refcursor)
is
oops varchar2(100);
begin
open RefCur for
-- see above query --
;
end pr_do_the_thing;
I get
Error(): PL/SQL: ORA-00932: inconsistent datatypes: expected NUMBER got -
Any thoughts? Like I said above, as a query, there is no issue. As a proc, the error appears at note (1) int eh query.
This seems to be bug 18139621 (see MOS Doc ID 2003626.1). There is a patch available, but if this is the only place you encounter this, it might be simpler to switch to a hierarchical query:
with Nums (NN) as
(
select level - 1
from dual
connect by level <= 31
)
...
You could also calculate the dates inside the CTE (which also fails with a recursive CTE):
with Dates (DD) as
(
select trunc(sysdate) - level + 1
from dual
connect by level <= 31
)
select null as errormsg, DD as the_date, count(id) as the_count
from Dates
left join
(
SELECT c1.id, trunc(c1.c_date) as c_date
FROM table1 c1
where c1.c_date > trunc(sysdate) - 30
UNION
SELECT c2.id, trunc(c2.c_date)
FROM table2 c2
where c2.c_date > trunc(sysdate) -30
) x1
on x1.c_date = DD
group by DD;
I'd probably organise it slightly differently, so the subquery doesn't limit the date range directly:
with dates (dd) as
(
select trunc(sysdate) - level + 1
from dual
connect by level <= 31
)
select errormsg, the_date, count(id) as the_count
from (
select null as errormsg, d.dd as the_date, c1.id
from dates d
left join table1 c1 on c1.c_date >= d.dd and c1.c_date < d.dd + 1
union all
select null as errormsg, d.dd as the_date, c2.id
from dates d
left join table2 c2 on c2.c_date >= d.dd and c2.c_date < d.dd + 1
)
group by errormsg, the_date;
but as always with these things, check the performance of each approach...
Also notice that I've switched from union to union all. If an ID could appear more than once on the same day, in the same table or across both tables, then the counts will be different - you need to decide whether you want to count them once or as many times as they appear. That applies to your original query too.

updating two column from select statement?

I am trying to update two columns from select statement, but I am getting error message that says
ORA-00927: missing equal sign
Please, can anyone tell me why ?
UPDATE table1 a
SET co1 ,co2 = (SELECT COUNT (*),
sum(cost)/4
FROM table2 b
WHERE a.customer_id = b.cust_info_customer_id
AND tariff_info = 2);
Since you didn't provide any input:
create table table1(
co1 number,
co2 number,
customer_id number
)
insert into table1 values (1,1, 1)
select * from table1
CO1 CO2 CUSTOMER_ID
-------------------
1 1 1
UPDATE table1 a
SET co1 = (
with table2 as (
select level cost, 1 cust_info_customer_id from dual connect by rownum < 5
)
SELECT COUNT (*)
FROM table2 b
WHERE a.customer_id = b.cust_info_customer_id
)
, co2 = (
with table2 as (
select level cost, 1 cust_info_customer_id from dual connect by rownum < 5
)
SELECT sum(cost)/4
FROM table2 b
WHERE a.customer_id = b.cust_info_customer_id
)
select * from table1
CO1 CO2 CUSTOMER_ID
-------------------
4 2.5 1

Retrieving page that contains specific row

I have a procedure that returns multiple rows on some criteria and in specific order. These rows are separated into few pages (50 rows per page).
How can I retrieve all rows from page having some specific row.
I've created a query the query that do this work, but it is not optimized and have huge impact on performance. Help me please to optimize it or give an alternative to it:
select *
from
(
select file_id, row_number() over (order by rownum) rn
from my_table
)
where trunc(rn/50) = (
select trunc(rn/50) from
(select t.*, rownum rn from my_table t)
where file_id = 29987);
You have to adjust it a little...
with tab as
(
-- the
-- row_number() over (order by rownum) rn
-- should be here
select level + 1000 as val
, level/50 as rn_50
from dual
connect by
level < 140
)
, val as
(
select rn_50
from tab
where val = 1004 -- pg 1
--where val = 1051 -- pg 2
--where val = 1101 -- pg 3
)
select *
from tab t
where rn_50 >= (select floor(rn_50) from val)
and rn_50 <= (select ceil (rn_50) from val)
;

Resources