SQL query between strings representing version numbers - sql

I have a SQL query that goes like this:
select v FROM v.rsversion between 'minVer' and 'maxVer';
Where the version is expressed as a string of format x.y.z
This will return fine all existing versions between 0.2.0 and 0.2.9
but will return nothing if the range is 0.2.0 and 0.2.10
Is there a way to make this work?

With Postgres you could do this by splitting up the version into three numbers and then compare those numbers. For other DBMS you would need to find a different way of splitting a string like 0.2.1 into three numbers.
with rsversion (v) as (
values
-- only here for sample data
('0.2.0'), ('0.2.1'), ('0.2.2'), ('0.2.10'), ('0.2.12'),
('0.3.0'), ('0.3.1'), ('0.3.2'), ('0.3.4')
), numeric_version (v, major, minor, patch) as (
select v,
split_part(v,'.', 1)::int,
split_part(v,'.', 2)::int, ,
split_part(v,'.', 3)::int
from rsversion
)
select v
FROM numeric_version
where (major,minor,patch) between (0,2,1) and (0,2,11)
The above prints:
v
------
0.2.1
0.2.2
0.2.10
SQLFiddle example: http://sqlfiddle.com/#!15/403d2/2

The reason this is not working as designed is because 0.2.0 and 0.2.x are not literal numbers, so if you are trying to do a string comparison it's looking at each incremented character and comparing them.
So 0.2.0, 0.2.1, 0.2.10, 0.2.2, 0.2.3, etc is how it's arranging the strings.
You may be able to make this work by adding a leading 0 to the third part of the string 0.2.00, 0.2.01, 0.2.02, etc if that is possible to do for your purposes.

Does this work? I'm assuming your version will always be in a similar format and that your minVer and maxVer are stored as text.
WITH
DATA AS
(
SELECT whatever other data you require,
SUBSTRING(v.rsversion FROM '^[0-9]+.[0-9]+'), '.', '')::decimal as version_part_one,
SUBSTRING(v.rsversion FROM '[0-9]+$')::decimal as version_part_two
FROM v
)
SELECT d.whatever other data you require
FROM data d
WHERE d.version_part_one >= SUBSTRING(minVer FROM '^[0-9]+.[0-9]+')::decimal
AND
d.version_part_two >= SUBSTRING(minVer FROM '.[0-9]+$')::decimal
AND
d.version_part_one <= SUBSTRING(minVer FROM '^[0-9]+.[0-9]+')::decimal
AND
d.version_part_two <= SUBSTRING(maxVer FROM '.[0-9]+$')::decimal
You can change the name of the CTE if you're feeling more creative...

Related

EXTERNAL_QUERY suddenly started to return BYTE value instead of STRING

I'm using Query which joins external data through EXTERNAL_QUERY() LIKE THIS
(this is just example, not actual one)
SELECT
ext.program_id,
SUM(price) AS total_price
FROM a_dataset.purchases pcs
LEFT OUTER JOIN (
SELECT
program_id,
version
FROM EXTERNAL_QUERY(
'CONNECTION_INFO',
'SELECT program_id, version FROM products'
)
) ext ON pcs.program_id = ext.program_id
This query actually worked at my environment.
However, from today, this part ↓
EXTERNAL_QUERY(
'CONNECTION_INFO',
'SELECT program_id, version FROM products'
)
starts to return byte value which looks like encrypted and
turns out to show this message
No matching signature for operator = for argument types: STRING, BYTES. Supported signatures: ANY = ANY at [37:9]
'CONNECTION_INFO' refers Cloud SQL, read replica instance of MySQL.
Do you have any ideas how to fix this, or why these return values started to changed ?

Azure Stream analytics default field values for missing fields

I have some json values coming in from an IOT datasource to stream analytics. They want to change the json in a later version to have extra fields but older versions will not have these fields. Is there a way I can detect the field is missing and set up a default value for it before it gets to the output? for example they would like to add an e.OSversion which if it did not exist would default to "unknown". The output is a sql database as it happens.
WITH MetricsData AS
(
SELECT * FROM [MetricsData]
PARTITION BY LID
WHERE RecordType='UseList'
)
SELECT
e.LID as LID
,e.EventEnqueuedUtcTime AS SubmitDate
,CAST (e.UsedDate as DateTime) AS UsedDate
,e.Version as Version
,caUsedList.ArrayValue.Module AS Module
,caUsedList.ArrayValue.UsageCount AS UsedCount
INTO
[ModuleUseOutput]
FROM
Usagedata as e
CROSS APPLY getElements (e.UsedList) as caUsedList
Please use case..when.. operator.
Example:
select j.id, case when j.version is null then 'unknown' else j.version end as version
from jsoninput as j
Output:
Or you could just set the default value in the sql database column directly.

SQLDF in R - Problems with date format

I am trying apply some transformations in a data.frame using the sqldf function in R, but got some weird outputs. (I tried to apply some SQL date transformations in the query, but got no success).
First the data.frame has the following format (all columns are of character class):
But after filtering the data.frame with 'sqldf':
sqldf("SELECT BP_OR, N_orden_OR, Tipo_ordenOR, N_lineaOR, OLUSER, OLPID,
(select Fecha_aprobac from aprob_or
order by Fecha_aprobac desc limit 1) AS LastOfFecha_aprobac,
Estadp_sig, Estado_ultimo
FROM aprob_or
GROUP BY BP_OR, N_orden_OR, Tipo_ordenOR, N_lineaOR, OLUSER, OLPID, Estadp_sig, Estado_ultimo
(((OLPID)='X43008'))
")
I got the following format for the column LastOfFecha_aprobc
Those 'P5' is what I don't undestand. I formated the SQL code with some parameters to change the date format, but it persisted.
Do you have a better idea to figure that out?

Oracle 10.2.0.4.0 query on partial xpath

I need to change the below query to be able to query any kind of tender item.
/Basket/CardTenderItem/Description
/Basket/CashTenderItem/Description
So
/Basket/WildcardTenderItem/Description
I have looked at various examples on but cannot them to bring back any results when running (happily admit to user error if can get working!)
SELECT
RETURN_ID
,SALE_ID,
,extractValue(xmltype(RETURNxml),'/Basket/CashTenderItem/NetValue')
,extractValue(xmltype(RETURNxml),'/Basket/CashTenderItem/Description')
FROM SPR361
WHERE return_id = '9999.0303|20170327224954|2063'
If you only want to match anything the ends with TenderItem, but doesn't have anything after that, you could be specific with substring checks:
SELECT
RETURN_ID
,SALE_ID
,extractValue(xmltype(RETURNxml),
'/Basket/*[substring(name(), string-length(name()) - 9) = "TenderItem"]/NetValue')
,extractValue(xmltype(RETURNxml),
'/Basket/*[substring(name(), string-length(name()) - 9) = "TenderItem"]/Description')
FROM SPR361
WHERE return_id = '9999.0303|20170327224954|2063'
If you never have any nodes with anything after that fixed string then #Shnugo's contains approach is easier, and in Oracle would be very similar:
...
,extractValue(xmltype(RETURNxml),
'/Basket/*[contains(name(), "TenderItem")]/NetValue')
,extractValue(xmltype(RETURNxml),
'/Basket/*[contains(name(), "TenderItem")]/Description')
I'm not sure there's any real difference between name() and local-name() here.
If a basket can have multiple child nodes (card and cash, or more than one of each) you could also switch to XMLTable syntax:
SELECT
s.RETURN_ID
,s.SALE_ID
,x.netvalue
,x.description
FROM SPR361 s
CROSS JOIN XMLTable(
'/Basket/*[contains(name(), "TenderItem")]'
PASSING XMLType(s.RETURNxml)
COLUMNS netvalue NUMBER PATH './NetValue'
, description VARCHAR(80) PATh './Description'
) x
WHERE s.return_id = '9999.0303|20170327224954|2063'
And it's overkill here maybe, but for more complicated tests you can use other XPath syntax, like:
CROSS JOIN XMLTable(
'for $i in /Basket/*
where contains($i/name(), "TenderItem") return $i'
PASSING XMLType(s.RETURNxml)
...
This is SQL-Server syntax and I cannot test, if this works with Oracle too, but I think it will. You can use XQuery function contains():
DECLARE #xml XML=
N'<root>
<abcTenderItem>test1</abcTenderItem>
<SomeOther>should not show up</SomeOther>
<xyzTenderItem>test2</xyzTenderItem>
</root>';
SELECT #xml.query(N'/root/*[contains(local-name(),"TenderItem")]')
only the elements with "TenderItem" in their names show up:
<abcTenderItem>test1</abcTenderItem>
<xyzTenderItem>test2</xyzTenderItem>

looking for db2 text function or method I can do a text contain rather than like

I'm looking for a db2 function that does a text contain search. At present I am running the following query against the data below....
SELECT distinct
s.search_id,
s.search_heading,
s.search_url
FROM repman.search s, repman.search_tags st
WHERE s.search_id = st.search_id
AND ( UPPER(s.search_heading) LIKE (cast('%REPORT%' AS VARGRAPHIC(32)))
OR (UPPER(st.search_tag) LIKE cast('%REPORT%' AS VARGRAPHIC(32)))
)
ORDER BY s.search_heading;
Which returns...
But if I change the search text to %REPORTS% rather than %REPORT% (which I need to do) the like search does not work and I get zero results.
I read a link that used a function named CONTAINS like below but when trying to use the function I get an error.
SELECT distinct
s.search_id,
s.search_heading,
s.search_url
FROM repman.search s, repman.search_tags st
WHERE s.search_id = st.search_id
AND CONTAINS(s.search_heading, 'REPORTS') = 1
Has anynoe got any suggestions? I'm on db2 version DB2/LINUXPPC 9.1.6.
Thanks
In order to look for a pattern in a string, you can use Regular Expressions. They are built-in DB2 with xQuery since DB2 v9. There are also other ways to do that. I wrote an article in my blog (in Spanish that you can translate) about Regular Expressions in DB2.
xmlcast(xmlquery('fn:matches(\$TEXT,''^[A-Za-z 0-9]*$'')')