Index like sql order - sql

I have a column with a string value, something like 1, 1.1, 1.1.2, 1.2, 2, 2.1, 1.3, 1.1.3, one for record, of course, and i want a sentence that returns the records ordered by this field, like a book index
1
1.1
1.1.2
1.1.3
1.2
1.3
2
2.1
Thanks

Use ORDER BY:
CREATE TABLE #tab(col VARCHAR(1000));
INSERT INTO #tab(col)
SELECT '1'
UNION ALL SELECT '1.1'
UNION ALL SELECT '1.1.2'
UNION ALL SELECT '1.1.3'
UNION ALL SELECT '1.2'
UNION ALL SELECT '1.3'
UNION ALL SELECT '2'
UNION ALL SELECT '2.1';
SELECT *
FROM #tab
ORDER BY col;
LiveDemo
EDIT:
Just for fun and experiment solution for SQL Server 2012+:
WITH cte AS (
SELECT col,
CASE LEN(col) - LEN(REPLACE(col, '.', ''))
WHEN 0 THEN col + '.0.0.0'
WHEN 1 THEN col + '.0.0'
WHEN 2 THEN col + '.0'
ELSE col
END AS col_alt
FROM #tab
)
SELECT col
FROM cte
ORDER BY
LEN(PARSENAME(col_alt,4)),
PARSENAME(col_alt,4),
LEN(PARSENAME(col_alt,3)),
PARSENAME(col_alt,3),
LEN(PARSENAME(col_alt,2)),
PARSENAME(col_alt,2),
LEN(PARSENAME(col_alt,1)),
PARSENAME(col_alt,1);
LiveDemo2

If the values between the dots are all single characters (as in the question), then the easiest way is to order by the length of the string and then the string:
order by len(col), col
(In some databases, len might be spelled length.)
Note: this only works when single digits separate the dots. A more general solution requires some knowledge of the database.

Related

Check if all characters are 'X'

I have the below table:
COL
---
XXY
YXX
XXX
NULL
I want to filter out the rows which don't consist of all 'X's.
Expected output:
COL
---
XXX
We can use REGEXP_LIKE here:
SELECT COL
FROM yourTable
WHERE REGEXP_LIKE(COL, '^X+$'); -- ^X+$ means all X from start to end
Another similar version:
SELECT COL
FROM yourTable
WHERE NOT REGEXP_LIKE(COL, '[^X]'); -- this means no non X present
Another option(without using a regular expression) might be using
WITH t(col) AS
(
SELECT 'XXY' FROM dual UNION ALL
SELECT 'YXX' FROM dual UNION ALL
SELECT 'XXX' FROM dual UNION ALL
SELECT NULL FROM dual UNION ALL
SELECT 'XX ' FROM dual
)
SELECT *
FROM t
WHERE REPLACE(NVL(col,'Y'),'X') IS NULL;
COL
----
XXX
without forgetting the case col = NULL through use of a NVL()
You can use the following syntax (assuming you are using MySQL database 5.6 or greater version):
SELECT * FROM table_name WHERE col_name REGEXP '^X+$';
If you don't want/have regexp then:
WITH
tbl AS
( Select 'XXY' "COL" From dual Union All
Select 'YXX' "COL" From dual Union All
Select 'XXX' "COL" From dual Union All
Select null "COL" From dual
)
Select COL
From tbl
Where Length(Nvl(COL, 'Z')) - Length( Replace( Upper(Nvl(COL, 'Z')), 'X', '')) Is Null
COL
---
XXX
This covers both small 'x' and capital 'X' if needed and returns original COL value

Is there a BigQuery version of isnumeric

I need to test if a field is numeric or not using standard SQL in BigQuery.
The example below works and is similar to what I have done in Cognos using TRANSLATE('mystring','1234567890.','') but its not very elegant.
SELECT
IF(LENGTH(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE('1234.56','1',''),'2',''),'3',''),'4',''),'5',''),'6',''),'7',''),'8',''),'9',''),'0',''),'.',''))=0,
'A number',
'Not a number')
You can use SAFE_CAST to try casting to a number. SAFE_CAST casts similar to CAST, but if casting fails, instead of erring null is returned.
For example you can do:
SAFE_CAST('1234567890' AS FLOAT64);
which will return 1.23456789E9
Thanks for both suggestions, both work a treat and I have gone for the SAFE_CAST option as it runs a fraction quicker.
#standardSQL
WITH `project.dataset.table` AS (
SELECT '1234.56' col UNION ALL
SELECT '1234.' col UNION ALL
SELECT '1234' col UNION ALL
SELECT '.56' col UNION ALL
SELECT '1234..56' col UNION ALL
SELECT 'a1234.56'
)
SELECT
col,
if(SAFE_CAST(col AS FLOAT64) is null,'Not a number', 'A number')
FROM `project.dataset.table`
but its not very elegant
Below examples for BigQuery Standard SQL
#standardSQL
WITH `project.dataset.table` AS (
SELECT '1234.56' col UNION ALL
SELECT '1234.' col UNION ALL
SELECT '1234' col UNION ALL
SELECT '.56' col UNION ALL
SELECT '1234..56' col UNION ALL
SELECT 'a1234.56'
)
SELECT
col,
IF(LENGTH(REGEXP_REPLACE(col, r'[\d.]', '')) = 0, 'A number', 'Not a number') ,
IF(REGEXP_CONTAINS(col, r'^\d*.?\d*$'), 'A number', 'Not a number')
FROM `project.dataset.table`
I think that we could use translate function to replace digits from 0 to 9
by 0 (let's say string_1 ) and then compare it to a string (let's say String_2) that equals to as much of 0 as then lengh of String_1.
(translate(src.NUM_BU , '0123456789', '0000000000'))
=
rpad('', length((translate(src.NUM_BU , '0123456789', '0000000000'))), '0')

In SQL sort by Alphabets first then by Numbers

In H2 Database when i have applied order by on varchar column Numbers are coming first then Alphabets. But need to come Alphabets first then Numbers.
I have tried with
ORDER BY IF(name RLIKE '^[a-z]', 1, 2), name
but getting error like If condition is not available in H2.
My Column Data is Like
A
1-A
3
M
2-B
5
B-2
it should come like
A
B-2
M
1-A
2-B
3
5
try this out
SELECT MYCOLUMN FROM MYTABLE ORDER BY REGEXP_REPLACE (MYCOLUMN,'(*)(\d)(*)','}\2') , MYCOLUMN
One thing can be done is by altering the ASCII in order by clause.
WITH tab
AS (SELECT 'A' col FROM DUAL
UNION ALL
SELECT '1-A' FROM DUAL
UNION ALL
SELECT '3' FROM DUAL
UNION ALL
SELECT 'M' FROM DUAL
UNION ALL
SELECT '2-B' FROM DUAL
UNION ALL
SELECT '5' FROM DUAL
UNION ALL
SELECT 'B-2' FROM DUAL)
SELECT col
FROM tab
ORDER BY CASE WHEN SUBSTR (col, 1, 1) < CHR (58) THEN CHR (177) || col ELSE col END;
I have Used CHR(58) as ASCII value of numbers end at 57. and CHR(177) is used as this is the maximum in the ASCII table.
FYR : ASCII table
Given the example dataset, I'm not sure if you need further logic than this- so I'll refrain from making further assumptions:
DECLARE #temp TABLE (myval char(3))
INSERT INTO #temp VALUES
('A'), ('1-A'), ('3'), ('M'), ('2-B'), ('5'), ('B-2')
SELECT myval
FROM #temp
ORDER BY CASE WHEN LEFT(myval, 1) LIKE '[a-Z]'
THEN 1
ELSE 2
END
,LEFT(myval, 1)
Gives output:
myval
A
B-2
M
1-A
2-B
3
5

Filter records based on numeric value when column type is varchar

I have a column in a table with datatype varchar that stores version numbers.I need to filter and select only version numbers that are less than equal to 5
Input
5.0.0.330
Eclair
5.0.0
5.0.0.591
5.0.0.405
6.0.0.522
4.0.2
7.1.0.205
5.0.0.592
2.3.4-ez
4.2.2-2013-12-11-V1.0
4.6.0.304
nubernel-2.6.35_v0.0.1
2.1-update1
2.3
Output
5.0.0
4.0.2
2.3.4-ez
4.2.2-2013-12-11-V1.0
4.6.0.304
2.1-update1
2.3
I can get all the versions less than 5 by converting the first character of the varchar column.However I can't figure out how to include the 5.0.0 version in the result set.
select distinct os_ver,substring(os_ver,1,1)
from
dbo.mytable
where
os_ver like '[0-9]%' and cast (substring(os_ver,1,1) as int) < 5
This gives me all version less than 5 except the version 5.0.0
4.0.2
2.3.4-ez
4.2.2-2013-12-11-V1.0
4.6.0.304
2.1-update1
2.3
Select *
From dbo.mytable
Where os_ver<='5.0.0'
Returns
os_ver
5.0.0
4.0.2
2.3.4-ez
4.2.2-2013-12-11-V1.0
4.6.0.304
2.1-update1
2.3
Try this condition.
select * from (
select '5.0.0.330' as a union
select 'Eclair' union
select '5.0.0' union
select '5.0.0.591' union
select '5.0.0.405' union
select '6.0.0.522' union
select '4.0.2' union
select '7.1.0.205' union
select '5.0.0.592' union
select '2.3.4-ez' union
select '4.2.2-2013-12-11-V1.0' union
select '4.6.0.304' union
select 'nubernel-2.6.35_v0.0.1' union
select '2.1-update1' union
select '2.3') b
where a <= '5.0.0' and ISNUMERIC(SUBSTRING(a, 1, 1)) = 1

Order versions as numbers

I have a table with file-names and version with subversion of files separated by ..
FNAME, VERSION
A 0.0.10
B 10.12.412
-- For example
create table file_versions as
select chr(mod(level,13)+65) as fname
, decode(mod(level,99),0, '0',
mod(level,10)||'.'||mod(level,500)||'.'||mod(level,14)
)
as version
from dual connect by level < 1001;
I'd like to order files by version, but use versions as numbers
select fname, version from file_versions
order by fname, version
FNAME, VERSION
A 0.0.10
A 0.0.6
...
I'd like don't think about subversion level(there may be one number (0) or more(1.23.14123)). How should I write order by statement ?
I may write something like:
select fname, version from file_versions
order by fname
, to_number(substr(version, 1, instr(version, '.',1,1)-1))
, to_number(substr(version, instr(version, '.',1,1)+1, instr(version, '.',1,2)-instr(version, '.',1,1)-1))
, to_number(substr(version, instr(version, '.',1,2)+1))
But its not so good and will not work if one digit was added to the version string (e.g. 0.0.0.123). Is there a better solution?
You can use regexp_substr():
order by fname,
cast(regexp_substr(version, '[^.]+', 1, 1) as number),
cast(regexp_substr(version, '[^.]+', 1, 2) as number),
cast(regexp_substr(version, '[^.]+', 1, 3) as number)
You may use two regexp first for enhance you group to add 5 zeros to any group. And another one to take last 5 digits from each group. And you get constant length rows and be able to sort it as chars.
with s(txt) as (select '1' from dual
union all
select '1.12' from dual
union all
select '1.12.410' from dual
union all
select rpad('1.12.410',401,'.03') from dual
union all
select rpad('1.12.410',401,'.03')||'.01' from dual
union all
select rpad('1.12.410',401,'.03')||'.02' from dual
)
select txt,regexp_replace(regexp_replace(txt, '(\d+)','00000\1'),'\d+ (\d{5})','\1') from s
order by regexp_replace(regexp_replace(txt, '(\d+)','00000\1'),'\d+(\d{5})','\1')
It will work up to 99999 version or subversion.
More for fun than as a serious suggestion, here's an alternative to parsing the string -- treating the version numbers as inet addresses.
Trickier when you have three levels in your version, but trivial for four levels:
Starting with the idea of:
select a.i::varchar
from (select '192.168.100.128'::inet i union
select '22.168.100.128'::inet) a
order by 1;
i
--------------------
192.168.100.128/32
22.168.100.128/32
(2 rows)
So for three-level versions you can:
with
versions as (
select '1.12.1' v union
select '1.3.100'),
inets as (
select (v||'.0')::inet i
from versions)
select substr(i::varchar,1,length(i::varchar)-5)
from inets
order by i;
substr
---------
1.3.100
1.12.1
(2 rows)
Maybe everyone should have four level versions ...