oracle transpose based on column values - sql

I know transpose (or pivot) w/ sql is a common ask, but I haven't been able to get to exactly what I'm trying to do on stack/google.
In short, I want case when/then without hardcoding all possible values of a column because these values may be numerous and/or change over time. For example,
id col val
1 a 65
1 b 34
1 c 25
2 a 67
2 c 22
...
the goal is to wind up with a single row for each distinct id, with columns for each distinct col
Easy enough when the values of col are static and small, but when there are dozens of such values hardcoding every possible clause in a case statement seems arduous.
in psuedo code, what i want to do is
select
for each attr in (select distinct col from table)
sum(case when col = attr then val end) as transposed_attr,
end for
from table
group by id
But i'm inexperienced with PL/SQL, so I don't know how to achieve this in oracle.
Advice?

What version of Oracle? 11g introduces the pivot command...
Infact, just look here for both using PIVOT command and not:
http://orafaq.com/wiki/PIVOT

Related

SYBASE ASE String replace in WHERE clause

I have a SYBASE ASE table with below values :
Table 1 :
**Value** **Status**
A STATUS 1
B STATUS 3
C STATUS 4
I have to filter the values based on the list of values like this .. STATUS1,STATUS2,STATUS3 (no space between values).
I want to remove the space/blanks from the value column from Table 1 and compare against the list.
I tried the below code and it wasn't working
select value ,status from Table 1
where str_replace(status,' ','') IN ('STATUS1','STATUS2','STATUS3')
select value ,status from Table 1
where str_replace(status,' ',NULL) IN ('STATUS1','STATUS2','STATUS3')
Any idea how to achieve without changing the list values
The latter one should work (apart from the Table 1 table name).
Note that an empty string in Sybase is often interpreted as a single space. See http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.dc36271.1550/html/blocks/blocks311.htm

SQL - Finding duplicates based on 3 columns with different data types

SQL noob here, let me know if I'm not wording anything right. I'm trying to find all entries where there is more than one instance of the same data in 3 columns. Below is some sample data from the 3 columns.
formatid type_from call_desc_code
20 002694W0:USAGE V9
20 013030W0:USAGE OM
20 013030W0:USAGE NULL
From what I understand checksum can be used for this but the output from the below query doesn't seem right. The first part of the query that I'm putting into the #temp table has 29824 results which tells me there should be only 29824 unique combinations of the 3 columns but when I've run the full query then tried removing duplicates in Excel based on only those 3 columns to sanity check the results I have a whole lot more then 29824 entries left.
The formatid is a smallint data type so when I've tried just concatenating the cells with + it returns a conversion failed error. I'm running SQL Server 2012 but I don't think the database is on the same as it doesn't recognise the concat function.
select checksum(formatid,type_from,call_desc_code) & checksum(reverse(formatid),reverse(type_from),reverse(call_desc_code)) as [checksum], count(*) as [Blah]
into #temp
from Table
group by checksum(formatid,type_from,call_desc_code) & checksum(reverse(formatid),reverse(type_from),reverse(call_desc_code))
having count(*) > 1
select * from
Table
where checksum(formatid,type_from,call_desc_code) & checksum(reverse(formatid),reverse(type_from),reverse(call_desc_code)) in (select [checksum] from #temp)
drop table #temp
this will get you everything from your source table which has duplicates
select *
from table t
inner join
(select formatid,type_from,call_desc_code
from Table
group by formatid,type_from,call_desc_code
having count(*) > 1) dup
on dup.formatid = t.formatid
and dup.type_from = t.type_from
and dup.call_desc_code = t.call_desc_code

Using Recursive CTE on ORACLE to create a multicolumn filtering

I am currently using a VBA function to loop over an Oracle result set to eliminate duplicates on multiple columns/fields (i.e. only distinct values in each column) For example:
My result set is ordered by RECORD_ID, and I want to eliminate FIELD_1 and FIELD_2 duplication:
RECORD_ID FIELD_1 FIELD_D
1 A i
2 A j
3 B i
4 B k
5 C j
6 C k
7 D k
So my program creates a new table (say FINAL_TABLE) and evaluates every line in the original sql resultset (say TABLE_1):
IF the current value of TALBE_1.FIELD_1 IS NOT in FINAL_TABLE.FIELD_1 AND the current value of TALBE_1.FIELD_2 IS NOT in FINAL_TABLE.FIELD_2 THEN insert record/row into FINAL_TABLE
This results in
Column 1 Column 2 Column 3
1 A i
4 B k
5 C j
Where there is only unique values on both columns 2 and 3.
I have tried looking into a way of moving away from loops into SQL with the LAG and PATTERN MATCHING functions but cant figure it out. (cant think of a way to use distinct)
I have also looked at methods that create a table of possible combinations and then select from there but this is unfeasible since only a couple of thousand rows of data would make the number of combinations too large for most computers to handle.
Bottom line: Can this logic be implemented through a recursive SQL query?
If you store the result set in a temporary table, then I think you can do this with delete:
delete from temp
where exists (select 1
from temp t2
where t2.id < temp.id and (t2.col2 = temp.col2 or t2.col3 = temp.col3)
);

How to assign a value to a casted column in Oracle

I am wondering whether is possible to assign a value to a casted column in SQL depending on real table values.
For Example:
select *, cast(null as number) as value from table1
where if(table1.id > 10 then value = 1) else value = 0
NOTE: I understand the above example is not completely Oracle, but, it is just a demonstration on what I want to accomplish in Oracle. Also, the above example can be done multiple ways due to its simplicity. My goal here is to verify if it is possible to accomplish the example using casted columns (columns not part of table1) and some sort of if/else.
Thanks,
Y_Y
select table1.*, (case when table1.id > 10 then 1 else 0 end) as value
from table1

comparing 2 consecutive rows in a recordset

Currently,I have this objective to meet. I need to query the database for certain results. After done so, I will need to compare the records:
For example: the query return me with 10 rows of records, I then need to compare: row 1 with 2, row 2 with 3, row 3 with 4 ... row 9 with 10.
The final result that I wish to have is 10 or less than 10 rows of records.
I have one approach currently. I do this within a function, hand have the variables call "previous" and "current". In a loop I will always compare previous and current which I populate through the record set using a cursor.
After I got each row of filtered result, I will then input it into a physical temporary table.
After all the results are in this temporary table. I'll do a query on this table and insert the result into a cursor and then returning the cursor.
The problem is: how can I not use a temporary table. I've search through online about using nested tables, but somehow I just could not get it working.
How to replace the temp table with something else? Or is there other approach that I can use to compare the row columns with other rows.
EDIT
So sorry, maybe I am not clear with my question. Here is a sample of the result that I am trying to achieve.
TABLE X
Column A B C D
100 300 99 T1
100 300 98 T2
100 300 97 T3
100 100 97 T4
100 300 97 T5
101 11 11 T6
ColumnA is the primary key of the table. ColumnA has duplicates because table X is an audit table that keep tracks of all changes.column D acts as the timestamp for that record.
For my query, I am only interested in changes in column A,B and D. After the query I would like to get the result as below:
Column A B D
100 300 T1
100 100 T4
100 300 T5
101 11 T6
I think Analytics might do what you want :
select col1, col2, last(col1) over (order by col1, col2) LASTROWVALUE
from table1
this way, LASTROWVALUE will contain de value of col1 for the last row, which you can directly compare to the col1 of the current row.
Look this URL for more info : http://www.orafaq.com/node/55
SELECT ROW_NUMBER() OVER(ORDER BY <Some column name>) rn,
Column1, <Some column name>, CompareColumn,
LAG(CompareColumn) OVER(ORDER BY <Some column name>) PreviousValue,
LEAD(CompareColumn) OVER(ORDER BY <Some column name>) NextValue,
case
when CompareColumn != LEAD(CompareColumn) OVER(ORDER BY <Some column name>) then CompareColumn||'-->'||LEAD(CompareColumn) OVER(ORDER BY <Some column name>)
when CompareColumn = LAG(CompareColumn) OVER(ORDER BY <Some column name>) then 'NO CHANGE'
else 'false'
end
FROM <table name>
You can use this logic in a loop to change behaviour.
Hi It's not very clear what exactly yuo want to accomplish. But maybe you can fetch the results of the original query in a PLSQL collection and use that to do your comparison.
What exactly are you doing the row comparison for? Are you looking to eliminate duplicates, or are you transforming the data into another form and then returning that?
To eliminate duplicates, look to use GROUP BY or DISTINCT functionality in your SELECT.
If you are iterating over the initial data and transforming it in some way then it is hard to do it without using a temporary table - but what exactly is your problem with the temp table? If you are concerned about the performance of a cursor then maybe you could do one outer SELECT that compares the results of two inner SELECTs - but the trick is that the second SELECT is offset by one row, so you achieve the requirement of comparing row 1 against row2, etc.
I think you are complicating things with the temp table.
It can be made using a cursor and 2 temporary variables.
Here is the pseudo code:
declare
v_temp_a%xyz;
v_temp_b%xyz;
i number;
cursor my_cursor is select xyz from xyz;
begin
i := 1;
for my_row in my_cursor loop
if (i = 1)
v_temp_a := my_row;
else
v_temp_b := v_temp_a;
v_temp_a := my_row;
/* at this point v_temp_b has the previous row and v_temp_a has the currunt row
compare them and put whatever logic you want */
end if
i := i + 1;
end loop
end