SQL "substr" combine with "not in" - sql

I have a table(ne) with this elements:
LECOT113_A42401
DA_RIMUOVERE
TVCCVC16_A46C01
CBCELEN1_A46C01
SPCBA440_A46C02
582
ghhtthth
TESTVMM
SACCALEN_A46C0da_cancellare
MICTEST
DA_CANCELLARE2
and i use this query:
select ne.NODE
from ne
where substr(ne.NODE,9,2) not in ('_A')
why result is:
DA_RIMUOVERE
DA_CANCELLARE2
and not this (TARGET):
582
DA_RIMUOVERE582
ghhtthth
TESTVMM
SACCALEN_A46C0da_cancellare
MICTEST
DA_CANCELLARE2
Thanks!

The function substr() returns null when the starting position is greater than the length of the string, so comparing null to '_A' gives wrong results.
So change to this:
select ne.NODE
from ne
where length(ne.NODE) < 9 or length(ne.NODE) <> 15 or substr(ne.NODE,9,2) not in ('_A')
See the demo.
Results:
> | NODE |
> | :-------------------------- |
> | DA_RIMUOVERE |
> | 582 |
> | ghhtthth |
> | TESTVMM |
> | SACCALEN_A46C0da_cancellare |
> | MICTEST |
> | DA_CANCELLARE2 |

In fact in SQL NULL neither true not false. In your case some of your string has length less then 9, so substr(...) return NULL. Value of NULL not in (...) is NULL.
Additional information can be found on Ask Tom
So, just rewrite condition on something like
select ne.NODE
from ne
where nvl(substr(ne.NODE,9,2),'x') not in ('_A')

The reason is that Oracle confuses the empty string ('') and NULL values. So, the substring returns NULL -- and then most comparisons return NULL rather than false.
One method would use LIKE:
where ne.NODE not like '_________$_A%' escape '$'
Or regexp_like():
where regexp_like(ne.NODE, '^.{9}_A')
Or being sure that the string is long enough:
where substr(ne.NODE || 'xxxxxxxxxxx', 9, 2) not in ('_A')
Or checking for the length as well as the patter.

Try the following query:
SELECT
NODE
FROM
(
SELECT
NE.NODE,
SUBSTR(NE.NODE, 9, 2) SUBST
FROM
NE
)
WHERE
SUBST <> '_A'
OR SUBST IS NULL
db<>fiddle demo
Cheers!!

Related

Convert a percentage(string, with a %) to a decimal in postgresql

I would like to average on the Scores(string) of each person from the following table in postgresql,
No. | Name | Term | Score
1 | A | 1 | 95.00%
2 | A | 2 | 99.00%
3 | C | 1 | 90.00%
4 | D | 1 | 100.00%
.
.
It does not like % on the score. How can I convert it into a decimal/float from a string containing a % as shown above?
Tried,
score::decimal
but it complains as,
ERROR: invalid input syntax for type numeric: "95.00%"
SQL state: 22P02
cast also does not seem to work.
How do I convert this?
One method uses replace():
select replace(score, '%', '')::numeric
If you actually want to convert it to a number between 0 and 1 rather than 0 and 100, try a case:
select (case when right(score, 1) = '%'
then (replace(score, '%', '')::numeric) / 100
else score::numeric
end)

How to select string data and exclude data contain zero

I have a table named ElectronicAddress like below, and a string type column Phone.
Id Name Phone
--------------------------
1 Adele 23432434
2 Diana 0000
3 Whale 0000000
4 Sion 936
5 Aria wwqq
6 Dave 665332
7 Daisy dai567
i want to select Phone which is exclude zero only, exclude character only and must have > 5 characters.
Result i'm trying to get :
Id Name Phone
--------------------------
1 Adele 23432434
6 Dave 665332
7 Daisy dai567
i already try this :
select * from ElectronicAddress where Phone not like '[[:alpha:] -]' and LENGTH(TRIM(Phone)) >5
but i'm having a hard time to exclude data contain zero value.
Apply 3 conditions in the WHERE clause:
select * from ElectronicAddress
where
regexp_like("Phone", '[[:digit:]]')
and length("Phone") > 5
and replace("Phone", '0', '') is not null
Oracle treats empty strings as nulls, this is why you need is not null.
See the demo.
Results:
> Id | Name | Phone
> -: | :---- | :-------
> 1 | Adele | 23432434
> 6 | Dave | 665332
> 7 | Daisy | Dai567
SELECT *
FROM Yourtable
WHERE length (phone) > 5
and REPLACE(Phone, '0', '') <> ''
and LENGTH(TRIM(TRANSLATE(Phone, ' +-.0123456789', ' '))) = 0
By "exclude 0" you seem to mean "exclude strings that only consist of zeros" rather than exclude '0' (as I originally interpreted it). That piece is harder to incorporate into a single regular expression, so separate logic can be used.
I think you want only digits:
where regexp_like(phone, '^[0-9]{5,}$') and replace(phone, '0', '') is not null
Or:
where regexp_like(phone, '^[^a-zA-Z]{5,}$') and replace(phone, '0', '') is not null
Or:
where regexp_like(phone, '^[^[:alpha]]{5,}$') and replace(phone, '0', '') is not null
You can also do this in one regular expression. I'm not sure if there is a cleaner method. The following is brute force, looking for a non-zero digit in any of the first five positions:
where regexp_like(phone, '^[1-9][0-9]{4,}$|^[0-9]{1}[1-9][0-9]{3,}$|^[0-9]{2}[1-9][0-9]{2,}$|^[0-9]{3}[1-9][0-9]{1,}$|^[0-9]{4}[1-9][0-9]*$')
Here is a db<>fiddle.

Oracle SQL - Substring issue

I have an field pattern and value in that field is INDI/17-18/6767/KER/787 .I want to get 6767 from this string
I used the query
select substr(pattern,12,15) from pattern_table
But the output I got is 6767/KER/787 instead of 6767.
Try this:
You have to give the length as the 3rd value, not the position.
SELECT SUBSTR(pattern,12,4) FROM pattern_table
For a generic result to get the 3rd value separated by a delimiter, you may use REGEXP_SUBSTR.
SQL Fiddle
Query 1:
SELECT pattern,REGEXP_SUBSTR(pattern, '[^/]+', 1, 3) id
FROM pattern_table
Results:
| PATTERN | ID |
|--------------------------|-------|
| INDI/17-18/6767/KER/787 | 6767 |
| INDI/17-18-19/67/KER/787 | 67 |
| INDI/16-18/67890/KAR/986 | 67890 |
even this will also work:
SELECT substr('INDI/17-18/6767/KER/787',instr('INDI/17-
18/6767/KER/787','/',1,2)+1,4) FROM dual;

IF statement within a formula in a SQL query

Let's say I have a table with two numeric columns: NUM and DEN.
I need to extract the ratio NUM/DEN only if DEN isn't 0: otherwise the ratio should be 0.
Something like this:
select ID, [...] AS RATIO
from Table
where [...] is some kind of equivalent of the excel formula IF(DEN=0;0;NUM/DEN).
Is there a way to perform this kind of query?
Many thanks!
This should work:
case
when DEN = 0 then 0
else NUM/DEN
end
Yes, what you are looking for is case. It has two versions:
case [variable]
when [value1] then [output1]
when [value2] then [output2]
when [value3] then [output3]
...
else [outputdefault] end
and
case when [Boolean(True/false) expression 1] then [output1]
when [Boolean(True/false) expression 2] then [output2]
when [Boolean(True/false) expression 3] then [output3]
...
else [outputdefault] end
If SQL Server 2012 you can use : IIF
IIF ( boolean_expression, true_value, false_value )
If you are using SQL Server you could use case statement like w0lf mentioned or you could use iif statement like so:
select iif(age > 21, 'Allowed', 'Not Allowed') as status
from test;
Example:
create table test (
fullname varchar (20),
age int
);
insert into test values
('John', 10),
('Matt', 90),
('Jane', 25),
('Ruby', 80),
('Randy', null);
Result
| fullname | status |
|----------|-------------|
| John | Not Allowed |
| Matt | Allowed |
| Jane | Allowed |
| Ruby | Allowed |
| Randy | Not Allowed |
The same thing can be written as
select case when age > 21 then 'Allowed' else 'Not Allowed' end as status
from test;
case statement is used by many database engines.
If you are dealing with null and not null values, you could also use coalesce like so:
select fullname, coalesce(age, 999) as status
from test;
The result will be:
| fullname | status |
|----------|--------|
| John | 10 |
| Matt | 90 |
| Jane | 25 |
| Ruby | 80 |
| Randy | 999 |
At first you may think that coalesce does if age is null then 999 else age. It does that, sort-of, but in particular coalesce outputs the first non-null value in a list. So, coalesce(null, null, 45) will result in 45. coalesce(null, 33, 45) will result in 33.
Feel free to play around with SQL Fiddle: http://sqlfiddle.com/#!6/a41a7/6

SQL for comparison of strings comprised of number and text

I need to compare 2 strings that contains number and possibly text. for example I have this table:
id | label 1 | label 2 |
1 | 12/H | 1 |
2 | 4/A | 41/D |
3 | 13/A | 3/F |
4 | 8/A | 8/B |
..
I need to determine the direction so that if Label 1 < Label2 then Direction is W (with) else it is A (against). So I have to build a view that presents data this way:
id | Direction
1 | A |
2 | W |
3 | A |
4 | W |
..
I'm using postgres 9.2.
WITH x AS (
SELECT id
,split_part(label1, '/', 1)::int AS l1_nr
,split_part(label1, '/', 2) AS l1_txt
,split_part(label2, '/', 1)::int AS l2_nr
,split_part(label2, '/', 2) AS l2_txt
FROM t
)
SELECT id
,CASE WHEN (l1_nr, l1_txt) < (l2_nr, l2_txt)
THEN 'W' ELSE 'A' END AS direction
FROM x;
I split the two parts with split_part() and check with an ad-hoc row type to check which label is bigger.
The cases where both labels are equal or where either one is NULL have not been defined.
The CTE is not necessary, it's just to make it easier to read.
-> sqlfiddle
You can try something like:
SELECT id, CASE WHEN regexp_replace(label_1,'[^0-9]','','g')::numeric <
regexp_replace(label_2,'[^0-9]','','g')::numeric
THEN 'W'
ELSE 'A'
END
FROM table1
regexp_replace deletes all non numeric characters from the string ::numeric converts the string to numeric.
Details here: regexp_replace, pattern matching, CASE WHEN