Bash For loop logic in oracle sql - sql

I am looking for something similar to the bash for loop in oracle
for i in 1 5 3 8; do echo "print $i"; done
so this would result as
print 1
print 5
print 3
print 8
I want something similar logic in Oracle SQL like
for i in 1 5 3 8; do echo " select * from TABLE where column1='$i';"; done
so this would result as
select * from TABLE where column1='1';
select * from TABLE where column1='5';
select * from TABLE where column1='3';
select * from TABLE where column1='8';
So how do I get something similar logic in Oracle SQL

I hope that I understood your request correctly:
SELECT r FROM (
SELECT ROWNUM r
FROM DUAL
CONNECT BY ROWNUM <= 8)
WHERE r IN (1, 5, 3, 8)
Basically, we're creating a column of all numbers from 1 through 8 and then selecting the ones you requested. If you're going to work with strings or with a large range of numbers (but a small amount of rows), this solution won't be very efficient, and then you should store your values in a table.

You can build a loop in plsql, for example:
begin
for i in (
select 1 as num from dual union all
select 5 from dual union all
select 3 from dual union all
select 8 from dual
)
loop
dbms_output.put_line('number: ' || i.num);
/* do whatever you need */
end loop;
end;
/
If you need this to run a query, maybe you can simply use the IN:
select *
from table
where column1 in (1, 5, 3, 8)
or, to build a more complex list:
select *
from table
where column1 in (
select something
from ...
)

Related

How to SELECT / Query Whole Numbers only

Would you know If i can select only whole numbers?
I don't want to round off the values.
For example this is select * from Table_1 below
Numbers
Team
10.5
A
12.12
B
23
C
I would do like
select * from Table_1
where NUMBERS is ;
Expected output below
Numbers
Team
23
C
Thank you very much!
The function TRUNC() truncates a number without rounding:
SELECT *
FROM Table_1
WHERE TRUNC("Numbers") = "Numbers";
See the demo.
You can use the ROUND function to filter (rather than to display) the values:
SELECT *
FROM table_1
WHERE ROUND(numbers) = numbers;
You could also use the FLOOR, CEIL or TRUNC functions instead of ROUND.
Or, you could use the MOD function:
SELECT *
FROM table_1
WHERE MOD(numbers, 1) = 0;
(And you could apply a function-based index to MOD(number, 1) if you wanted to improve performance.)
Which, for the sample data:
CREATE TABLE table_1 (Numbers, Team) AS
SELECT 10.5, 'A' FROM DUAL UNION ALL
SELECT 12.12, 'B' FROM DUAL UNION ALL
SELECT 23, 'C' FROM DUAL;
All the options output:
NUMBERS
TEAM
23
C
db<>fiddle here

Random data sampling with oracle sql, data generation

i need to generate some sample data from a population. I want to do this with an SQL query on an Oracle 11g database.
Here is a simple working example with population size 4 and sample size 2:
with population as (
select 1 as val from dual union all
select 2 from dual union all
select 3 from dual union all
select 4 from dual)
select val from (
select val, dbms_random.value(0,10) AS RANDORDER
from population
order by randorder)
where rownum <= 2
(the oracle sample() funtion didn't work in connection with the WITH-clause for me)
But now I, I want to "upscale" or multiply my sample data. So that I can get something like 150 % sample data of the population data (population size 4 and sample size 6, e.g.)
Is there a good way to achieve this with an SQL query?
You could use CONNECT BY:
with population(val, RANDOMORDER) as (
select level, dbms_random.value(0,10) AS RANDORDER
from dual
connect by level <= 6
ORDER BY RANDORDER
)
select val
FROM population
WHERE rownum <= 4;
db<>fiddle demo
The solution depends, if you want all rows from first initial set(s) and random additional rows from last one then use:
with params(size_, sample_) as (select 4, 6 from dual)
select val
from (
select mod(level - 1, size_) + 1 val, sample_,
case when level <= size_ * floor(sample_ / size_) then 0
else dbms_random.value()
end rand
from params
connect by level <= size_ * ceil(sample_ / size_)
order by rand)
where rownum <= sample_
But if you allow possibility of result like (1, 1, 2, 2, 3, 3), where some values may not appear at all in output (here 4) then use this:
with params(size_, sample_) as (select 4, 6 from dual)
select val
from (
select mod(level - 1, size_) + 1 val, sample_, dbms_random.value() rand
from params
connect by level <= size_ * ceil(sample_ / size_)
order by rand)
where rownum <= sample_
How it works? We build set of (1, 2, 3, 4) as many times as it results from division sample / size. Then we assign random values. In first case I assign 0 to first set(s), so they will be in output for sure, and random values to last set. In second case randoms are assigned to all rows.

Is the Else part of Case When statement ignored in PL/SQL?

Why "else" part is not working I don't understand. Normally it should returns reverse of numbers if case statement not provided.
Here is my code;
select Id,
case
when mod(Id,10)<(Id/10)
then (cast((Id/10) as number(5))*10)+(mod(Id,10))
else
mod(Id,10)*10+(Id/10)
end Col
from digits
Sample data;
CREATE TABLE Test
(
Id INT
);
insert into test
select 21 from dual
UNION ALL
select 12 from dual
UNION ALL
select 34 from dual
UNION ALL
select 43 from dual
UNION ALL
select 29 from dual
UNION ALL
select 92 from dual;
Thanks in advance..
For reversing the integers with two digits the case should look like(notice the trunc function):
select
Id,
case
when mod(Id,10)<(Id/10)
then trunc(Id/10)*10+mod(Id,10) --this is the number itself, Id
else
mod(Id,10)*10+trunc(Id/10) --reverse :)
end Col
#aprkturk you mentioned T-SQL; here is Florin's answer translated:
select
Id,
case
when Id % 10 < Id / 10
then Id / 10 * 10 + Id % 10 -- this is the number itself, Id
else
Id % 10 * 10 + Id / 10 -- reverse :)
end Col
from #i
order by Id
What is this for? It seems an odd sort of thing to want to do...

How to use Recursive query in db2

I am learning to use recursive query in db2, got a problem online to print following pattern
*
**
***
****
*****
******
*******
********
*********
**********
***********
upto 20 level, solved it in Oracle using following query
select lpad('*', level, '*') result from dual connect by level <= 20
but got no idea how to do it in db2, would really appreciate any help.
Also how to do it in reverse order, means how to print stars pyramid from 20th to 1st level?
Correct query for db2, using REPEAT instead of LPAD
with x(id,val) as
(
select 1, REPEAT('*',1) from sysibm.sysdummy1
union all
select id+1, REPEAT('*',id+1) from x where id < 20
)
select val from x
 
with x(id,val) as
(
select 20, REPEAT('*',20) from sysibm.sysdummy1
union all
select id-1, REPEAT('*',id-1) from x where id > 1
)
select val from x
Replace dual with sysibm.sysdummy1
select lpad('*', level, '*') result
from sysibm.sysdummy1
connect by level <= 20
Recursive query for printing 1 to 20 *s
with x(id,val) as
(
select 1 as id,lpad('*',1,'*') as val from sysibm.sysdummy1
union all
select id+1,lpad('*',id+1,'*') from x where id < 20
)
select val from x
Recursive query for printing 20 to 1 *s
with x(id,val) as
(
select lpad('*',20,'*') as val,20 as id from sysibm.sysdummy1
union all
select lpad('*',id-1,'*'),id-1 from x where id > 1
)
select val from x

SQL to generate a list of numbers from 1 to 100

Using the DUAL table, how can I get a list of numbers from 1 to 100?
Your question is difficult to understand, but if you want to select the numbers from 1 to 100, then this should do the trick:
Select Rownum r
From dual
Connect By Rownum <= 100
Another interesting solution in ORACLE PL/SQL:
SELECT LEVEL n
FROM DUAL
CONNECT BY LEVEL <= 100;
Using Oracle's sub query factory clause: "WITH", you can select numbers from 1 to 100:
WITH t(n) AS (
SELECT 1 from dual
UNION ALL
SELECT n+1 FROM t WHERE n < 100
)
SELECT * FROM t;
Do it the hard way. Use the awesome MODEL clause:
SELECT V
FROM DUAL
MODEL DIMENSION BY (0 R)
MEASURES (0 V)
RULES ITERATE (100) (
V[ITERATION_NUMBER] = ITERATION_NUMBER + 1
)
ORDER BY 1
Proof: http://sqlfiddle.com/#!4/d41d8/20837
You could use XMLTABLE:
SELECT rownum
FROM XMLTABLE('1 to 100');
-- alternatively(useful for generating range i.e. 10-20)
SELECT (COLUMN_VALUE).GETNUMBERVAL() AS NUM
FROM XMLTABLE('1 to 100');
DBFiddle Demo
If you want your integers to be bound between two integers (i.e. start with something other than 1), you can use something like this:
with bnd as (select 4 lo, 9 hi from dual)
select (select lo from bnd) - 1 + level r
from dual
connect by level <= (select hi-lo from bnd);
It gives:
4
5
6
7
8
Peter's answer is my favourite, too.
If you are looking for more details there is a quite good overview, IMO, here.
Especially interesting is to read the benchmarks.
Using GROUP BY CUBE:
SELECT ROWNUM
FROM (SELECT 1 AS c FROM dual GROUP BY CUBE(1,1,1,1,1,1,1) ) sub
WHERE ROWNUM <=100;
Rextester Demo
A variant of Peter's example, that demonstrates a way this could be used to generate all numbers between 0 and 99.
with digits as (
select mod(rownum,10) as num
from dual
connect by rownum <= 10
)
select a.num*10+b.num as num
from digits a
,digits b
order by num
;
Something like this becomes useful when you are doing batch identifier assignment, and looking for the items that have not yet been assigned.
For example, if you are selling bingo tickets, you may want to assign batches of 100 floor staff (guess how i used to fund raise for sports). As they sell a batch, they are given the next batch in sequence. However, people purchasing the tickets can select to purchase any tickets from the batch. The question may be asked, "what tickets have been sold".
In this case, we only have a partial, random, list of tickets that were returned within the given batch, and require a complete list of all possibilities to determine which we don't have.
with range as (
select mod(rownum,100) as num
from dual
connect by rownum <= 100
),
AllPossible as (
select a.num*100+b.num as TicketNum
from batches a
,range b
order by num
)
select TicketNum as TicketsSold
from AllPossible
where AllPossible.Ticket not in (select TicketNum from TicketsReturned)
;
Excuse the use of key words, I changed some variable names from a real world example.
... To demonstrate why something like this would be useful
I created an Oracle function that returns a table of numbers
CREATE OR REPLACE FUNCTION [schema].FN_TABLE_NUMBERS(
NUMINI INTEGER,
NUMFIN INTEGER,
EXPONENCIAL INTEGER DEFAULT 0
) RETURN TBL_NUMBERS
IS
NUMEROS TBL_NUMBERS;
INDICE NUMBER;
BEGIN
NUMEROS := TBL_NUMBERS();
FOR I IN (
WITH TABLA AS (SELECT NUMINI, NUMFIN FROM DUAL)
SELECT NUMINI NUM FROM TABLA UNION ALL
SELECT
(SELECT NUMINI FROM TABLA) + (LEVEL*TO_NUMBER('1E'||TO_CHAR(EXPONENCIAL))) NUM
FROM DUAL
CONNECT BY
(LEVEL*TO_NUMBER('1E'||TO_CHAR(EXPONENCIAL))) <= (SELECT NUMFIN-NUMINI FROM TABLA)
) LOOP
NUMEROS.EXTEND;
INDICE := NUMEROS.COUNT;
NUMEROS(INDICE):= i.NUM;
END LOOP;
RETURN NUMEROS;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN NUMEROS;
WHEN OTHERS THEN
RETURN NUMEROS;
END;
/
Is necessary create a new data type:
CREATE OR REPLACE TYPE [schema]."TBL_NUMBERS" IS TABLE OF NUMBER;
/
Usage:
SELECT COLUMN_VALUE NUM FROM TABLE([schema].FN_TABLE_NUMBERS(1,10))--integers difference: 1;2;.......;10
And if you need decimals between numbers by exponencial notation:
SELECT COLUMN_VALUE NUM FROM TABLE([schema].FN_TABLE_NUMBERS(1,10,-1));--with 0.1 difference: 1;1.1;1.2;.......;10
SELECT COLUMN_VALUE NUM FROM TABLE([schema].FN_TABLE_NUMBERS(1,10,-2));--with 0.01 difference: 1;1.01;1.02;.......;10
If you want to generate the list of numbers 1 - 100 you can use the cartesian product of {1,2,3,4,5,6,6,7,8,9,10} X {0,10,20,30,40,50,60,70,80,90}
https://en.wikipedia.org/wiki/Cartesian_product
Something along the lines of the following:
SELECT
ones.num + tens.num
FROM
(
SELECT 1 num UNION ALL
SELECT 2 num UNION ALL
SELECT 3 num UNION ALL
SELECT 4 num UNION ALL
SELECT 5 num UNION ALL
SELECT 6 num UNION ALL
SELECT 7 num UNION ALL
SELECT 9 num UNION ALL
SELECT 10 num
) as ones
CROSS JOIN
(
SELECT 0 num UNION ALL
SELECT 10 num UNION ALL
SELECT 20 num UNION ALL
SELECT 30 num UNION ALL
SELECT 40 num UNION ALL
SELECT 50 num UNION ALL
SELECT 60 num UNION ALL
SELECT 70 num UNION ALL
SELECT 80 num UNION ALL
SELECT 90 num
) as tens;
I'm not able to test this out on an oracle database, you can place the dual where it belongs but it should work.
SELECT * FROM `DUAL` WHERE id>0 AND id<101
The above query is written in SQL in the database.