SQL Oracle Sort string (numbers) and (letters with numbers) - oracle

I am new to oracle and I have a problem.
I have a column named file_id.
When I do an order by it sorts strings such as
1
1
10
100
11
11
110
114
12
300
31
4200
B14
B170
B18
edit:
I would like it to sort this way.
1
1
10
11
11
12
31
100
300
4200
B14
B18
B170
The answer below works perfectly. Only other problem I ran into now..I have records that are blank. How could I make the blank records order at the end?
1
1
10
11
11
12
31
100
300
4200
BLANK
BLANK
BLANK
BLANK
BLANK
B14
B18
B170
Thank you for your help.

select column
from table
order by
regexp_substr(column, '^\D*') nulls first,
to_number(regexp_substr(column, '\d+'))
fiddle

This is an old question, but it was the first hit on google so I thought I'd share an alternative solution:
select column
from table
order by
LPAD(column, 10)
The LPAD function pads the left-side of the string with spaces so that the results will be sorted numerically. This works for non-numeric values, and null values will be sorted last. This works well if you know the maximum length of the strings to be sorted (you may need to adjust the second parameter to suit your needs).
Source: http://www.techonthenet.com/oracle/questions/sort1.php
EDIT:
I noticed that while my solution works well for my case, the output is slightly different from the accepted answer (http://www.sqlfiddle.com/#!4/d935b8/2/0):
1
1
10
11
11
12
31
100
110
114
300
A14
A18
4200
A170
(null)
(null)
4200 should come after 300. For my situation this is good enough, but this may not always be the case.

Based on the previous solution:
SELECT column
FROM table
ORDER BY LPAD(column, (SELECT MAX(LENGTH(column)) FROM table)) ASC
The advantage of this approach is that you don't need know the table column size.

Related

Hive SELECT col, COUNT(*) mismatch

Let me start by saying, I am very new to Hive, so I'm not sure what information folks will need to help me out. Please let me know what information would be useful. Also, while I'd usually create a small dataset to recreate the problem with, I think this problem has to do with the scale of my dataset, because I can't seem to recreate the problem on a smaller dataset. Let me know if you have suggestions to make this more easy to answer.
Okay now that's out of the way, here's my problem. I have a huge dataset, partitioned by month, with about 500 million rows per month. I have a column with an ID number in it (I'll call it idcol), and I want to closely examine a couple of examples where there's a high number of repeated IDs and a very low number. So, I used this:
SELECT idcol, COUNT(*) FROM table WHERE month = 7 GROUP BY idcol LIMIT 10;
And got:
000005185884381 13
000035323848000 24
000017027256315 531
000010121767109 54
000039844553332 3
000013731352481 309
000024387407996 3
000028461234451 67
000016564844672 1
000032933040806 17
So, I went to investigate the first idvar with a count of 3, with:
SELECT * FROM table WHERE month = 7 AND idcol = '000039844553332';
I expected to see just 3 rows, but ended up with 469 rows found! That was strange enough, but then I just happened to run the original line of code above but with LIMIT 5 instead and ended up with:
000005185884381 13
000017027256315 75
000010121767109 25
000013731352481 59
000024387407996 1
And, it may be hard to see because the idcol is so long, but idvar 000017027256315 ended up with a count of 531 when I did LIMIT 10 and just 75 when I did LIMIT 5.
What am I missing?! How can I get a correct count of just a small number of values so I can investigate further?!
BTW my first thought was to make the counting part a sub-query, but that didn't change a thing. I used:
SELECT * FROM (SELECT idcol, COUNT(*) FROM table WHERE month = 7 GROUP BY idcol) x LIMIT 10;
...same EXACT results
Most likely the counts are being computed from statistics.See here for the bug and the related discussion.
hive.compute.query.using.stats = FALSE
If this doesn't fix it try the ANALYZE command before running the count(*)
ANALYZE TABLE table_name PARTITION(month) COMPUTE STATISTICS;

Deleting opposite duplicates in Oracle SQL

I have some issues that I hope you can lend a helping hand towards.
So I have some data with opposite value, for example:
Amount Type ID
10000 10 25
-10000 10 25
20000 11 30
30000 12 49
-30000 12 49
Sorry for the ugly table.
But how can I delete the lines where the amount cancelled out? I would like the ones with 10000 and -10000 to be deleted. But I wont know the specific type and id number. (30000 and -30000 is the same issue).
Any ideas? I've been searching forever, but can only find how to remove a duplicate row, and not both rows.
Hope it makes sense :)
Update. Thanks for the solutions so far! :)
There can be more than 1:1 in the amount column, but those wouldn't identical Type and ID. For exampel could a 6th entry look like this:
Amount Type ID
10000 10 25
-10000 10 25
20000 11 30
30000 12 49
-30000 12 49
10000 31 42
And the last one should not be deleted :) Hope it makes sense now.
On the basis only of the limited information provided...
DELETE x
FROM my_table x
JOIN my_table y
ON y.id = x.id
AND y.type = x.type
AND y.amount = x.amount * -1;

Open a refcursor for a PL/SQL statement?

I'm creating a report for a business using Oracle and SSRS. The report requires me to aggregate contiguous ranges of serial numbers, which can consist of alphanumerics.
For example, say I have the following serials:
OPS114
OPS115
OPS116
OPS117
OPS145
OPS146
OPS160
890RPT
896RPT
897RPT
The report should have a single aggregate row for each contiguous range, with the count of each range, like so:
OPS114 - OPS117 (4)
OPS145 - OPS146 (2)
OPS160 - OPS160 (1)
890RPT - 890RPT (1)
896RPT - 897RPT (2)
I've pulled the data I need, and I'm bulk-collecting it into a table variable. Now, I need to aggregate the rows - this wouldn't be bad if I only needed to manipulate the data, but I need to have this available as a query for the refcursor. Can I open a refcursor for a PL/SQL FOR loop, or am I barking up the wrong tree? I've attempted to Google this, but the "cursor for loop" is not what I'm looking for. The alternative is to try to aggregate the results in SSRS using VB. (So either way, it won't be a good time.) I'm not sure if I have access to create a SQL table type for this, so this is the alternative I've sought.
If anyone has any experience with this, it would be greatly appreciated!
You could do this from a single SQL statement but you need to define the data better. Your column stores strings but you are using them as numbers to find out the range. And it seems the number part could either be before or after the string part.
If you are able to write some logic that separates out the numbers like this (and maybe keep the string part in another column)-
114
115
116
117
145
146
160
890
896
897
Then it reduces to a simple gaps and islands problem.
Step 1 - Select rownum along with this column (this would be a continuous sequence starting from 1)
Step 2 - Subtract rownum from this numeric data column.
Step 3 - Group by that result
Step 4 - Get min(numeric value) and max(numeric value) and count(numeric value) from the group which will be your result when combined as string.
Numeric_part Rownum Difference
------------ ------ ------------
114 1 113
115 2 113
116 3 113
117 4 113
145 5 140
146 6 140
160 7 153
890 8 882
896 9 887
897 10 887
Grouping this by Difference column, you get -
Difference Min(num) Max(num) count(num) Result
---------- --------- ---------- ---------- -----------------
113 114 117 4 114 - 117 (4)
140 145 146 2 145 - 146 (2)
153 160 160 1 160 - 160 (1)
882 890 890 1 890 - 890 (1)
887 896 897 2 896 - 897 (2)
That SQL statement can be used in PLSQL to return a cursor as in this link that #MickMnemonic has in the comments.
Basically -
OPEN cursor_variable FOR SELECT something FROM something...;
I spoke to a coworker about this and he had an idea that I've been able to implement.
I was able to create a pipelined function that handled my data selection and transforms for me; this allowed me to aggregate my rows as I needed to and only pass rows after a contiguous range was complete.
I am now calling that function from a procedure using the SELECT ... FROM TABLE(MYFUNCTION()) syntax. This allows me to get everything into a refcursor without much issue.
While this may not be performant (looping over a cursor, and manually aggregating), this is for a monthly report, and so I won't attempt to optimize until it's necessary (as we have other work to do).

Transpose without PIVOT in ORACLE

currently I am using pl/sql Developer(Oracle). I am told to convert a Row wise arranged data into columns but without the use of PIVOT. Since the Table I am working on dynamically changes, I am not able to use DECODE too.
POLICY SEQS INVDATE SUM(AMT)
-------- ------ ----------- ----------
policA 123 01-JAN-10 40
policA 123 01-FEB-10 50
policA 123 01-MAR-10 60
policA 456 01-JAN-10 360
policA 456 01-FEB-10 450
policA 456 01-MAR-10 540
policA 789 01-FEB-10 1000
polcA 789 01-MAR-10 1000
I have to re-arrange the dates and the sum of amounts column wise. So that the Single Policy and Single SEQS will have the dates and its amount column wise in a line.
"POLICY","SEQS","INST1","INST1SUM","INST2","INST2SUM","INST3","INST3SUM"
"policA","123","01-JAN-10","40","01-FEB-10","50","01-MAR-10","60"
"policA","456","01-JAN-10","360","01-FEB-10","450","01-MAR-10","540"
"policA","789","01-FEB-10","1000","01-MAR-10","1000"
Some Policy might not be starting from Jan, so the INST1 must be from feb, INST2 must be Mar and INST3 and corresponding INSTSUM must be NULL.
Is there any way that this can be done using CROSS JOINS or using xml function?
Can I use xmlagg with alternative data (INST and SUM)?
I have done some research and am not able to solve this out. Can you please help me with this?

use lag int the next line after its line have been executed

This is a very complicated situation for me and I was wondering if someone can help me with it:
Here is my table:
Record_no Type Solde SQLCalculatedPmu DesiredValues
------------------------------------------------------------------------
2570088 Insertion 60 133 133
2636476 Insertion 67 119,104 119,104
2636477 Insertion 68 117,352 117,352
2958292 Insertion 74 107,837 107,837
3148350 Radiation 73 107,837 107,83 <---
3282189 Insertion 80 98,401 98,395
3646066 Insertion 160 49,201 49,198
3783510 Insertion 176 44,728 44,725
3783511 Insertion 177 44,475 44,472
4183663 Insertion 188 41,873 41,87
4183664 Insertion 189 41,651 41,648
4183665 Radiation 188 41,651 41,64 <---
4183666 Insertion 195 40,156 40,145
4183667 Insertion 275 28,474 28,466
4183668 Insertion 291 26,908 26,901
4183669 Insertion 292 26,816 26,809
4183670 Insertion 303 25,842 25,836
4183671 Insertion 304 25,757 25,751
In my table every value in the SQLCalculatedPmu column or desiredValue Column is calculated based on the preceding value.
As you can see, I have calculated the SQLcalculatedPMU column based on the round on 3 decimals. The case is that on each line radiation, the client want to start the next calculation based on 2 decimals instead of 3(represented in the desired values column). Next values will be recalculated. For example line 6 will change as the value in line 5 is now on 2 decimals. I could handle this if there where one single radiation but in my case I have a lot of Radiations and in this case they will change all based on the calculation of the two decimals.
In summary, Here are the steps:
1 - round the value of the preceding row of a raditaiton and put it in the radiation row.
2 - calculate all next insertion rows.
3 - when we reach another radiation we redo steps 1 and 2 and so on
I m using an oracle DB and I m the owner so I can make procedures, insert, update, select.
But I m not familiar with procedures or loops.
For information, this is the formula for SQLCalculatedPmu uses two additional culmns price and number and this is calculated every line cumulativelly for each investor:
(price * number)+(cumulative (price*number) of the preceeding lines)
I tried something like this :
update PMUTemp
set SQLCalculatedPmu =
case when Type = 'Insertion' then
(number*price)+lag(SQLCalculatedPmu ,1) over (partition by investor
order by Record_no)/
(number+lag(solde,1) over (partition by investor order by Record_no))
else
TRUNC(lag(SQLCalculatedPmu,1) over partition by invetor order by Record_no))
end;
but I gave me this error (I think it's because I m looking at the preceiding line that itself is modified during the SQL statement) :
ORA-30486 : window function are allowed only in the SELECT list of a query.
I was wondering if creating a procedure that will be called as many time as the number of radiations would do the job but I m really not good in procedures
Any help
Regards,
just to make my need simpler, all I want is to have the DesiredValues column starting from the SQLCalculatedPmu column. Steps are
1 - on a radiation the value become = trunc(preceding value,2)
2 - calculate all next insertion rows this way : (price * number)+(cumulative (price*number) of the preceeding lines). As the radiation value have changed then I need to recalculate next lines based on it
3 - when we reach another radiation we redo steps 1 and 2 and so on
Kindest regards
You should not need a procedure here -- a SQL update of the Radiation rows in the table would do this quicker and more reliably.
Something like ..
update my_table t1
set (column_1, column_2) =
(select round(column_1,2), round(column_2,2)
from my_table t2
where t2.type = 'Insertion' and
t2.record_no = (select max(t3.record_no)
from my_table t3
where t3.type = 'Insertion' and
t3.record_no < t1.record_no ))
where t1.type = 'Radiation'

Resources