When is a virtual column using AS rendered in SQL? - sql

I wasn't sure how to search for the answer:
select orderid, REGEXP_REPLACE (
orderid,
'^0+(.)',
'\1'
) as new_order_id
from orders
where length('new_order_id') < 6
This returns nothing. But I know the data is there. If I do:
select orderid, REGEXP_REPLACE (
orderid,
'^0+(.)',
'\1'
) as new_order_id
from orders
order by order_id asc
I get order ids like 1, 2, 3...
So how can I get back the ones that are less than six? Does the where not operation on my returned regexp_replace data after the dataset is returned. Oracle if it matters.
Also, I believe my query is knocking out all leading zeros and replacing it with nothing. Not sure what the \1 means. Yes, I copied it. I thought it was putting nothing there, which is what I want. Just truncate leading zeros.
Thanks.

In your query,
where length('new_order_id') < 6
compares the length of the literal string 'new_order_id', not the value of the field new_order_id.
Try removing the quotes:
where length(new_order_id) < 6

Try this:
select * from
(select orderid
, regexp_replace(orderid,'^0+(.)','\1') new_order_id
from orders)
where length(new_order_id) < 6;
You can avoid using regexp:
select orderid
, ltrim(orderid,'0') new_order_id
from orders
where length(ltrim(orderid,'0'))<6
order by 1;

The length of the string 'new_order_id' is never less than 6. Probably you will have to do the length(regexp_replace(...)) < 6 instead if oracle doesn't support using the output column name without quotes (I have no idea).

Related

Teradata Concatenate multiple rows using XMLAGG getting issue in XmlAgg function or any equivalent logic to concatendate multiple rows

I have a table of record tried to concatenate multiple rows on group wise and i use XMLAGG function but when i try to run the query for particular group which has 2000 records, getting error message:
Select failed 9134 : Intermediate aggregate storage limit for aggregation has been exceeded during computation
SELECT
H.GROUP_id,
H.Group_name,
TRIM(
TRAILING ',' FROM (
XMLAGG(TRIM(COALESCE(H.Group_desc, -1) || '') ORDER BY H.LINE_NBR) (VARCHAR(7000))
)
) AS Group_detail
even increased the varchar value but still having same issue
XMLAGG() adds overhead. However, you can get a sense for how large the result set is by using:
SELECT H.GROUP_id, H.Group_name,
SUM(LENGTH(COALESCE(H.Group_Desc, '-1'))) as total_string_length,
COUNT(*) as cnt
FROM . . .
GROUP BY H.GROUP_id, H.Group_name
ORDER BY total_string_length DESC
You will probably find that some of the groups have total string close to or more than 7000 characters.
I'm not sure if you want to fix the data or do something else. But this should at least identify the problem.
The problem is that the concatenation would be repeated for every row in the dataset, you need to get the distinct Group_desc first, try this:
WITH BASE AS(
SEL
H.GROUP_id,
H.Group_name,
H.Group_desc,
MAX(H.LINE_NBR) AS LINE_NBR
FROM TABLE_NAME
GROUP BY 1,2,3
)
SELECT
BASE.GROUP_id,
BASE.Group_name,
TRIM(
TRAILING ',' FROM (
XMLAGG(TRIM(COALESCE(BASE.Group_desc, -1) || '') ORDER BY BASE.LINE_NBR) (VARCHAR(7000)) -- You probably won't need the varchar to be that large.
)
) AS Group_detail
FROM BASE

Concatenate & Trim String

Can anyone help me, I have a problem regarding on how can I get the below result of data. refer to below sample data. So the logic for this is first I want delete the letters before the number and if i get that same thing goes on , I will delete the numbers before the letter so I can get my desired result.
Table:
SALV3000640PIX32BLU
SALV3334470A9CARBONGRY
TP3000620PIXL128BLK
Desired Output:
PIX32BLU
A9CARBONGRY
PIXL128BLK
You need to use a combination of the SUBSTRING and PATINDEX Functions
SELECT
SUBSTRING(SUBSTRING(fielda,PATINDEX('%[^a-z]%',fielda),99),PATINDEX('%[^0-9]%',SUBSTRING(fielda,PATINDEX('%[^a-z]%',fielda),99)),99) AS youroutput
FROM yourtable
Input
yourtable
fielda
SALV3000640PIX32BLU
SALV3334470A9CARBONGRY
TP3000620PIXL128BLK
Output
youroutput
PIX32BLU
A9CARBONGRY
PIXL128BLK
SQL Fiddle:http://sqlfiddle.com/#!6/5722b6/29/0
To do this you can use
PATINDEX('%[0-9]%',FieldName)
which will give you the position of the first number, then trim off any letters before this using SUBSTRING or other string functions. (You need to trim away the first letters before continuing with the next step because unlike CHARINDEX there is no starting point parameter in the PATINDEX function).
Then on the remaining string use
PATINDEX('%[a-z]%',FieldName)
to find the position of the first letter in the remaining string. Now trim off the numbers in front using SUBSTRING etc.
You may find this other solution helpful
SQL to find first non-numeric character in a string
Try this it may helps you
;With cte (Data)
AS
(
SELECT 'SALV3000640PIX32BLU' UNION ALL
SELECT 'SALV3334470A9CARBONGRY' UNION ALL
SELECT 'SALV3334470A9CARBONGRY' UNION ALL
SELECT 'SALV3334470B9CARBONGRY' UNION ALL
SELECT 'SALV3334470D9CARBONGRY' UNION ALL
SELECT 'TP3000620PIXL128BLK'
)
SELECT * , CASE WHEN CHARINDEX('PIX',Data)>0 THEN SUBSTRING(Data,CHARINDEX('PIX',Data),LEN(Data))
WHEN CHARINDEX('A9C',Data)>0 THEN SUBSTRING(Data,CHARINDEX('A9C',Data),LEN(Data))
ELSE NULL END AS DesiredResult FROM cte
Result
Data DesiredResult
-------------------------------------
SALV3000640PIX32BLU PIX32BLU
SALV3334470A9CARBONGRY A9CARBONGRY
SALV3334470A9CARBONGRY A9CARBONGRY
SALV3334470B9CARBONGRY NULL
SALV3334470D9CARBONGRY NULL
TP3000620PIXL128BLK PIXL128BLK

Sort alphanumeric column

I have a column in database:
Serial Number
-------------
S1
S10
...
S2
S11
..
S13
I want to sort and return the result as follows for serial number <= 10 :
S1
S2
S10
One way I tried was:
select Serial_number form table where Serial_Number IN ('S1', 'S2',... 'S10');
This solves the purpose but looking for a better way
Here is an easy way for this format:
order by length(Serial_Number),
Serial_Number
This works because the prefix ('S') is the same length on all the values.
For Postgres you can use something like this:
select serial_number
from the_table
order by regexp_replace(serial_number, '[^0-9]', '', 'g')::integer;
The regexp_replace will remove all non-numeric characters and the result is treated as a number which is suited for a "proper" sorting.
Edit 1:
You can use the new "number" to limit the result of the query:
select serial_number
from (
select serial_number,
regexp_replace(serial_number, '[^0-9]', '', 'g')::integer as snum
from the_table
) t
where snum <= 10
order by snum;
Edit 2
If you receive the error ERROR: invalid input syntax for integer: "" then apparently you have values in the serial_number column which do no follow the format you posted in your question. It means that regexp_replace() remove all characters from the string, so a string like S would cause that.
To prevent that, you need to either exclude those rows from the result using:
where length(regexp_replace(serial_number, '[^0-9]', '', 'g')) > 0
in the inner select. Or, if you need those rows for some reason, deal with that in the select list:
select serial_number
from (
select serial_number,
case
when length(regexp_replace(serial_number, '[^0-9]', '', 'g')) > 0 then regexp_replace(serial_number, '[^0-9]', '', 'g')::integer as snum
else null -- or 0 whatever you need
end as snum
from the_table
) t
where snum <= 10
order by snum;
This is a really nice example on why you should never mix two different things in a single column. If all your serial numbers have a prefix S you shouldn't store it and put the real number in a real integer (or bigint) column.
Using something like NOT_SET to indicate a missing value is also a bad choice. The NULL value was precisely invented for that reason: to indicate the absence of data.
Since only the first character spoils your numeric fun, just trim it with right() and sort by the numeric value:
SELECT *
FROM tbl
WHERE right(serial_number, -1)::int < 11
ORDER BY right(serial_number, -1)::int;
Requires Postgres 9.1 or later. In older versions substitute with substring (x, 10000).

Oracle "Select Level from dual" does not work as expected with to_number result

Why does
select *
from (
SELECT LEVEL as VAL
FROM DUAL
CONNECT BY LEVEL <= 1000
ORDER BY LEVEL
) n
left outer join (select to_number(trim(alphanumeric_column)) as nr from my_table
where NOT regexp_like (trim(alphanumeric_column),'[^[:digit:]]')) d
on n.VAL = d.nr
where d.nr is null
and n.VAL >= 100
throw a ORA-01722 invalid number (reason is the last row, n.VAL), whereas the similar version with numeric columns im my_table works fine:
select *
from (
SELECT LEVEL as VAL
FROM DUAL
CONNECT BY LEVEL <= 1000
ORDER BY LEVEL
) n
left outer join (select numeric_column as nr from my_table) d
on n.VAL = d.nr
where d.nr is null
and n.VAL >= 100
given that numeric_column is of type number and alphanumeric_column of type nvarchar_2. Note that the upper example works fine without the numerical comparison (n.VAL >= 100).
Does anybody know?
This problem was driving me crazy. I narrowed the problem to a simpler query
SELECT *
FROM (SELECT TO_NUMBER(TRIM (alphanumeric_column)) AS nr
FROM my_table
WHERE NOT REGEXP_LIKE (TRIM (alphanumeric_column), '[^[:digit:]]')) d
WHERE d.nr > 1
With alphanumeric_colum values of ('100','200','XXXX'); Running the above statement gave the "invalid number" error. I then made a slight change to the query to use the CAST function instead of TO_NUMBER:
SELECT *
FROM (SELECT CAST (TRIM (alphanumeric_column) AS NUMBER) AS nr
FROM my_table
WHERE NOT REGEXP_LIKE (TRIM (alphanumeric_column), '[^[:digit:]]')) d
WHERE d.nr > 1
And this correctly returned - 100, 200. I would think that those functions would be similar in behavior. It almost appears as though oracle is trying to evaluate the d.nr > 1 constraint before the view is constructed, which makes no sense. If anyone can shed light on why this is happening, I would be grateful. See SQLFiddle example
UPDATE: I did some more digging, because I don't like not knowing why something just works. I ran EXPLAIN PLAN on both queries and got some interesting results.
For the query that failed, the predicate information looks like this:
1 - filter(TO_NUMBER(TRIM("ALPHANUMERIC_COLUMN"))>1 AND NOT
REGEXP_LIKE (TRIM("ALPHANUMERIC_COLUMN"),'[^[:digit:]]'))
You will notice that the TO_NUMBER function is called first in the AND condition, then
the regexp to exclude alpha values. I am thinking oracle maybe does a short-circuit evaluation with the AND condition, and since it is executing TO_NUMBER first, it fails.
However, when we use the CAST function, the evaluation order is swapped, and the
regexp exclusion is evaluated first. Since for the alpha values, it is false, then the
second part of the AND clause is not evaluated, and the query works.
1 - filter( NOT REGEXP_LIKE (TRIM("ALPHANUMERIC_COLUMN"),'[^[:digit:]
]') AND CAST(TRIM("ALPHANUMERIC_COLUMN") AS NUMBER)>1)
Oracle can be strange sometimes.
I believe when it comes to the Predicate (where) clause, Oracle can/will reorder the entire plan as it sees fit. So with regard to the predicate, it will short-circuit (as OldProgrammer noted) the evaluation however it wants, and you wont be able to guarantee the exact order it occurs.
In your current SQL, you are depending on the predicate to remove non numbers. One option would be to not use "WHERE NOT regexp_like ..." and instead use regexp_substr with coalesce. For example:
create table t_tab2
(
col varchar2(10)
);
create index t_tab2_idx on t_tab2(col);
insert into t_tab2
select level from dual
connect by level <= 100;
insert into t_tab2 values ('123ABC456');
commit;
-- select values > 95 (96->100 exclude non numbers)
select d.* from
(
select COALESCE(TO_NUMBER(REGEXP_SUBSTR(trim(col), '^\d+$')), 0) as nr
from t_tab2
) d
where d.nr > 95;
This should run without throwing invalid number error. Note that the coalesce will return the number 0 for any non numbers coming from the data, you may want to change that based on your needs and data.

SQL Using ORDER BY with UNION doesn't sort numbers correctly (e.g. 10 before 8)

I've tried looking for the answer, and read many threads on this site, but still can't find the answer I'm looking for.
I am trying to sort a series of numbers that are real look-ups and also one * which isn't, I can sort fine when I don't need to add the fake * but not after.
I have tried
SELECT DISTINCT MasterTable.ClassName, MasterTable.ClassYear
FROM MasterTable
UNION ALL
SELECT DISTINCT "*" As [ClassName], "1" As [ClassYear]
FROM MasterTable
ORDER BY MasterTable.ClassYear;
And
SELECT DISTINCT MasterTable.ClassName, MasterTable.ClassYear
FROM (
SELECT DISTINCT MasterTable.ClassName, MasterTable.ClassYear FROM MasterTable
UNION
SELECT DISTINCT "*" As [ClassName], "1" As [ClassYear] FROM MasterTable
)
ORDER BY MasterTable.ClassYear;
But both return the ClassYear as 1, 10, 12, 8... rather than 1, 8, 10, 12....
Any help would be much appreciated,
Thanks :)
MasterTable.ClassYear is varchar so it will sort as a string.
You'll have to convert it in the query or fix the column type.
For the 2nd clause, you also need only:
SELECT "*" As [ClassName], "1" As [ClassYear] --No FROM MasterTable
However, you can "cheat" and do this. Here 1 will be int and will force a conversion to int from the 1st clause because
SELECT "*" As [ClassName], 1 As [ClassYear] --force int. And fixed on edit
UNION ALL
SELECT DISTINCT MasterTable.ClassName, MasterTable.ClassYear
FROM MasterTable
ORDER BY ClassYear; --no table ref needed
It's property sorting those values as strings. If you want them in numerical order, try something like Cast(MasterTable.ClassYear AS int), either in the select or in the order by, or both, depending on how you end up structuring your query.
And instead of SELECT ..., "1" As [ClassYear], write SELECT ..., 1 As [ClassYear].
You are returning the year as a string, not a number. That means that it's sorted as text, not numerically.
Either return the year as a number, or convert the value into a number when sorting it.