I have one source table whose data looks like - sql

**COLOR** **TIMES**
ORANGE 1
RED 2
BLACK 3
YELLOW 4
But I need Data to be display in below format :-
**COLOR** **TIMES**
ORANGE 1
RED 1
RED 1
BLACK 1
BLACK 1
BLACK 1
YELLOW 1
YELLOW 1
YELLOW 1
YELLOW 1
Please suggest me the query in oracle SQL

A possible solution:
SQL> with t as (
2 select 'ORANGE' as color, 1 as times from dual
3 union all select 'RED' as color, 2 as times from dual
4 union all select 'BLACK' as color, 3 as times from dual
5 union all select 'YELLOW' as color, 4 as times from dual),
6 num as (
7 select rownum as n
8 from dual
9 connect by level <= 4)
10 select t.color,
11 1 as times
12 from t
13 join num on num.n <= t.times
14 order by t.times;
COLOR TIMES
------ ----------
ORANGE 1
RED 1
RED 1
BLACK 1
BLACK 1
BLACK 1
YELLOW 1
YELLOW 1
YELLOW 1
YELLOW 1
10 rows selected.

Maybe not very nice but it is working:
WITH t AS
(SELECT 'Orange' AS color, 1 AS times FROM dual UNION ALL
SELECT 'Red' AS color, 2 AS times FROM dual UNION ALL
SELECT 'Black' AS color, 3 AS times FROM dual UNION ALL
SELECT 'Yellow' AS color, 4 AS times FROM dual),
t2 AS
(SELECT DISTINCT color, 1 AS item, times, LEVEL AS C
FROM t
CONNECT BY LEVEL <= times)
SELECT color, item
FROM t2;

Related

what is wrong with my query in oracle live sql?

I ran the following code in oracle live SQL but it gives an error. What is the problem?
select * from bricks full join
((select count(*) from bricks) as "counts"
inner join (select count(*) from bricks group by colour) as "colours"
on counts.colour=colours.colour)
on bricks.colour=bricks.colour; --a dummy condition just for connecting tables without a specific column
output:
ORA-00907: missing right parenthesis
The problems are:
AS before a table/sub-query alias is invalid syntax in Oracle. Remove the AS keywords.
The aliases you are using are quoted identifiers which are case-sensitive and you do not use quoted identifiers with the same case when you refer to the aliases. Remove the double quotes.
Neither the counts nor colours sub-query has a colour column in the SELECT clause and you cannot subsequently refer to non-existent columns in the ON clause of the join condition.
You can fix it using:
select *
from bricks
CROSS JOIN (select count(*) AS cnt from bricks) counts
LEFT OUTER JOIN (
select colour, count(*) AS colour_cnt
from bricks
group by colour
) colours
on bricks.colour=colours.colour
Which, for the sample data:
CREATE TABLE bricks (id, colour) AS
SELECT 1, 'red' FROM DUAL UNION ALL
SELECT 2, 'red' FROM DUAL UNION ALL
SELECT 3, 'red' FROM DUAL UNION ALL
SELECT 4, 'green' FROM DUAL UNION ALL
SELECT 5, 'green' FROM DUAL UNION ALL
SELECT 6, 'blue' FROM DUAL;
Outputs:
ID
COLOUR
CNT
COLOUR
COLOUR_CNT
1
red
6
red
3
2
red
6
red
3
3
red
6
red
3
4
green
6
green
2
5
green
6
green
2
6
blue
6
blue
1
However, you probably want to simplify things and use the analytic COUNT() OVER (...) function and eliminate the self-joins:
select b.*,
COUNT(*) OVER () AS cnt,
COUNT(*) OVER (PARTITION BY colour) AS colour_cnt
from bricks b;
Which outputs:
ID
COLOUR
CNT
COLOUR_CNT
6
blue
6
1
5
green
6
2
4
green
6
2
2
red
6
3
1
red
6
3
3
red
6
3
(identical other than not duplicating the colours column)
db<>fiddle here

how to extract unique words from cell and count them

I have an column "DESCRIPTION" (VARCHAR2 (500 Byte))
I want as result two columns. First extract from each cell unique words and display them in one column, and in second count their frequency.
Additionaly I have limiting parametre "ENTRYDATE" (i.e. "WHERE ENTRYDATE BETWEEN 20180101 and 20190101"). Because table is quite big.
I have some solution in Excel, but it's messy and painful to do.
Is it even possible to do in Oracle with SELECT?
Example:
NUMBER OF COLUMN | EXPLANATION
1 | roses are red violets are blue
2 | red violets
3 | red
4 | roses
5 | blue
RESULT:
WORDS | COUNTING
roses | 2
are | 2
red | 3
violets | 2
blue | 2
Variation of query:
with test as
(select 1 as nor, 'roses are red violets are blue' as explanation from dual union all
select 2 as nor, 'red violets' as explanation from dual union all
select 3 as nor, 'red' as explanation from dual union all
select 4 as nor, 'roses' as explanation from dual union all
select 5 as nor, 'blue' as explanation from dual
),
temp as
(select nor,
trim(column_value) word
from test join xmltable(('"' || replace(explanation, ' ', '","') ||'"')) on 1 = 1
)
select word,
count(*)
from temp
group by word
order by word;
returns ORA-00905: missing keyword
Split explanation into rows (so that you'd get words), then apply COUNT function to those words.
SQL> with test (nor, explanation) as
2 (select 1, 'roses are red violets are blue' from dual union all
3 select 2, 'red violets' from dual union all
4 select 3, 'red' from dual union all
5 select 4, 'roses' from dual union all
6 select 5, 'blue' from dual
7 ),
8 temp as
9 (select nor,
10 regexp_substr(explanation, '[^ ]+', 1, column_value) word
11 from test join table(cast(multiset(select level from dual
12 connect by level <= regexp_count(explanation, ' ') + 1
13 ) as sys.odcinumberlist)) on 1 = 1
14 )
15 select word,
16 count(*)
17 from temp
18 group by word
19 order by word;
WORD COUNT(*)
------------------------------ ----------
are 2
blue 2
red 3
roses 2
violets 2
SQL>
You mentioned entrydate column but there's none in your sample data so - if necessary, include it into the TEMP CTE.
Edit
Huh, Oracle 9i ... back to the Dark Ages:
SQL> with test (nor, explanation) as
2 (select 1, 'roses are red violets are blue' from dual union all
3 select 2, 'red violets' from dual union all
4 select 3, 'red' from dual union all
5 select 4, 'roses' from dual union all
6 select 5, 'blue' from dual
7 ),
8 temp as
9 (select nor,
10 trim(column_value) word
11 from test join xmltable(('"' || replace(explanation, ' ', '","') ||'"')) on 1 = 1
12 )
13 select word,
14 count(*)
15 from temp
16 group by word
17 order by word;
WORD COUNT(*)
-------------------- ----------
are 2
blue 2
red 3
roses 2
violets 2
SQL>
The problem is in your old Oracle version. This query should work, it has only basic connect by, instr and dbms_random:
select word, count(1) counting
from (
select id, trim(case pos2 when 0 then substr(description, pos1)
else substr(description, pos1, pos2 - pos1)
end) word
from (
select id, description,
case level when 1 then 1 else instr(description, ' ', 1, level - 1) end pos1,
instr(description, ' ', 1, level) pos2
from t
connect by prior dbms_random.value is not null
and prior id = id
and level <= length(description) - length(replace(description, ' ', '')) + 1))
group by word
demo
-- Oracle 12c+
with test (nor, explanation) as (
select 1, 'roses are red violets are blue' from dual union all
select 2, 'red violets' from dual union all
select 3, 'red' from dual union all
select 4, 'roses' from dual union all
select 5, 'blue' from dual)
select regexp_substr(explanation, '\S+', 1, lvl) word, count(*) cnt
from test,
lateral(
select rownum lvl
from dual
connect by level <= regexp_count(explanation, '\S+')
)
group by regexp_substr(explanation, '\S+', 1, lvl);
WORD CNT
------------------------------ ----------
roses 2
are 2
violets 2
red 3
blue 2

How to pull entries with 'Shared' Foreign Keys from a Single Table

Let's say that I have a table called Color_Size_Rel that looks like this
Color_Size_ID Color Size
1 Blue L
2 Blue M
3 Green L
4 Purple L
5 Pink XL
6 White S
7 Blue L
What would be the query to pull the colors / the number of colors with the same Size? The expected result set is below:
Blue
Green
Purple
I've tried the following to no avail (I'm really stuck and have no idea how this would be done):
select color
from color_size_rel
where size = size;
Thanks in advance.
You can try to use exists subquery to make it.
Query 1:
SELECT DISTINCT t1.color
FROM Color_Size_Rel t1
WHERE exists (
SELECT 1
FROM Color_Size_Rel tt
WHERE t1.Size = tt.Size
HAVING COUNT(*) > 1
)
Results:
| color |
|--------|
| Blue |
| Green |
| Purple |
This should do the trick:
WITH color_size_rel AS (SELECT 1 color_size_id, 'Blue' color, 'L' "SIZE" FROM dual UNION ALL
SELECT 2 color_size_id, 'Blue' color, 'M' "SIZE" FROM dual UNION ALL
SELECT 3 color_size_id, 'Green' color, 'L' "SIZE" FROM dual UNION ALL
SELECT 4 color_size_id, 'Purple' color, 'L' "SIZE" FROM dual UNION ALL
SELECT 5 color_size_id, 'Pink' color, 'XL' "SIZE" FROM dual UNION ALL
SELECT 6 color_size_id, 'White' color, 'S' "SIZE" FROM dual UNION ALL
SELECT 6 color_size_id, 'Orange' color, 'S' "SIZE" FROM dual UNION ALL
SELECT 7 color_size_id, 'Blue' color, 'L' "SIZE" FROM dual)
SELECT "SIZE", color
FROM (SELECT DISTINCT "SIZE",
color,
COUNT(DISTINCT color) OVER (PARTITION BY "SIZE") cnt
FROM color_size_rel)
WHERE cnt > 1
ORDER BY "SIZE", color;
SIZE COLOR
---- ------
L Blue
L Green
L Purple
S Orange
S White
(I added an extra colour in size "S" to demonstrate the output when there are multiple sizes with more than one colour.)
This finds the count of distinct colours per size, and then distincts the result set, so that if there are duplicate colours in each size, only one is reported. Finally, we filter the results to show those sizes which have a count that is greater than 1.
N.B. Please don't use size as a column name - it's a reserved word, hence why I had to use double-quotes every time I referenced it.
Select distinct colour from colour_size_rel where size=size

Oracle SQL Query to get the RAG

RAG PCT
------ ---
GREEN 100
AMBER 50
ORANGE 20
RED 0
I need an oracle query to result like (Suggest to use Inner Join or Outer Join)
if the given PCT >100 then Green
if the given PCT >=50 and PCT < 100 then AMBER
if the given PCT >=20 and PCT < 0 then ORANGE
else RED
You can implement the logic using CASE, like :
SELECT pct, CASE
WHEN pct >= 100 THEN 'GREEN'
WHEN pct >= 50 THEN 'AMBER'
WHEN pct >= 20 then 'ORANGE'
ELSE 'RED'
END
FROM mytable
CASE stops on the first matching condition (hence no need to write WHEN pct >= 50 AND pct < 100 for example, since pct >= 100 is already caught by the previous condition.
If you are using a separate table to store the lower bound of each interval (like
myranges), as shown in your example, and you are looking to JOIN it with a table that cotains actual data (like mydata), then it is a little more tricky : you would need to ensure that you are joining with the relevant range record :
SELECT d.*, r.*
FROM mydata d
INNER JOIN myranges r
ON d.value >= r.pct
AND (
LEAD (r.pct) OVER (ORDER BY pct) IS NULL
OR d.value < LEAD (r.pct) OVER (ORDER BY pct)
)
What you're asking for doesn't entirely make sense - you've said "Use a join" but have provided nothing to join to, but never mind. The following strictly implements your spec:
WITH cteData AS (SELECT 'GREEN' AS RAG, 100 AS PCT FROM DUAL UNION ALL
SELECT 'AMBER', 50 FROM DUAL UNION ALL
SELECT 'ORANGE', 20 FROM DUAL UNION ALL
SELECT 'RED', 0 FROM DUAL)
SELECT RAG, PCT, CASE
WHEN PCT > 100 THEN 'GREEN'
WHEN PCT >= 50 AND PCT < 100 THEN 'AMBER'
WHEN PCT >= 20 AND PCT < 0 THEN 'ORANGE'
ELSE 'RED'
END AS COLOR
FROM cteData;
When executed, the above produces:
RAG PCT COLOR
GREEN 100 RED
AMBER 50 AMBER
ORANGE 20 RED
RED 0 RED
And may Codd have mercy upon your soul.
If I understand you correctly, I think this might be something like what you're after:
WITH rag_data AS (SELECT 'GREEN' AS rag, 100 AS PCT FROM DUAL UNION ALL
SELECT 'AMBER' AS rag, 50 AS PCT FROM DUAL UNION ALL
SELECT 'ORANGE' AS rag, 20 AS PCT FROM DUAL UNION ALL
SELECT 'RED' AS rag, 0 AS PCT FROM DUAL),
sample_data AS (SELECT -1 NUM FROM dual UNION ALL
SELECT 0 NUM FROM dual UNION ALL
SELECT 1 NUM FROM dual UNION ALL
SELECT 19 NUM FROM dual UNION ALL
SELECT 20 NUM FROM dual UNION ALL
SELECT 21 NUM FROM dual UNION ALL
SELECT 49 NUM FROM dual UNION ALL
SELECT 50 NUM FROM dual UNION ALL
SELECT 51 NUM FROM dual UNION ALL
SELECT 99 NUM FROM dual UNION ALL
SELECT 100 NUM FROM dual UNION ALL
SELECT 101 NUM FROM dual)
SELECT NUM,
rag,
pct,
rn
FROM (SELECT sd.num,
rd.rag,
rd.pct,
row_number() OVER (PARTITION BY sd.num ORDER BY rd.pct DESC) rn
FROM sample_data sd
INNER JOIN rag_data rd ON sd.num >= rd.pct)
WHERE rn = 1;
NUM RAG PCT RN
---------- ------ ---------- ----------
0 RED 0 1
1 RED 0 1
19 RED 0 1
20 ORANGE 20 1
21 ORANGE 20 1
49 ORANGE 20 1
50 AMBER 50 1
51 AMBER 50 1
99 AMBER 50 1
100 GREEN 100 1
101 GREEN 100 1

SELECTING aggregate data using CASE statement issue

I'm trying to create a query to obtain zone data in preparation to moving the column to another table. How our DB is set up is that we have assets and cables. One asset can have many cables but a cable cannot have multiple assets. We're currently trying to move the Zone field from the Cable table (ex. Below) to the Asset table.
A_ID C_ID Zone
--------------------------
1 1 Green
1 2 Green
1 3 Yellow
2 4 Green
2 5 Red
3 6 Yellow
3 7 Yellow
3 8 Yellow
3 9 Red
The way I want it to be set up for the Asset table is if the Asset contains multiple cables with different zones, if one of the zones is Yellow it defaults to Yellow (ex. 3 green cables, 1 yellow cable - Asset_ID has Yellow zone). Next if it doesn't have any Yellows but has at least 1 red, it defaults to Red (ex. 2 green cables, 3 red cables - Asset_ID has Red Zone). Only if it just has Green zones then it defaults to Green.
Using the sample table above these are the results I would expect.
Expected Results
A_ID Zone
-------------
1 Yellow
2 Red
3 Yellow
I'm trying to use CASE statements but I'm having difficulty formulating the query to group the results correctly.
Any help would be greatly appreciated, thank you in advance.
One way to do it using conditional aggregation and a case expression.
select a_id
,case when yellow_count >=1 then 'Yellow'
when yellow_count = 0 and red_count >=1 then 'Red'
when yellow_count = 0 and red_count = 0 and green_count >=1 then 'Green'
end zone
from (select a_id,
count(case when zone = 'Yellow' then 1 end) yellow_count,
count(case when zone = 'Red' then 1 end) red_count
count(case when zone = 'Green' then 1 end) green_count
from cable_table
group by a_id) t
No case or if statement needed. Think in sets. You need to assign a priority join to the priority and select the one with the highest priority first. Like this
WITH Priority AS
(
SELECT * FROM (
VALUES
('Yellow', 1),
('Red', 2),
('Green', 3)
) AS P(Zone,P)
)
SELECT A_ID, Zone
FROM (
SELECT A_ID, Zone
ROW_NUMBER() OVER (PARTITION BY A_ID ORDER BY P.P ASC) AS RN
FROM AssetTable A
LEFT JOIN Priority P ON A.Zone = P.Zone
) SUB
WHERE RN = 1
Not sure if my syntax is right for VALUES in CTE for Oracle. if that gives an error replace with this:
WITH Priority AS
(
SELECT 'Yellow' AS Zone, 1 AS P
UNION ALL
SELECT 'Red' AS Zone, 2 AS P
UNION ALL
SELECT 'Green' AS Zone, 3 AS P
)
Why just don't do a simple:
SELECT A_ID, MAX( Zone )
FROM table
GROUP BY A_ID
if some A_ID has Yellow then max( Zone ) returns Yellow
if some A_ID hasn't Yellow, but has Red then max( Zone ) returns Red<br>
otherwise (noYellownorRed) max( Zone ) returnsGreen`
and example:
with data as (
select 1 a_id, 1 c_id , 'Green' zone from dual union all
select 1 , 2 , 'Green' from dual union all
select 1 , 3, 'Yellow' from dual union all
select 2 , 4 , 'Green' from dual union all
select 2 , 5 , 'Red' from dual union all
select 3 , 6 , 'Yellow' from dual union all
select 3 , 7 , 'Yellow' from dual union all
select 3 , 8 , 'Yellow' from dual union all
select 3 , 9 , 'Red' from dual
)
select a_id, max( zone )
from data
group by a_id
A_ID MAX(ZO
---------- ------
1 Yellow
2 Red
3 Yellow