TRUNC(NVL(COLUMN, '2')) ORA-01722: invalid number - sql

I have a column in table which is varchar2(3) style column. This column has some nulls and when I try to run following query it runs for some records but when I scroll to some record I get ORA-01722: invalid number error.
Query used:
Select TRUNC(NVL(COLUMN, '2'))
from TABLE;
Also I ran distinct on column to see what values it has.
Select distinct COLUMN
from TABLE;
I got following results:
1
2 62
3 90
4 70
5 82
6 71
7 05
8 21
9 81
10 66
11 12
12 95
13 02
14 91
15 92
16 94
17 01
18 65
19 30
20 20
21
22 50
23 63
24 51
25 64
26 09
Why am I getting this error and how can I do this without getting error?

https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions200.htm
Because you want to trunc a string, but TRUNC function is for number or date .
This function takes as an argument any numeric datatype or any nonnumeric datatype that can be implicitly converted to a numeric datatype
Maybe one of your string value cannot be converted to number
edit : your 21t value is not a number and is not null, you should trim your column
SELECT trunc(nvl(trim(column),'2'))
FROM table;

Related

Display rows where multiple columns are different

I have data that looks like this. Thousands of rows returned, but this is just a sample.
Most days have the same numbers in them, but some do not. Note that ID 1 and 5 have identical numbers every day.
ID
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
1
26
26
26
26
26
26
26
2
44
44
30
30
44
44
44
3
55
55
55
55
80
90
55
4
12
12
43
43
43
43
43
5
36
36
36
36
36
36
36
I'd like to only return rows where the days of the week have different numbers.
In this case, the only IDs returned should be 2, 3 & 4.
What would I want this query to look like?
Thanks!
One idea that should work in most RDBMS (with some syntax tweaks) is the following.
This is SQL Server compatible: pivot the days into rows and count the distinct values and filter accordingly:
select id
from t
cross apply (
select Count(distinct d) from (
values(sunday),(monday),(tuesday),(wednesday),(thursday),(friday),(saturday)
)d(d)
)d(v)
where d.v>1

yearly average from monthly daterange data

I have the following table in postgresql;
Value period
1 [2017-01-01,2017-02-01)
2 [2017-02-01,2017-03-01)
3 [2017-03-01,2017-04-01)
4 [2017-04-01,2017-05-01)
5 [2017-05-01,2017-06-01)
6 [2017-06-01,2017-07-01)
7 [2017-07-01,2017-08-01)
8 [2017-08-01,2017-09-01)
9 [2017-09-01,2017-10-01)
10 [2017-10-01,2017-11-01)
11 [2017-11-01,2017-12-01)
12 [2017-12-01,2018-01-01)
13 [2018-01-01,2018-02-01)
14 [2018-02-01,2018-03-01)
15 [2018-03-01,2018-04-01)
16 [2018-04-01,2018-05-01)
17 [2018-05-01,2018-06-01)
18 [2018-06-01,2018-07-01)
19 [2018-07-01,2018-08-01)
20 [2018-08-01,2018-09-01)
21 [2018-09-01,2018-10-01)
22 [2018-10-01,2018-11-01)
23 [2018-11-01,2018-12-01)
24 [2018-12-01,2019-01-01)
25 [2019-01-01,2019-02-01)
26 [2019-02-01,2019-03-01)
27 [2019-03-01,2019-04-01)
28 [2019-04-01,2019-05-01)
29 [2019-05-01,2019-06-01)
30 [2019-06-01,2019-07-01)
31 [2019-07-01,2019-08-01)
32 [2019-08-01,2019-09-01)
33 [2019-09-01,2019-10-01)
34 [2019-10-01,2019-11-01)
35 [2019-11-01,2019-12-01)
36 [2019-12-01,2020-01-01)
37 [2020-01-01,2020-02-01)
38 [2020-02-01,2020-03-01)
39 [2020-03-01,2020-04-01)
40 [2020-04-01,2020-05-01)
41 [2020-05-01,2020-06-01)
42 [2020-06-01,2020-07-01)
How can I get yearly average from monthly data in postgresql?
Note: Column Value is type integer and column period is type daterange.
The expected result should be
6.5 2017
18.5 2018
30.5 2019
39.5 2020
If your periods are always taking one month, including the lower bound and excluding the upper, you could try this
select
avg(value * 1.0) as average,
extract(year from lower(period)) as year
from table
group by year

Aggregate result from query by quarter SQL

Lets say I have a table which holds all exports for some time back in Microsoft SQL database:
Name:
ExportTable
Columns:
id - numeric(18)
exportdate - datetime
In order to get the number of exports per week I can run the following query:
SELECT DATEPART(ISO_WEEK,[exportdate]) as 'exportdate', count(exportdate) as 'totalExports'
FROM [ExportTable]
Group By DATEPART(ISO_WEEK,[exportdate])
order by exportdate;
Returns:
exportdate totalExports
---------- ------------
27 13
28 12
29 15
30 8
31 17
32 10
33 7
34 15
35 4
36 18
37 10
38 14
39 14
40 21
41 19
Would it be possible to aggregate the week results by quarter so the output becomes something like the bellow?
UPDATE
Sorry for not being crystal clear, I would like the current result to add upp with previous result up to a new quarter.
Note week 41 contains 21+19 = 40
Week 39 contains 157 (13+12+15+8+17+10+7+15+4+18+10+14+14)
exportdate totalExports Quarter
---------- ------------ -------
27 13 3
28 25 3
29 40 3
30 48 3
31 65 3
32 75 3
33 82 3
34 97 3
35 101 3
36 119 3
37 129 3
38 143 3
39 157 3 -- Sum of 3 Quarter values.
40 21 4 -- New Quarter show current week value
41 40 4 -- (21+19)
You can use this.
SELECT
DATEPART(ISO_WEEK,[exportdate]) as 'exportdate'
, SUM( count(exportdate) ) OVER ( PARTITION BY DATEPART(QUARTER,MIN([exportdate])) ORDER BY DATEPART(ISO_WEEK,[exportdate]) ROWS UNBOUNDED PRECEDING ) as 'totalExports'
, DATEPART(QUARTER,MIN([exportdate])) [Quarter]
FROM [ExportTable]
Group By DATEPART(ISO_WEEK,[exportdate])
order by exportdate;
You could use a case statement to separate the dates into quarters.
e.g.
CASE
WHEN EXPORT_DATE BETWEEN '1' AND '4' THEN 1
WHEN Export_Date BETWEEN '5' and '9' THEN 2
ELSE 0 AS [Quarter]
END
Its just an example but you get the idea.
You could then use the alias from the case
SELECT DATEPART(ISO_WEEK,[exportdate]) as 'exportdate', count(exportdate) as 'totalExports', DATEPART(quarter,[exportdate]) as quarter FROM [ExportTable] Group By DATEPART(ISO_WEEK,[exportdate]), DATEPART(quarter,[exportdate]) order by exportdate;

transpose column to row oracle

I have a query returned value in this form (query return more than 50 columns).
1-99transval 100-200transval 200-300transval ... 1-99nontransval 100...
50 90 80 67 58
For a row value. I want these details to be converted into columns and take the following shape:
Range Transval NonTransval
1-99 50 67
100-200 90 58
In pure SQL, it will need a lot of coding because you will have to manually put the range as there is no relation between the values and the range at all. Had there been a relationship, you could use CASE expression and build the range dynamically.
SQL> WITH DATA AS
2 (SELECT 50 "1-99transval",
3 90 "100-200transval",
4 80 "200-300transval",
5 67 "1-99nontransval",
6 58 "100-200nontransval",
7 88 "200-300nontransval"
8 FROM dual
9 )
10 SELECT '1-99' range,
11 "1-99transval" transval,
12 "1-99nontransval" nontransval
13 FROM DATA
14 UNION
15 SELECT '100-200' range,
16 "100-200transval",
17 "100-200nontransval" nontransval
18 FROM DATA
19 UNION
20 SELECT '200-300' range,
21 "200-300transval",
22 "200-300nontransval" nontransval
23 FROM DATA;
RANGE TRANSVAL NONTRANSVAL
------- ---------- -----------
1-99 50 67
100-200 90 58
200-300 80 88
From Oracle database 11g Release 1 and above, you could use UNPIVOT
SQL> WITH DATA AS
2 (SELECT 50 "1-99transval",
3 90 "100-200transval",
4 80 "200-300transval",
5 67 "1-99nontransval",
6 58 "100-200nontransval",
7 88 "200-300nontransval"
8 FROM dual
9 )
10 SELECT *
11 FROM DATA
12 UNPIVOT( (transval,nontransval)
13 FOR RANGE IN ( ("1-99transval","1-99nontransval") AS '1-99'
14 ,("100-200transval","100-200nontransval") AS '100-200'
15 ,("200-300transval","200-300nontransval") AS '200-300'));
RANGE TRANSVAL NONTRANSVAL
------- ---------- -----------
1-99 50 67
100-200 90 58
200-300 80 88
Above, in your case you need to replace the WITH clause with your existing query as a sub-query. You need to include other columns in the UNION.
In PL/SQL, you could (ab)use EXECUTE IMMEDIATE and get the "range" by extracting the column names in dynamic sql.
Although, it would be much better to modify/rewrite your existing query which you have not shown yet.
If you are using Oracle 11g version then you can use the UNPIVOT feature.
CREATE TABLE DATA AS
SELECT 50 "1-99transval",
90 "100-200transval",
80 "200-300transval",
67 "1-99nontransval",
58 "100-200nontransval",
88 "200-300nontransval"
FROM dual
SELECT *
FROM DATA
UNPIVOT( (Transval,NonTransval) FOR Range IN ( ("1-99transval","1-99nontransval") as '1-99'
,("100-200transval","100-200nontransval") as '100-200'
,("200-300transval","200-300nontransval") as '200-300'))
http://sqlfiddle.com/#!4/c9747/3/0

convert datediff minutes to 100 base format in sql

I want to calculate date time difference between two dates but the minutes should be in .100 scale i.e if date time difference is 2.30 (2 Hours 30 Minutes) i want it in format 2.50
30 minutes = 0.50
i wrote a query for it but it does not work well when minutes are in range of 01 - 09
Case 1 : Wrong Output
Declare #Start DateTime='02-03-2014 14:25:00'
Declare #End DateTime='02-03-2014 20:29:46'
Select STR(DateDiff(MINUTE,#Start,#End)/60)+'.'+STR(DateDiff(MINUTE,#Start,#End)%60/0.6)
DateTime Difference : 6.04
Expected Output : 6.10
Actual Output : 6.7
Case 2 : Correct Output
Declare #Start DateTime='02-03-2014 13:55:02'
Declare #End DateTime='02-03-2014 17:33:31'
Select STR(DateDiff(MINUTE,#Start,#End)/60)+'.'+STR(DateDiff(MINUTE,#Start,#End)%60/0.6)
DateTime Difference : 3.38
Expected Output : 6.63
Actual Output : 6.63
what i am missing in case, when minutes are less than 10 ??
DB : SQL Server 2008
60 minutes = 60/60 = 1.0
30 minutes = 30/60 = 0.5
4 minutes = 4/60 = 0.066, not 0.10
0.10 = 6 minutes, not 4 minutes difference as in your first example.
If we temporarily remove the seconds to reduce complexity, then you simply divide the seconds by 3600.00:
Declare #Start DateTime='02-03-2014 14:25:00'
Declare #End DateTime='02-03-2014 20:29:00'
SELECT DATEDIFF(s,#Start,#End)/3600.00
This returns 6.066 which is the correct portion of hours to return 6 hours 4 minutes difference and a far simpler expression.
Try this, it should give a very exact result(after rounding):
Declare #Start DateTime='02-03-2014 13:55:02'
Declare #End DateTime='02-03-2014 17:33:31'
Select round(cast(#end-#start as float)*24, 2)
use this if you want to round down
Select floor(cast(#end-#start as float)*2400) / 100
My friend following is tabular presentation of you requirement.
1 1.666666667
2 3.333333333
3 5
4 6.666666667
5 8.333333333
6 10
7 11.66666667
8 13.33333333
9 15
10 16.66666667
11 18.33333333
12 20
13 21.66666667
14 23.33333333
15 25
16 26.66666667
17 28.33333333
18 30
19 31.66666667
20 33.33333333
21 35
22 36.66666667
23 38.33333333
24 40
25 41.66666667
26 43.33333333
27 45
28 46.66666667
29 48.33333333
30 50
31 51.66666667
32 53.33333333
33 55
34 56.66666667
35 58.33333333
36 60
37 61.66666667
38 63.33333333
39 65
40 66.66666667
41 68.33333333
42 70
43 71.66666667
44 73.33333333
45 75
46 76.66666667
47 78.33333333
48 80
49 81.66666667
50 83.33333333
51 85
52 86.66666667
53 88.33333333
54 90
55 91.66666667
56 93.33333333
57 95
58 96.66666667
59 98.33333333
60 100
In First Case you actual difference is 6 hours & 4 Minutes. So as per requirement ans of 6.07 is correct how you are saying it is wrong?
It seems in STR(DateDiff(MINUTE,#Start,#End)%60/0.6) 0.6 should be replaced with 6.0
The above solution would round off upto 1 place after decimal.
If you want to ROUND off to 2 places after decimal you can try the below snippet:
SELECT CAST(CAST(ROUND(DateDiff(MINUTE,#Start,#End)%60/0.6) AS NUMERIC(12,2)) AS VARCHAR(50))