How to select string data and exclude data contain zero - sql

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.

Related

Padding inside of a string in SQL

I just started learning SQL and there is my problem.
I have a column that contains acronyms like "GP2", "MU1", "FR10", .... and I want to add '0's to the acronyms that don't have enough characters.
For example I want acronyms like "FR10", "GP48",... to stay like this but acronyms like "MU3" must be converted into "MU03" to be as the same size as the others.
I already heard about LPAD and RPAD but it just add the wanted character at the left or the right.
Thanks !
Is the minimum length 3 as in your examples and the padded value should always be in the 3rd position? If so, use a case expression and concat such as this:
with my_data as (
select 'GP2' as col1 union all
select 'MU1' union all
select 'FR10'
)
select col1,
case
when length(col1) = 3 then concat(left(col1, 2), '0', right(col1, 1))
else col1
end padded_col1
from my_data;
col1
padded_col1
GP2
GP02
MU1
MU01
FR10
FR10
A regexp_replace():
with tests(example) as (values
('ab02'),('ab1'),('A'),('1'),('A1'),('123'),('ABC'),('abc0'),('a123'),('abcd0123'),('1a'),('a1a'),('1a1') )
select example,
regexp_replace(
example,
'^(\D{0,4})(\d{0,4})$',
'\1' || repeat('0',4-length(example)) || '\2' )
from tests;
example | regexp_replace
----------+----------------
ab02 | ab02
ab1 | ab01
A | A000
1 | 0001
A1 | A001
123 | 0123
ABC | ABC0
abc0 | abc0
a123 | a123
abcd0123 | abcd0123 --caught, repeat('0',-4) is same as repeat('0',0), so nothing
1a | 1a --doesn't start with non-digits
a1a | a1a --doesn't end with digits
1a1 | 1a1 --doesn't start with non-digits
catches non-digits with a \D at the start of the string ^
catches digits with a \d at the end $
specifies that it's looking for 0 to 4 occurences of each {0,4}
referencing each hit enclosed in consecutive parentheses () with a backreference \1 and \2.
filling the space between them with a repeat() up to the total length of 4.
It's good to consider additional test cases.
Thank you all for your response. I think i did something similar as Isolated.
Here is what I've done ("acronym" is the name of the column and "destination" is the name of the table) :
SELECT CONCAT(LEFT(acronym, 2), LPAD(RIGHT(acronym, LENGTH(acronym) - 2), 2, '0')) AS acronym
FROM destination
ORDER BY acronym;
Thanks !

Extract string before certain character or without that character present

I am using SQL Server 2016 and I am trying to extract the first set of numbers of a certain string. Here are some examples
12345
123456
12345-ks
12345-12
123456-ks
I want:
12345
123456
12345
12345
123456
I have tried SUBSTRING(#str, 0, charindex('-', str#, 0). But that excludes any strings without '-'
I have also tried using a case statement to include those strings. But, I can't group by a case statement. Any thoughts?
I've used a case statement to return the whole value when the column does not constain -.
create table strings ( s varchar(10));
insert into strings values
('12345'),
('123456'),
('12345-ks'),
('12345-12'),
('123456-ks');
select
case when charindex('-',s,0) = 0
then s
else SUBSTRING(s,0,
charindex('-', s, 0)
)
end as first_group
from strings;
GO
ci | first_group
-: | :----------
0 | 12345
0 | 123456
6 | 12345
6 | 12345
7 | 123456
db<>fiddle here

PostgreSQL search lists of substrings in string column

I have the following table in a postreSQL database (simplified for clarity):
| serverdate | name | value
|-------------------------------------
0 | 2019-12-01 | A LOC 123 DISP | 1
1 | 2019-12-01 | B LOC 456 DISP | 2
2 | 2019-12-01 | C LOC 777 DISP | 0
3 | 2019-12-01 | D LOC 000 DISP | 10
4 | 2019-12-01 | A LOC 700 DISP | 123
5 | 2019-12-01 | F LOC 777 DISP | 8
name columns is of type string. The substrings LOC and DISP can have other values of different lengths but are not of interest in this question.
The problem: I want to SELECT the rows that only contain a certain substring. There are several substrings, passed as an ARRAY, in the following format:
['A_123', 'F_777'] # this is an example only
I would want to select all the rows that contain the first part of the substring (sepparating it by the underscore '_'), as well as the second. In this example, with the mentioned array, I should obtain rows 0 and 5 (as these are the only ones with exact matches in both parts of the):
| serverdate | name | value
|-------------------------------------
0 | 2019-12-01 | A LOC 123 DISP | 1
5 | 2019-12-01 | F LOC 777 DISP | 8
Row 4 has the first part of the substring correct, but not the other one, so it shouldn't be returned. Same thing with row 2 (only second part matches).
How could this query be done? I'm relatively new to SQL.
This query is part of process in Python, so I can adjust the input parameter (the substring array) if needed, but the behaviour must be the same as the one described.
Thanks!
Have you tried with regexp_replace and a subquery?
SELECT * FROM
(SELECT serverdate, substring(name from 1 for 1)||'_'||
regexp_replace(name, '\D*', '', 'g') AS name, value
FROM t) j
WHERE name IN('A_123', 'F_777');
Or using a CTE
WITH j AS (
SELECT serverdate, substring(name from 1 for 1)||'_'||
regexp_replace(name, '\D*', '', 'g') AS name2,
value,name
FROM t
) SELECT serverdate,name,value FROM j
WHERE name2 IN('A_123', 'F_777');
serverdate | name | value
------------+----------------+-------
2019-12-01 | A LOC 123 DISP | 1
2019-12-01 | F LOC 777 DISP | 8
(2 Zeilen)
Just unnest the array and join the table using a like clause
select
*
from
Table1
join
(
select
'%'||replace(unnest, '_', '%')||'%' pat
from
unnest(array['A_123', 'F_777'])
) pat_table on "name" like "pat"
Just replace unnest(array['A_123', 'F_777']) with unnest(string_to_array(str_variable, ','))
Thanks for your answers! Solution by Larry B got me an error, but it was caused by external factors (I run the queries using an internal tool developed by my company and it threw errors when using the % wildcard. Strange behaviour, I already contacted support team), so I could not test it properly.
Solution by Jim Jones seemed an alternative, but I found that, in some cases, the values in the name field would look like these (didn't notice it when writing the question, as it a rare case):
ABC LOC 123 DISP
So I modified the solution a little bit so as to grab the first part of the name when splitting it by the ' ' character.
(TLDR: 1st substring of name could be of arbitrary length, but is always at the start)
My solution is this one:
SELECT * FROM
(SELECT serverdate, split_part(name, ' ', 1)||'_'||
regexp_replace(name, '\D*', '', 'g') AS name, value
FROM t) j
WHERE name IN('A_123', 'F_777');
split_part(name,'_',1) + '_' + split_part(name,'_',3) as name
this is the break down of the query: A + _ + 123 = A_123

SQL "substr" combine with "not in"

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!!

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