how to convert distinct row values into column [duplicate] - sql

This question already has answers here:
Dynamic Pivot in Oracle's SQL
(10 answers)
Closed last year.
Here is my data
Id type count
1 jim 2
1 bim 2
1 sim 3
1 pim 1
2 jim 2
2 bim 1
Want to convert this data into below
Id jim bim sim pim
1 2 2 3 1
2 2 1 0 0
Tried this, its now working
select * FROM table
PIVOT
(
Min(Id)
FOR Id IN (select distinct(type) from table)
)
I'm trying to convert all distinct values of type row into columns and then assign respective values against every ID. Any suggestion please ?
Error
ORA-00936: missing expression
00936. 00000 - "missing expression"
*Cause:
*Action:
Error at Line: 26 Column: 16

Would this do?
SQL> with test (id, type, count) as
2 (select 1, 'jim', 2 from dual union all
3 select 1, 'bim', 2 from dual union all
4 select 1, 'sim', 3 from dual union all
5 select 1, 'pim', 1 from dual union all
6 select 2, 'jim', 2 from dual union all
7 select 2, 'bim', 1 from dual
8 )
9 select id, nvl(jim, 0) jim,
10 nvl(bim, 0) bim,
11 nvl(sim, 0) sim,
12 nvl(pim, 0) pim
13 from test
14 pivot
15 (min(count)
16 for type in ('jim' as jim, 'bim' as bim, 'sim' as sim, 'pim' as pim)
17 );
ID JIM BIM SIM PIM
---------- ---------- ---------- ---------- ----------
1 2 2 3 1
2 2 1 0 0
SQL>

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>

generate sequence number

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.

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

UNPIVOT the results of a delimited string on several rows

I am struggling with some complex hierarchical data. I have successfully used a CONNECT BY query to limit the rows down to the subset that i want - and i have used SYS_CONNECT_BY_PATH to return the full tree up to the nodes of interest.
this gives me essentially some rows like this (delimited by '|'):
id path
-------------------
1, '|10|11|12|13'
2, '|10|14|15'
3, '|16|11|12|13'
4, '|16|17'
now - my challenge is to unwrap or UNPIVOT these values back into a structure like this:
id ord node
-------------
1, 1, 10
1, 2, 11
1, 3, 12
1, 4, 13
2, 1, 10
2, 2, 14
2, 3, 15
3, 1, 16
3, 2, 11
3, 3, 12
3, 4, 13
4, 1, 16
4, 2, 17
I think i am unable to use UNPIVOT directly as that is working on a fixed set of columns - which this is not.
I am playing with a PIPELINE function to unwrap this, but frankly - passing all these rows to the function is an issue since they come from another query. I am wondering if anyone has a way to UNPIVOT the values from a SYS_CONNECT_BY_PATH result set back into rows that is maybe a pure sql solution - probably with REGEX parsing...
help always appreciated - thanks
Yes, UNPIVOT operator wont do much here to help you produce the desired output.
As one of the approaches you could user regexp_count()(11g R1 version and up) regular
expression function to count all occurrences of numbers and then use regexp_substr() regular
expression function to extract the numbers as follows:
-- sample of data
SQL> with t1(id1, path1) as(
2 select 1, '|10|11|12|13' from dual union all
3 select 2, '|10|14|15' from dual union all
4 select 3, '|16|11|12|13' from dual union all
5 select 4, '|16|17' from dual
6 ),
7 occurrences(ocr) as( -- occurrences
8 select level
9 from ( select max(regexp_count(path1, '[^|]+')) as mx_ocr
10 from t1
11 ) t
12 connect by level <= t.mx_ocr
13 )
14 select id1
15 , row_number() over(partition by id1 order by id1) as ord
16 , node
17 from ( select q.id1
18 , regexp_substr(q.path1, '[^|]+', 1, o.ocr) as node
19 from t1 q
20 cross join occurrences o
21 )
22 where node is not null
23 order by id1, 2, node
24 ;
Result:
ID1 ORD NODE
---------- ---------- ------------------------------------------------
1 1 10
1 2 11
1 3 12
1 4 13
2 1 10
2 2 14
2 3 15
3 1 11
3 2 12
3 3 13
3 4 16
4 1 16
4 2 17
13 rows selected
As another approach, starting from 10g version and up, you could use model clause:
SQL> with t1(id1, path1) as(
2 select 1, '|10|11|12|13' from dual union all
3 select 2, '|10|14|15' from dual union all
4 select 3, '|16|11|12|13' from dual union all
5 select 4, '|16|17' from dual
6 )
7 select id1
8 , ord
9 , node
10 from t1
11 model
12 partition by ( rownum as id1)
13 dimension by ( 1 as ord)
14 measures( path1
15 , cast(null as varchar2(11)) as node
16 , nvl(regexp_count(path1, '[^|]+'), 0) as ocr )
17 rules(
18 node[for ord from 1 to ocr[1] increment 1] =
19 regexp_substr(path1[1], '[^|]+', 1, cv(ord))
20 )
21 order by id1, ord, node
22 ;
Result:
ID1 ORD NODE
---------- ---------- -----------
1 1 10
1 2 11
1 3 12
1 4 13
2 1 10
2 2 14
2 3 15
3 1 16
3 2 11
3 3 12
3 4 13
4 1 16
4 2 17
13 rows selected
SQLFiddle Demo

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.