问题
The sqlite table consists of attributes :
|Versions (TEXT)|
| "2.73.8" |
| "3.6.4 " |
| "3.9.11" |
and so on..
I want to retrieve all the versions from the table between two versions given in the query. For instance: Between versions- 2.9.10 & 3.7.10 .
I could not find any sqlite function to query this directly. I used Substring (SUBSTR) to split to get individual digits which could then be compared to the one present in the table. I was successful in doing that but I could find a way to query to retrieve all versions between two version set.
create table prod(version varchar);
insert into prod values('2.7.5');
insert into prod values('2.7.4');
insert into prod values('2.0.0');
insert into prod values('22.73.55');
insert into prod values('22.17.54');
insert into prod values('22.10.06');
insert into prod values('3.7.5');
insert into prod values('3.4.5');
insert into prod values('3.7.6');
Query to retrieve all versions below or equal to : "3.50.6" (using nested "case when" ):
SELECT * from prod
Where version IN ( SELECT
CASE WHEN (CAST(substr(version,0,instr(version,'.')) as integer)=3)
THEN
CASE WHEN (cast(SUBSTR(SUBSTR(version, INSTR(version, '.')),1,INSTR(SUBSTR(version, INSTR(version, '.') + 1), '.') - 1) as float)< 0.50 )
THEN
version
ELSE
CASE WHEN (cast(SUBSTR(SUBSTR(version, INSTR(version, '.')),1,INSTR(SUBSTR(version, INSTR(version, '.') + 1), '.') - 1) as float)=0.50)
THEN
CASE WHEN (CAST(replace(version, rtrim(version, replace(version, '.', '')), '')AS INTEGER)<=6)
THEN version
END END END END FROM prod);
Kindly provide me a way to query to retrieve all versions in the table between two sets of versions.
回答1:
I believe the following will do as you want :-
WITH
/* SELECTION parameters */
parm(p1,p2) AS (SELECT '3.4.5', '22.10.6' /*<<<<<<<<<<<<< Versions to check against lower first*/),
/* Parse 1, 1st value of each and the rest for parse2 */
pv1(parmv1,rest4p2,parm2v1,rest4p22) AS (
SELECT
CAST(substr(p1,1,instr(p1,'.')-1) AS INTEGER),
substr(p1,instr(p1,'.')+1),
CAST(substr(p2,1,instr(p2,'.')-1) AS INTEGER),
substr(p2,instr(p2,'.')+1)
FROM parm
),
/* Parse 2 extra 2 values retrieved for each parameter */
pv2(parmv2,parmv3,parm2v2,parm2v3) AS (
SELECT
CAST(substr(rest4p2,1,instr(rest4p2,'.')-1) AS INTEGER),
CAST(substr(rest4p2,instr(rest4p2,'.')+1) AS INTEGER),
CAST(substr(rest4p22,1,instr(rest4p22,'.')-1) AS INTEGER),
CAST(substr(rest4p22,instr(rest4p22,'.')+1) AS INTEGER)
FROM pv1),
/* calculate the lower and upper values to be checked against for the BETWEEN clause */
finalp(lower,higher) AS (
SELECT
(parmv1 * 1000 * 1000) + (SELECT (parmv2 * 1000) + parmv3 FROM pv2),
(parm2v1 * 1000 * 1000) + (SELECT (parm2v2 * 1000) + parm2v3 FROM pv2)
FROM pv1),
/* Parse 1 for the actual data gets the 1st part of the version and the rest of the version */
v1(v1rowid,vpart1,rest4v2) AS (SELECT rowid, CAST(substr(version,1,instr(version,'.')-1) AS INTEGER),substr(version,instr(version,'.')+1) FROM prod),
/* Parse 2 for the actual data gets the 2nd and third parts */
v2(v2rowid,vpart2,vpart3) AS (SELECT v1rowid, CAST(substr(rest4v2,1,instr(rest4v2,'.')+1) AS INTEGER),CAST(substr(rest4v2,instr(rest4v2,'.')+1) AS INTEGER) FROM v1)
SELECT version
FROM v1
JOIN v2 ON v1rowid = v2rowid /* join the 2nd and third parts with the 1st */
JOIN prod ON prod.rowid = v1rowid /* also join the original data for the original version */
JOIN finalp ON 1 = 1 /* joint the upper and lower values */
WHERE
(vpart1 * 1000 * 1000) + (vpart2 * 1000) + vpart3 /* do the same calculation used for the upper and lower parameters */
BETWEEN lower AND higher
;
The above results in :-
and using :-
.... (SELECT '3.4.5', '22.10.06' /*<<<<<<<<<<<<< Versions to check against lower first*/) ...
as well as :-
.... (SELECT '3.4.5', '22.10.6' /*<<<<<<<<<<<<< Versions to check against lower first*/) ....
- .6
instead of .06
(i.e. leading 0's are ignored)
Results in :-
- This also checks boundary hits i.e. 3.4.5 and 20.10.06 were selected.
回答2:
I assume that each one of the 3 parts of the version value is max 3 digits.
The simplest way to convert a version value to a number so to make it comparable, is : multiply the 1st part by 1000000, the 2nd part by 1000 and then add them plus the 3d part.
In code:
1000000 * replace(version, '.', 'x') +
1000 * replace(substr(version, instr(version, '.') + 1), '.', 'x') +
replace(version, '.', '000') % 1000 number
If you execute:
select
version,
1000000 * replace(version, '.', 'x') +
1000 * replace(substr(version, instr(version, '.') + 1), '.', 'x') +
replace(version, '.', '000') % 1000 numericversion
from prod
you get:
| version | numericversion |
| -------- | -------------- |
| 2.7.5 | 2007005 |
| 2.7.4 | 2007004 |
| 2.0.0 | 2000000 |
| 22.73.55 | 22073055 |
| 22.17.54 | 22017054 |
| 22.10.06 | 22010006 |
| 3.7.5 | 3007005 |
| 3.4.5 | 3004005 |
| 3.7.6 | 3007006 |
So to get all versions between versions- 2.9.10 & 3.7.10, do this:
with
cte1 as (
select
1000000 * replace('2.9.10', '.', 'x') +
1000 * replace(substr('2.9.10', instr('2.9.10', '.') + 1), '.', 'x') +
replace('2.9.10', '.', '000') % 1000 numericversion
),
cte2 as (
select
1000000 * replace('3.7.10', '.', 'x') +
1000 * replace(substr('3.7.10', instr('3.7.10', '.') + 1), '.', 'x') +
replace('3.7.10', '.', '000') % 1000 numericversion
),
versions as (
select
version,
1000000 * replace(version, '.', 'x') +
1000 * replace(substr(version, instr(version, '.') + 1), '.', 'x') +
replace(version, '.', '000') % 1000 numericversion
from prod
)
select version from versions
where numericversion between
(select numericversion from cte1) and (select numericversion from cte2)
The 1st CTE returns the numeric value of 2.9.10
, the 2nd CTE returns the numeric value of 3.7.10
and the 3d the numeric values of all the versions in the table.
Finally the query compares the numeric versions.
See the demo.
Results:
| version |
| ------- |
| 3.7.5 |
| 3.4.5 |
| 3.7.6 |
Or without the CTEs by hardcoding the 2 versions as numbers:
select version from prod
where
1000000 * replace(version, '.', 'x') +
1000 * replace(substr(version, instr(version, '.') + 1), '.', 'x') +
replace(version, '.', '000') % 1000
between 2009010 and 3007010
See the demo.
Or:
select version from prod
where
1000000 * replace(version, '.', 'x') +
1000 * replace(substr(version, instr(version, '.') + 1), '.', 'x') +
replace(version, '.', '000') % 1000
between
1000000 * replace('2.9.10', '.', 'x') +
1000 * replace(substr('2.9.10', instr('2.9.10', '.') + 1), '.', 'x') +
replace('2.9.10', '.', '000') % 1000
and
1000000 * replace('3.7.10', '.', 'x') +
1000 * replace(substr('3.7.10', instr('3.7.10', '.') + 1), '.', 'x') +
replace('3.7.10', '.', '000') % 1000
See the demo.
来源:https://stackoverflow.com/questions/57049677/sqlite-query-to-retrieve-all-versions-between-two-versions