ORA-01722: Invalid number for Fr Locale for Oracle 11g - database

I have a view named My_View which contains one varchar column having numeric decimal data.
While,select calculating avg in am getting error
ORA-01722: Invalid number for Fr Locale
Here is my oracle query which i tried but getting the error:
select (AVG(MY_COLUMN))
from MY_TABLE;
select TO_NUMBER((AVG(MY_COLUMN)), '90.9999', 'NLS_NUMERIC_CHARACTERS='',.''')
from MY_TABLE
GROUP BY MY_COLUMN;
How to get rid of this error?

Starting with Oracle 12.2, you can use the on conversion error clause of to_number() to return a default value when the conversion fails. This is handy for your use case: you can return null on conversion error, which aggregate function avg() will happily ignore.
select avg(
to_number(
my_column default null on conversion error,
'9999d9999',
'nls_numeric_characters = ''.,'''
)
) my_avg
from my_table;

Problem seems to be in data that isn't "numeric" (why do you keep numbers in VARCHAR2 columns)? For example, it contains '123A56'. What is AVG of that value?
A simple option is to use REGEXP_LIKE and perform numeric operations only on "valid" values. For example:
SQL> with test (col) as
2 (select '1234.56' from dual union all -- valid
3 select '131' from dual union all -- valid
4 select 'ABC' from dual union all -- invalid
5 select 'xy.23' from dual union all -- invalid
6 select '.3598' from dual union all -- invalid
7 select '12.34.56'from dual -- invalid
8 )
9 select col,
10 to_number(col, '9999D9999', 'nls_numeric_characters = ''.,''') col_as_num
11 from test
12 where regexp_like(col, '^\d+\.?\d+$');
COL COL_AS_NUM
-------- ----------
1234.56 1234,56
131 131
SQL>
Now you can AVG such values:
SQL> with test (col) as
2 (select '1234.56' from dual union all -- valid
3 select '131' from dual union all -- valid
4 select 'ABC' from dual union all -- invalid
5 select 'xy.23' from dual union all -- invalid
6 select '.3598' from dual union all -- invalid
7 select '12.34.56'from dual -- invalid
8 )
9 select avg(to_number(col, '9999D9999', 'nls_numeric_characters = ''.,''')) result
10 from test
11 where regexp_like(col, '^\d+\.?\d+$');
RESULT
----------
682,78
SQL>

Related

SQL Server - Behaviour of ROW_NUMBER Partition by Null Value

I find this behaviour very strange and counterintuitive. (Even for SQL).
set ansi_nulls off
go
;with sampledata(Value, CanBeNull) as
(
select 1, 1
union
select 2, 2
union
select 3, null
union
select 4, null
union
select 5, null
union
select 6, null
)
select ROW_NUMBER() over(partition by CanBeNull order by value) 'RowNumber',* from sampledata
Which returns
1 3 NULL
2 4 NULL
3 5 NULL
4 6 NULL
1 1 1
1 2 2
Which means that all of the nulls are being treated as part of the same group for the purpose of calculating the row number. It doesn't matter whether the SET ANSI_NULLLS is on or off.
But since by definition the null is totally unknown then how can the nulls be grouped together like this? It is saying that for the purposes of placing things in a rank order that apples and oranges and the square root of minus 1 and quantum black holes or whatever can be meaningfully ordered. A little experimentation suggests that the first column is being used to generate the rank order as
select 1, '1'
union
select 2, '2'
union
select 5, null
union
select 6, null
union
select 3, null
union
select 4, null
generates the same values. This has significant implications which have caused problems in legacy code I am dealing with. Is this the expected behaviour and is there any way of mitigating it other than replacing the null in the select query with a unique value?
The results I would have expected would have been
1 3 NULL
1 4 NULL
1 5 NULL
1 6 NULL
1 1 1
1 2 2
Using Dense_Rank() makes no difference.
Yo.
So the deal is that when T-SQL is dealing with NULLs in predicates, it uses ternary logic (TRUE, FALSE or UNKNOWN) and displays the behavior that you have stated that you expect from your query. However, when it comes to grouping values, T-SQL treats NULLs as one group. So your query will group the NULLs together and start numbering the rows within that window.
For the results that you say you would like to see, this query should work...
WITH sampledata (Value, CanBeNull)
AS
(
SELECT 1, 1
UNION
SELECT 2, 2
UNION
SELECT 3, NULL
UNION
SELECT 4, NULL
UNION
SELECT 5, NULL
UNION
SELECT 6, NULL
)
SELECT
DENSE_RANK() OVER (PARTITION BY CanBeNull ORDER BY CASE WHEN CanBeNull IS NOT NULL THEN value END ASC) as RowNumber
,Value
,CanBeNull
FROM sampledata

Finding longest sequence in SQL

If I have a table which contains dates, eg (in year-month-day then time format):
2015-06-22 12:39:11.257
2015-06-22 15:44:46.790
2015-06-22 15:48:50.583
2015-06-23 08:25:50.060
2015-07-01 07:11:37.037
2015-07-07 13:40:11.997
2015-07-08 13:12:08.723
2015-07-08 13:12:13.900
2015-07-08 13:12:16.010
2015-07-10 12:29:59.777
2015-07-13 15:42:49.077
2015-07-13 15:47:48.670
2015-07-13 15:47:51.547
2015-07-14 08:11:53.023
2015-07-14 08:14:21.243
2015-07-14 08:16:49.410
2015-07-14 08:17:11.997
2015-07-14 09:58:28.840
2015-07-14 09:59:34.640
2015-07-15 15:39:39.993
2015-07-17 08:45:20.157
2015-07-24 14:00:00.487
2015-07-24 14:03:53.773
2015-07-24 14:12:41.717
2015-07-24 14:13:33.957
2015-07-24 14:15:40.953
2015-08-25 12:43:03.920
... is there a way (in SQL) that I can find the longest unbroken sequence of days. I just need the total number of days. So in the above, there are entries for 22nd June and 23rd of June, so the sequence there is 2 days. There's also entries for 13th July, 14th July, and 15th July; this is the longest sequence - 3 days. I don't care about the time portion, so an entry just before midnight, then an entry just after would count as 2 days.
So I want some SQL that can look at the table, and return the value 3 for the above.
No need for a cursor or any type of recursion to solve this. You can do this using a gaps and islands technique. This produces the desired output from your sample data.
with SomeDates as
(
select cast('2015-06-22 12:39:11.257' as datetime) as MyDate union all
select '2015-06-22 15:44:46.790' union all
select '2015-06-22 15:48:50.583' union all
select '2015-06-23 08:25:50.060' union all
select '2015-07-01 07:11:37.037' union all
select '2015-07-07 13:40:11.997' union all
select '2015-07-08 13:12:08.723' union all
select '2015-07-08 13:12:13.900' union all
select '2015-07-08 13:12:16.010' union all
select '2015-07-10 12:29:59.777' union all
select '2015-07-13 15:42:49.077' union all
select '2015-07-13 15:47:48.670' union all
select '2015-07-13 15:47:51.547' union all
select '2015-07-14 08:11:53.023' union all
select '2015-07-14 08:14:21.243' union all
select '2015-07-14 08:16:49.410' union all
select '2015-07-14 08:17:11.997' union all
select '2015-07-14 09:58:28.840' union all
select '2015-07-14 09:59:34.640' union all
select '2015-07-15 15:39:39.993' union all
select '2015-07-17 08:45:20.157' union all
select '2015-07-24 14:00:00.487' union all
select '2015-07-24 14:03:53.773' union all
select '2015-07-24 14:12:41.717' union all
select '2015-07-24 14:13:33.957' union all
select '2015-07-24 14:15:40.953' union all
select '2015-08-25 12:43:03.920'
)
, GroupedDates as
(
select cast(MyDate as DATE) as MyDate
, DATEADD(day, - ROW_NUMBER() over (Order by cast(MyDate as DATE)), cast(MyDate as DATE)) as DateGroup
from SomeDates
group by cast(MyDate as DATE)
)
, SortedDates as
(
select DATEDIFF(day, min(MyDate), MAX(MyDate)) + 1 as GroupCount
, min(MyDate) as StartDate
, MAX(MyDate) as EndDate
from GroupedDates
group by DateGroup
)
select top 1 GroupCount
, StartDate
, EndDate
from SortedDates
order by GroupCount desc
The input here is, in fact:
select trunc(date_column,'DD') day
from your_table
group by trunc(date_column,'DD');
From this point I can consider dates as numbers to input more easier the data and your problem is to find longest consecutive sequence.
so, an input table:
create table a(
col integer);
insert into a values (1);
insert into a values (2);
insert into a values (4);
insert into a values (5);
insert into a values (6);
insert into a values (8);
insert into a values (9);
insert into a values (11);
insert into a values (13);
insert into a values (14);
insert into a values (17);
and with this query you will get the longest sequence starting from every line:
with s(col, i) as (
select col, 1 i from a
union all
select a.col, i + 1
from s join a on s.col = a.col+1
)
--select * from s
select col, max(i)
from s
group by col
order by col
;
Result:
col max
1 2
2 1
4 3
5 2
6 1
8 2
9 1
11 1
13 2
14 1
17 1
From this point you can easily select the maximum. Also, for dates you can use dateadd(dd,1,date_column).
The explanation of recursive CTE: For every row I will find (if exists) the next row and increment the column i. The recursion exits when there are no "next" line.
OBS: I believe the code can be improved, but you got the ideea.
SQLFIDDLE
UPDATE To improve performance and keep using recursivity we can start only from numbers that doesn't have a prior consecutive number.
with p as (
select * from (
select col, coalesce(col - (lag(col) over (order by col)),2) as has_prev
from a
) b
where has_prev != 1
),
s(col, i) as (
select col, 1 i from p
union all
select s.col, i + 1
from s join a on s.col+i = a.col
)
--select * from p
select col, max(i)
from s
group by col
order by col
;
SQLFIDDLE2

Retrieve only distinct combinations

I'm having column as Node varchar(25) in MS-SQL Server.
The possible values are
node
-----
D-C
C-B
B-A
B-C
B-E
C-A
A-B
A-C
C-D
etc.
I want to retrieve the the distinct combinations from it.
E.g.:
node
----
D-C
C-B
B-A
B-E
C-A
Please tell the SQL for this.
You have two pieces of data pressed into one column. This is not ideal. So my solution first has to correct this:
SQL> create table mytable (node)
2 as
3 select 'D-C' from dual union all
4 select 'C-B' from dual union all
5 select 'B-A' from dual union all
6 select 'B-C' from dual union all
7 select 'B-E' from dual union all
8 select 'C-A' from dual union all
9 select 'A-B' from dual union all
10 select 'A-C' from dual union all
11 select 'C-D' from dual
12 /
Table created.
SQL> with structured_data as
2 ( select regexp_substr(node,'[^-]+',1,1) startnode
3 , regexp_substr(node,'[^-]+',1,2) endnode
4 from mytable
5 )
6 select distinct least(startnode,endnode) startnode
7 , greatest(startnode,endnode) endnode
8 from structured_data
9 /
STARTNODE ENDNODE
--------- -------
B E
A C
A B
C D
B C
5 rows selected.
select distinct(Node) from YOUR_TABLE;
Here is a SQLfiddle with examples : http://www.sqlfiddle.com/#!4/76484/2
I answered for oracle since the question is tagged oracle. For MS-Server, it's probably the same thing though...
Another approach. Very similar to what Rob van Wijk has posted in terms of using greatest() and least() functions, but without invocation of regular expression functions. We can calculate greatest and least of column value and its revers value - value returned by the reverse() function:
Note: reverse() function is undocumented function. Do not use it in the the production application(s).
with t1(col) as(
select 'D-C' from dual union all
select 'C-B' from dual union all
select 'B-A' from dual union all
select 'B-C' from dual union all
select 'B-E' from dual union all
select 'C-A' from dual union all
select 'A-B' from dual union all
select 'A-C' from dual union all
select 'D-C' from dual
)
select res_1 /* as well/or we can choose res_2 */
from (select distinct
greatest(col, reverse(col)) as res_1
, least(col, reverse(col)) as res_2
from t1)
Result:
RES_1
-----
D-C
B-A
C-A
C-B
E-B
Or #2
select col
from ( select col
, row_number() over(partition by greatest(col, reverse(col))
, least(col, reverse(col))
order by col) as rn
from t1
)
where rn = 1
Result:
COL
---
A-B
A-C
B-C
D-C
B-E

datepart function not working in sql azure

I want to split date part like year and assign it to a variable in a stored procedure.
I run that stored procedure in sql azure. it throws error "Reference to database and/or server name in 'MASTER..spt_values' is not supported in this version of SQL Server."
Code:
declare #Year int
SET #Year =DATEPART(YYYY,GETDATE())
create table #SundayDates (Sunday datetime,NextSunday datetime)
INSERT INTO #SundayDates(Sunday,NextSunday)
SELECT max(dates),MAX(DATEADD(DD,+7,dates)) AS last_sunday from
(
SELECT dateadd(day,number-1,DATEADD(year,#year-1900,0)) AS dates
FROM MASTER..spt_values WHERE type='p' and
number between 1 and DATEDIFF(day,DATEADD(year,#year-1900,0),DATEADD(year,#year-1900+1,0))
) as t
WHERE DATENAME(weekday,dates)='sunday' GROUP BY DATEADD(month,datediff(month,0,dates),0)
This query gives the same results (as a result set, rather than inserting into a temp table, but can be easily adapted to do so) and doesn't rely on the spt_values table that the error message tells you isn't allowed:
;With Numbers (Num) as (
select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all
select 6 union all select 7 union all select 8 union all select 9 union all select 10 union all select 11
), MonthEnds as (
select DATEADD(month,DATEDIFF(year,'20010101',CURRENT_TIMESTAMP)*12 + n.Num,'20010131') as EndDate
from Numbers n
), LastSundays as (
select DATEADD(day,-n.Num,EndDate) as EndDate
from
MonthEnds me
cross join
Numbers n
where
n.Num between 0 and 6 and
DATEPART(weekday,DATEADD(day,-n.Num,EndDate)) = DATEPART(weekday,'20130512')
)
select EndDate,DATEADD(day,7,EndDate) as FollowingSunday
from LastSundays

Sorting nvarchar column SQL Server 2005

When sorting a nvarchar column in SQL that has a numeric value, SQL Server returns bad sorting and I don't know why!
Is there any particular way SQL Server sorts varchar columns having numeric values?
Thanks
This has a fail-safe which will take into account non numerical values, and place them at the end of your result set.
SELECT [nvarcharColumn]
FROM [Table]
ORDER BY CAST(ISNULL(NULLIF(ISNUMERIC([nvarcharColumn]),0),2147483647) as INT)
If you want it sorted as numbers, you will have to convert it to numbers.
Check here is a list of numbers in an nVarChar column being sorted:
With MyTable AS
(
Select Cast ('1' as nVarChar) nVarCharColumn UNION
Select '100' UNION
Select '99'
)
Select *
From MyTable
Order by 1
Logically 99 should come before the 100, right? But it is sorted on nVarChar at a time, so the first 1 of the 100 is smaller than the first 9 and therefore it comes before 99.
nVarCharColumn
------------------------------
1
100
99
(3 row(s) affected)
If you CAST then you get the desired output
With MyTable AS
(
Select Cast ('1' as nVarChar) nVarCharColumn UNION
Select '100' UNION
Select '99'
)
Select *
From MyTable
Order by CAST (nVarCharColumn as Int)
Here is the proper output
nVarCharColumn
------------------------------
1
99
100
(3 row(s) affected)
Here's a twist in the table. What if there is a string value in the column as well?
;With MyTable AS
(
Select Cast ('1' as nVarChar) nVarCharColumn UNION
Select '100' UNION
Select '99' UNION
Select 'String Value'
)
Select *
From MyTable
Order by CAST (nVarCharColumn as Int)
Error!!
Msg 245, Level 16, State 1, Line 22
Conversion failed when converting the nvarchar value 'String Value' to data type int.
So then the SORT part checks IsNumeric - no need to do the same thing in the SELECT part.
With MyTable AS
(
Select Cast ('1' as nVarChar) nVarCharColumn UNION
Select '100' UNION
Select '99' UNION
Select 'String Value'
)
Select *
From MyTable
Order by CAST ( Case When ISNUMERIC (nVarCharColumn)=1 Then nVarCharColumn Else -99 End as Int)
Get the proper output
nVarCharColumn
------------------------------
String Value
1
99
100
(4 row(s) affected)
you can type cast the values in number like int or float
SELECT nVarCharValue FROM table ORDER BY CAST(nVarCharValue AS INT)
Your sort value should be converted to a numeric value.
ORDER BY CONVERT(INT, YourVarcharCol) ASC
Hope this helps,
John

Resources