Oracle SQL - How to "pivot" one row to many - sql

In Oracle 12c, I have a view, which takes a little time to run. When I add the where clause, it will return exactly one row of interest. The row has columns/value like this...
I need this flipped so that I can see one row per EACH "set". I need the SQL to return something like
I know I can do a UNION ALL for each of the entry sets, but as the view takes a little while to run, plus there are about 30 different sets (I only showed 3 - Car, Boat, and truck)
Is there a better way of doing this? I have looked at PIVOT/UNPIVOT, but I didn't see how to make this work.

I think you are looking for UNPIVOT
WITH TEMP_DATA (ID1, CarPrice, CarTax, BoatPrice, BoatTax, TruckPrice, TruckTax)
AS (
select 'AAA', 1, 2, 3, 4, 5, 6 from dual )
select TYPE, PRICE, TAX
from temp_data
unpivot
(
(PRICE, TAX)
for TYPE IN
(
(CarPrice, CarTax) as 'CAR',
(BoatPrice, BoatTax) as 'BOAT',
(TruckPrice, TruckTax) as 'TRUCK'
)
)
;
OUTPUT:
TYPE PRICE TAX
----- ---------- ----------
CAR 1 2
BOAT 3 4
TRUCK 5 6

Related

APEX Oracle Compare Two Fields (varchar2 )

I want to compare two varchar2 fields and based on the percentage similarity to get this percentage as a result in my function, and the ID of this record from this table.
I have the table (SYMPTOMS), I also have the field Symptom_Descr (VARCHAR2) of this table and the variable v_symptom (varchar2) and I want to compare this variable with this field.
For example, this is my table:
The variable that I want to compare is:
'flashes the room lights 5 times'
I want as a result=
id
1
0%
2
0%
3
90%
Another example if the variable is 'washes her hands 7 times':
id
1
80%
2
0%
3
10%
The above percentages are not exact.
If the above cannot be done, then what can I do to find the similarities?
You can use the UTL_MATCH package:
SELECT id,
UTL_MATCH.EDIT_DISTANCE_SIMILARITY(
symptom_descr,
'flashes the room lights 5 times'
) AS ed_similarity,
UTL_MATCH.JARO_WINKLER_SIMILARITY(
symptom_descr,
'flashes the room lights 5 times'
) AS jw_similarity
FROM symptoms;
Which, for the sample data:
CREATE TABLE symptoms (id, symptom_descr) AS
SELECT 1, 'washes his hands constantly' FROM DUAL UNION ALL
SELECT 2, 'checks several times if he turned off the water heater' FROM DUAL UNION ALL
SELECT 3, 'flashes the room lights too many times' FROM DUAL;
Outputs:
ID
ED_SIMILARITY
JW_SIMILARITY
1
30
62
2
25
62
3
79
93
db<>fiddle here

Order by random number not working in Impala

When I run this in Hue against an Impala data source, I get an unexpected result
select id, rand(unix_timestamp(now())) as ord
from (
select 1 as id union select 2 union select 3 union select 4
) x
order by ord
I'd expect to get the rows ordered in a random order by whatever comes up in the ord column. Instead, I always get 3, 4, 1, 2 with numbers in ord that aren't in order:
id ord
3 0.8899110606358904
4 0.3766916056055071
1 0.09029531483086539
2 0.7446466436351866
So, what part of ORDER BY does Impala not understand? Or what am I missing?
It seems that Impala 2.8 and lower has this bug. This is claimed to be fixed in Impala 2.9, but I haven't tested it yet.
In the meantime, since I don't need very robust randomness, I just used:
order by mod((((unix_timestamp() + MY_ID_COLUMN) * 7621) + 1), 32768)
(There is a definite pattern to such results, but it is probably good enough to fool a casual observer.)
Thanks to Vamsi Prabhala for pointing me in the right direction.

How to categorize several columns in one statement in SQL/PLSQL

I've got a table with 20 columns which I like to categorize like;
0-25 --> 1
25-50 --> 2
50-75 --> 3
75-100 --> 4
I prefer not to use 20 case ... when statements. Anyone who knows how to do this more dynamically & efficiently? Can be SQL or PL/SQL.
I tried some PL/SQL, but I didn't see a simple method to use the column names as variables.
Many thanks.
Frans
Your example is a bit confusing, but assuming you want to put a certain value into those categories, the function width_bucket might be what you are after:
Something like this:
with sample_data as (
select trunc(dbms_random.value(1,100)) as val
from dual
connect by level < 10
)
select val, width_bucket(val, 0, 100, 4) as category
from sample_data;
This will assign the numbers 1-4 to the (random) values from sample_data. the 0, 100 defines the range from which to build the buckets, and the final parameter 4 says in how many (equally wide) buckets this should be distributed. The result of the function is the bucket into which the value val would fall.
SQLFiddle example: http://sqlfiddle.com/#!4/d41d8/10721
The case statement is probably the most efficient way of doing it. A more dynamic way would be to create a table using the with statement. Here is an example of the code:
with ref as (
select 0 as lower, 25 as higher 1 as val from dual union all
select 25, 59, 2 from dual union all
select 50, 75, 3 from dual union all
select 75, 100, 4 from dual
)
select ref.val
from t left outer join ref
on t.col >= ref.lower and t.col < ref.higher
That said, this particular lookup could be done with arithmetic:
select trunc((t.col - 1) / 25) + 1 as val
from t
And, if your problem is managing the different columns, you might consider unpivot. However, I think it is probably easier just to write the code and modify the column names in a text editor or Excel.

Comparing list of values against table

I tried to find solution for this problem for some time but without success so any help would be much appreciated. List of IDs needs to be compared against a table and find out which records exist (and one of their values) and which are non existent. There is a list of IDs, in text format:
100,
200,
300
a DB table:
ID(PK) value01 value02 value03 .....
--------------------------------------
100 Ann
102 Bob
300 John
304 Marry
400 Jane
and output I need is:
100 Ann
200 missing or empty or whatever indication
300 John
Obvious solution is to create table and join but I have only read access (DB is closed vendor product, I'm just a user). Writing a PL/SQL function also seems complicated because table has 200+ columns and 100k+ records and I had no luck with creating dynamic array of records. Also, list of IDs to be checked contains hundreds of IDs and I need to do this periodically so any solution where each ID has to be changed in separate line of code wouldn't be very useful.
Database is Oracle 10g.
there are many built in public collection types. you can leverage one of them like this:
with ids as (select /*+ cardinality(a, 1) */ column_value id
from table(UTL_NLA_ARRAY_INT(100, 200, 300)) a
)
select ids.id, case when m.id is null then '**NO MATCH**' else m.value end value
from ids
left outer join my_table m
on m.id = ids.id;
to see a list of public types on your DB, run :
select owner, type_name, coll_type, elem_type_name, upper_bound, precision, scale from all_coll_types
where elem_type_name in ('FLOAT', 'INTEGER', 'NUMBER', 'DOUBLE PRECISION')
the hint
/*+ cardinality(a, 1) */
is just used to tell oracle how many elements are in our array (if not specified, the default will be an assumption of 8k elements). just set to a reasonably accurate number.
You can transform a variable into a query using CONNECT BY (tested on 11g, should work on 10g+):
SQL> WITH DATA AS (SELECT '100,200,300' txt FROM dual)
2 SELECT regexp_substr(txt, '[^,]+', 1, LEVEL) item FROM DATA
3 CONNECT BY LEVEL <= length(txt) - length(REPLACE(txt, ',', '')) + 1;
ITEM
--------------------------------------------
100
200
300
You can then join this result to the table as if it were a standard view:
SQL> WITH DATA AS (SELECT '100,200,300' txt FROM dual)
2 SELECT v.id, dbt.value01
3 FROM dbt
4 RIGHT JOIN
5 (SELECT to_number(regexp_substr(txt, '[^,]+', 1, LEVEL)) ID
6 FROM DATA
7 CONNECT BY LEVEL <= length(txt) - length(REPLACE(txt, ',', '')) + 1) v
8 ON dbt.id = v.id;
ID VALUE01
---------- ----------
100 Ann
300 John
200
One way of tackling this is to dynamically create a common table expression that can then be included in the query. The final synatx you'd be aiming for is:
with list_of_values as (
select 100 val from dual union all
select 200 val from dual union all
select 300 val from dual union all
...)
select
lov.val,
...
from
list_of_values lov left outer join
other_data t on (lov.val = t.val)
It's not very elegant, particularly for large sets of values, but compatibility with a database on which you might have few privileges is very good.

SQL COUNT of COUNT

I have some data I am querying. The table is composed of two columns - a unique ID, and a value. I would like to count the number of times each unique value appears (which can easily be done with a COUNT and GROUP BY), but I then want to be able to count that. So, I would like to see how many items appear twice, three times, etc.
So for the following data (ID, val)...
1, 2
2, 2
3, 1
4, 2
5, 1
6, 7
7, 1
The intermediate step would be (val, count)...
1, 3
2, 3
7, 1
And I would like to have (count_from_above, new_count)...
3, 2 -- since three appears twice in the previous table
1, 1 -- since one appears once in the previous table
Is there any query which can do that? If it helps, I'm working with Postgres. Thanks!
Try something like this:
select
times,
count(1)
from ( select
id,
count(distinct value) as times
from table
group by id ) a
group by times