generate sequence number - sql

Hi can any one help me generate version number based on below table data
ex:
book_name version
math_1 1
physics_1 3
physics_6 4
chemist_1 1
chemist_2 2
eng_1 1
i want a query to rerun below result based on above table data( especially Physics )
expected result
book_name version
math_1 1
physics_1 1
physics_6 2
chemist_1 1
chemist_2 2
eng_1 1
Thanks in advance

To produce the new version you need to separate the subject from the level. Once you do that, a simple ROW_NUMBER() function does the job.
For example:
select x.*,
row_number() over(partition by subject order by lvl) as new_version
from (
select t.*,
substr(book_name, 1, instr(book_name, '_') - 1) as subject,
to_number(substr(book_name, 1 + instr(book_name, '_'))) as lvl
from t
) x;
Result:
BOOK_NAME version SUBJECT LVL NEW_VERSION
----------- --------- --------- ----- -------------
chemist_1 1 chemist 1 1
chemist_2 2 chemist 2 2
eng_1 1 eng 1 1
math_1 1 math 1 1
physics_1 3 physics 1 1
physics_6 4 physics 6 2
See running example at db<>fiddle.

Similarly (lines #1 - 8 represent sample data; you don't type that. Query that returns result you wanted begins at line #9):
SQL> with test (book_name, version) as
2 (select 'math_1' , 1 from dual union all
3 select 'physics_1', 3 from dual union all
4 select 'physics_6', 4 from dual union all
5 select 'chemist_1', 1 from dual union all
6 select 'chemist_2', 2 from dual union all
7 select 'eng_1' , 1 from dual
8 )
9 select book_name,
10 version,
11 row_number() over (partition by regexp_substr(book_name, '[[:alpha:]]')
12 order by to_number(regexp_substr(book_name, '\d+$'))
13 ) new_version
14 from test
15 order by book_name, new_version;
BOOK_NAME VERSION NEW_VERSION
--------- ---------- -----------
chemist_1 1 1
chemist_2 2 2
eng_1 1 1
math_1 1 1
physics_1 3 1
physics_6 4 2
6 rows selected.
SQL>

You seem to just want the last digits:
select t.*,
regexp_substr(book_name, '[0-9]+$')
from t;
Here is a db<>fiddle.

Related

List number into TABLE records by the value in another table

Sorry for a little bit confuse on the title.
I would like to use SQL for join sequence table as below
Original Table Records
3
3
4
1
Result Table
1
2
3
1
2
3
1
2
3
4
1
Based on comment you posted,
sample data:
SQL> with test (col) as
2 (select 3 from dual union all
3 select 3 from dual union all
4 select 4 from dual union all
5 select 1 from dual
6 )
Query begins here:
7 select column_value
8 from test cross join
9 table(cast(multiset(select level from dual
10 connect by level <= col
11 ) as sys.odcinumberlist));
COLUMN_VALUE
------------
1
2
3
1
2
3
1
2
3
4
1
11 rows selected.
SQL>

ORACLE SQL: Reseting a counting sequence when ID ends for the next one

I am currently trying to reset a sequence if an ID of a Customer ends
right now, it is something like this:
CustomerID Product PosNr
1 Banana 1
1 Papaya 2
1 Apple 3
2 Laptop 1
2 Keyboard 2
I hope it is clear what I mean.
The PosNr should reset for another Customer.
Can I Set up something like this while inserting the values into the table, or in any other way?
It is row_number analytic function with appropriate partitioning.
SQL> with test (customerid, product) as
2 (select 1, 'banana' from dual union all
3 select 1, 'papaya' from dual union all
4 select 1, 'apple' from dual union all
5 select 2, 'laptop' from dual union all
6 select 2, 'keyboard' from dual
7 )
8 select customerid, product,
9 row_number() over (partition by customerid order by product) posnr
10 from test
11 /
CUSTOMERID PRODUCT POSNR
---------- -------- ----------
1 apple 1
1 banana 2
1 papaya 3
2 keyboard 1
2 laptop 2
SQL>

Filtering SQL using a oracle database

I would like to know if the following is possible
For example I have a shoe factory. In this factory I have a production line, Every step in this production line is recorded into the oracle database.
if the shoe has completed a production step the result is = 1
example table
Shoe_nr production step result
1 1 1
1 2 1
1 3
2 1 1
2 2 1
2 3
3 1
3 2
3 3
Now the question, is it possible to filter out production step 3 where only the shoes have passed production step 2 which is equal to 1 in result.
I know if it can be done it's probably very easy but if you dont know i found out it's a little bit tricky.
Thanks,
Chris
Yes, you can do it with IN and a Subselect
select *
from shoes
where shoe.id in (
select shoe.id
from shoes
where production_step = 2
and result = 1
)
and production_step = 3
This might be one option; see comments within code (lines #1 - 12 represent sample data; you already have that and don't type it. Query you might be interested in begins at line #13).
SQL> with shoes (shoe_nr, production_step, result) as
2 -- sample data
3 (select 1, 1, 1 from dual union all
4 select 1, 2, 1 from dual union all
5 select 1, 3, null from dual union all
6 select 2, 1, 1 from dual union all
7 select 2, 2, 1 from dual union all
8 select 2, 3, null from dual union all
9 select 3, 1, null from dual union all
10 select 3, 2, null from dual union all
11 select 3, 3, null from dual
12 ),
13 -- which shoes' production step #3 should be skipped?
14 skip as
15 (select shoe_nr
16 from shoes
17 where production_step = 2
18 and result = 1
19 )
20 -- finally:
21 select a.shoe_nr, a.production_step, a.result
22 from shoes a
23 where (a.shoe_nr, a.production_step) not in (select b.shoe_nr, 3
24 from skip b
25 )
26 order by a.shoe_nr, a.production_step;
SHOE_NR PRODUCTION_STEP RESULT
---------- --------------- ----------
1 1 1
1 2 1
2 1 1
2 2 1
3 1
3 2
3 3
7 rows selected.
SQL>
If you just want the shoe_nr that satisfy the condition, you can use aggregation and a having clause:
select shoe_nr
from mytable
group by shoe_nr
having
max(case when production_step = 2 then result end) = 0
and max(case when production_step = 3 then 1 end) = 1
If you want the entire row corresponding to this shoe_nr at step 3, use window functions instead:
select 1
from (
select
t.*,
max(case when production_step = 2 then result end)
over(partition by shoe_nr) as has_completed_step_2
from mytable t
) t
where production_step = 3 and has_completed_step_2 = 0

Oracle - generate a running number by group

I need to generate a running number / group sequence inside a select statement for a group of data.
For example
Group Name Sequence
1 a 1
1 b 2
1 c 3
2 d 1
2 e 2
2 f 3
So for each group the sequence should be a running number starting with 1 depending on the order of column"Name".
I already pleayed around with Row_Number() and Level but I couldn't get a solution.
Any idea how to do it?
Analytic functions help.
SQL> with test (cgroup, name) as
2 (select 1, 'a' from dual union all
3 select 1, 'b' from dual union all
4 select 1, 'c' from dual union all
5 select 2, 'd' from dual union all
6 select 2, 'e' from dual union all
7 select 2, 'f' from dual
8 )
9 select cgroup,
10 name,
11 row_number() over (partition by cgroup order by name) sequence
12 from test
13 order by cgroup, name;
CGROUP N SEQUENCE
---------- - ----------
1 a 1
1 b 2
1 c 3
2 d 1
2 e 2
2 f 3
6 rows selected.
SQL>
Try this
SELECT
"Group",
Name,
DENSE_RANK() OVER (PARTITION BY "Group" ORDER BY Name) AS Sequence
FROM table;

Split words and insert it new table with counting these words

I have the foolwoing table and data.
i need to:
1- split each sentence in each row into new row
2-count the words in each row based on last part of sentence based on soundex function
create table a (id number(9), words varchar(500));
insert into a values(1,'UK,LONDON,YEMEN,JOHN,CAIRO,OMAR ALI,EGYPT,Cairo,YEMAN,OMAR AMR ALI,LONDAN');
insert into a values(2,'UK,SUDAI,SUDAIN,AYHAM SHAHER YAFOOZ,ALI YAFOOZ');
insert into a values(3,'MALAYSIA, AHMED ALI,MALYSIAN');
expexted output
create table temp_words(id number(9),words varchar2(100), count_words number(9));
id words count_words
1 UK 1
1 LONDON 2
1 YEMEN 2
1 CAIRO 2
1 OMAR ALI 2
1 JOHN 1
2 UK 1
2 SUDAI 2
2 AYHAM SHAHER YAFOOZ 2
3 MALAYSIA 2
3 AHMED ALI 1
regards
to split the data as you want you can use a "connect by" as a row generator.
SQL> with src as (select id,',' || words || ',' as words,
2 length(words) - length(translate(words, '.,', '.')) + 1 no_of_words
3 from a)
4 select a.id,
5 substr(a.words,
6 instr(words, ',', 1, r) + 1,
7 instr(words, ',', 1, r + 1) - instr(words, ',', 1, r) - 1) word,
8 a.no_of_words
9 from (select level r
10 from dual
11 connect by level <= (select max(no_of_words) from src)) d
12 inner join src a
13 on d.r <= a.no_of_words
14 where a.no_of_words is not null
15 order by a.id, d.r
16 /
ID WORD NO_OF_WORDS
---------- -------------------- -----------
1 UK 11
1 LONDON 11
1 YEMEN 11
1 JOHN 11
1 CAIRO 11
1 OMAR ALI 11
1 EGYPT 11
1 Cairo 11
1 YEMAN 11
1 OMAR AMR ALI 11
1 LONDAN 11
2 UK 5
2 SUDAI 5
2 SUDAIN 5
2 AYHAM SHAHER YAFOOZ 5
2 ALI YAFOOZ 5
3 MALAYSIA 3
3 AHMED ALI 3
3 MALYSIAN 3
19 rows selected.
SQL>
Here is a SQLFiddle demo
select id,words,
case when i=0 then
SUBSTR(words,
1,
case when INSTR(words,',', 1, 1)=0
then 100000
else
INSTR(words,',', 1, 1)-1
end
)
ELSE
SUBSTR(words,
INSTR(words,',', 1, i)+1,
case when INSTR(words,',', 1, i+1)=0
then 100000
else
INSTR(words,',', 1, i+1)-INSTR(words,',', 1, i)-1
end
)
END word,
i+1 COUNTWORDS
from a,
(
select * from
(
select 0 i from dual
union
select 1 i from dual
union
select 2 i from dual
union
select 3 i from dual
union
select 4 i from dual
union
select 5 i from dual
union
select 6 i from dual
union
select 7 i from dual
union
select 8 i from dual
union
select 9 i from dual
union
select 10 i from dual
union
select 11 i from dual
union
select 12 i from dual
)
)
table_i
where
case when i>0 then INSTR(words,',', 1, i)
else 100000 end <>0
order by id,i
Another approach(using regexp_count and regexp_substr regular expression functions):
SQL> with Occurence(oc) as(
2 select level
3 from ( select max(regexp_count(words, '[^,]+')) ml
4 from a
5 ) t
6 connect by level <= t.ml
7 )
8 select id
9 , word
10 , count(word) over(partition by id, soundex(word) order by id) as count_words
11 From ( select a.id
12 , regexp_substr(words, '[^,]+', 1, o.oc) as word
13 from occurence o
14 cross join a
15 ) s
16 where s.word is not null
17 order by id
18 ;
ID WORD COUNT_WORDS
---------- -------------------- -----------
1 Cairo 2
1 CAIRO 2
1 EGYPT 1
1 JOHN 1
1 LONDAN 2
1 LONDON 2
1 OMAR ALI 1
1 OMAR AMR ALI 1
1 UK 1
1 YEMEN 2
1 YEMAN 2
2 ALI YAFOOZ 1
2 AYHAM SHAHER YAFOOZ 1
2 SUDAI 1
2 SUDAIN 1
2 UK 1
3 AHMED ALI 1
3 MALAYSIA 1
3 MALYSIAN 1
19 rows selected
You would need to insert your data as separate records. You can keep them as a concatenated string, if you like, but it'll just make your life very difficult. So:
create table words (
id number,
w varchar2(100),
s varchar2(4)
);
create or replace trigger words_auto
before insert or update on words
for each row
begin
select trim(upper(:new.w)), soundex(:new.w)
into :new.w, :new.s
from dual;
end;
insert into words (id, w) values (1, 'UK');
insert into words (id, w) values (1, 'LONDON');
...
insert into words (id, w) values (3, ' AHMED ALI');
insert into words (id, w) values (3, 'MALYSIAN');
You could write a procedure to split your concatenated string and populate the words table appropriately. Note that I have created a trigger that normalises your input to uppercase, removing all extraneous whitespace and automatically produces the Soundex codes.
Now here's a question: You want to group words by their Soundex codes; however, how do you determine the baseline? For example, 'LONDON' and 'LONDAN' both have code 'L535', but how do you know which record is the 'main' one?... You can't, without further lookup tables! As such, the best you can do is to group by the Soundex codes. This doesn't have to be stored in a table and it makes more sense to be a view:
create or replace view word_counts as
select id,
s soundex,
count(w) count_rows
from words
group by id,
s;
Note that I've called the count field count_rows as it counts records, rather than distinct rows. That is: records of 'LONDON', 'LONDAN' and 'LONDON' will show with a count of 3, not 2 (which you might be expecting). Anyway, with your data, the view will look something like this:
id soundex count_rows
----- --------- -----------
1 U200 1
1 L535 2
... ... ...
3 M420 2
3 A534 1
As I say, that's really the best you can expect without further infrastructure.