MySQL - How to use JSON_EXTRACT to get values between two indices - mysql

I am trying to extract values between 2 indices in a JSON array using mysql JSON_EXTRACT.
SELECT JSON_EXTRACT('[10, 20, 30, 40,50, 60]', '$[1]');
This query will smoothly return 20 as result. But if I want to get all the numbers between, say, 1st and 3rd indices, how do I query it?
I was expecting something like:
SELECT JSON_EXTRACT('[10, 20, 30, 40,50, 60]', '$[1]..$[3]'); // Not the proper syntax
which will return 20,30,40. But not working.
How do I achieve this?

SELECT JSON_EXTRACT('[10, 20, 30, 40,50, 60]', CONCAT('$[', idx, ']'))
FROM ( SELECT 1 idx UNION SELECT 2 UNION SELECT 3 ) src;
Of course the indices range can be provided as list/range and converted to rowset in CTE/subquery.

Related

SQL : I want to compare 10 consecutive value

I am trying to solve a problem with SQL.
I have a SQL database which has a table named "data", in this table you have a row named "points". There is like 10000 float values in this row.
I want to make a desktop application that can compare 10 consecutive values ( which i manually enter ) to his nearest 10 consecutive values in the database.
exemple :
i want to compare this list of 10 values ( that i enter ):
10.1 , 25.4, 2, 35, 45, 78.9, 41.1, 44, 1, 65
to the best list of 10 values in my database where the 10 values are the nearest to my 10 entered values ( IMPORTANT : VALUES HAVE TO BE CONSECUTIVE ).
You can see below what i want to do, i want to get the list of the 10 consecutive values that is the nearest to the 10 values i want to compare.
points ( 10000 values... )
points row : 10, 15.5, 14.3, 2, 1, 10.2, 55, 65.3, 41, 10, 25.2, 3, 34, 44, 78.8, 41.2, 41, 2, 66, 44, 25.1, 33.2, 45, 75, 98, 12, 11.2 etc etc
The 10 values in bold are the best nearest consecutive values:
10 is near to 10.1
25.2 is near to 25.4
3 is near to 2
34 is near to 35
44 is near to 45
78.8 is near to 78.9
41.2 is near to 41.1
41 is near to 44
2 is near to 1
66 is near to 65
Is there any way to do this with SQL Command ?
Thanks in advance.
One option uses a row-limiting correlated subquery:
select v.val,
(select val from mytable t order by abs(t.val - v.val) limit 1) as match_val
from (
select 10.1 as val
union all select 25.4
union all ...
) v
Basically the subquery executes for each row in the from clause (which is where you put the list of values): it scans the table, orders rows by the difference with the original value, and brings the value of top row only.
SQL tables represent unordered sets. You need a column to specify the ordering.
You can use lag() or lead() to bring 10 values together. Then you need a definition of closest. One possibility is to take the absolute value of the differences and add them up:
select t.*
from (select t.*,
lead(val, 1) over (order by <ordercol>) as val_2,
lead(val, 2) over (order by <ordercol>) as val_3,
. . .
lead(val, 9) over (order by <ordercol>) as val_10
from t
) t
order by abs(val - $val_1) + abs(val_2 - $val_2) + . . .
limit 1;
The $val_1, $val_2, and so on represent the values that you are passing in.
The rest is just sorting and taking a limit.

How to split string with square brackets and commas to columns and rows in mysql

I want to split strings like: [[6, 10, 11, 16], [0.4444444444444445, 53.0, 7.555555555555555, 5.111111111111111]]
to output
id value
---------------------------
6 0.4444444444444445
10 53.0
11 7.555555555555555
16 5.111111111111111
Number of 'ids' in the string varies but there is always value for an 'id'.
This is possible using a stored procedure which chops up the string, creates a (temporary) table, stores the result in said table and returns the table content using a SELECT statement.
However it is unlikely that this is an efficient approach - it will be time consuming to write and execution time will not be good, creating a bottle neck if you pass it a lot of data or if is called repeatedly.
A simpler approach - and the route I would follow - is to do this in your favorite programming language and then store the result in the database.
Alternatively the output of your code could be a query with UNION which would return the desired data structure. Essentially the task is to transpose a two dimensional array and structure the output. If it is not overly large, this would be fast and efficient and could be used as a subquery if you need to combine it with other data.
To give you and idea, the output could be as follows:
SELECT 6 AS id, 0.4444444444444445 AS value
UNION ALL SELECT 10, 53.0
UNION ALL SELECT 11,7.555555555555555
UNION ALL SELECT 16, 5.111111111111111
Put to practical use:
SELECT
sub.id,
sub.value,
info.someFieldFromAnotherTable
FROM
(SELECT 6 AS id, 0.4444444444444445 AS value
UNION ALL SELECT 10, 53.0
UNION ALL SELECT 11,7.555555555555555
UNION ALL SELECT 16, 5.111111111111111
) AS sub
INNER JOIN otherInfoTable info ON info.id = sub.id

How to properly format overlapping mySQL IN and NOT IN conditions

I have the following mySQL table:
data
1
2
3
4
5
6
7
8
9
I would like to supply my select statement with two seperate lists
Exculde List:
1,4,5,7
Include List:
1,2,3,4,5,6,7
I tried the following statement:
Select * FROM table WHERE data NOT IN ('1,4,5,7') AND data IN ('1,2,3,4,5,6,7)
Expecting the following output:
data
2
3
6
But I received no results. I realize I passed an impossible condition but I don't know how to format my query to return the expected results.
Can anyone tell me what I'm doing wrong here?
IN takes a list of values, not a string that holds a delimited list of values.
Examples:
x IN (1, 2, 3)
x IN ('a', 'b', 'c')
Use IN (1,2,3) and not IN ('1,2,3') as the former compares to individual values 1, 2 and 3 while the latter is against the literal string 1,2,3.
Select * FROM ( (Select * FROM table WHERE data NOT IN ('1,4,5,7') ) AS table WHERE data IN ('1,2,3,4,5,6,7)
you try againt

Convert char to date in mysql query

I am having a table in phpmyadmin named transaction_details where i am having a column named tr_date whose datatype is char(10) and a sample value is '21-02-2016'.
I want to convert that char to date using the following query ---
SELECT str_to_date('SUBSTR( tr_date, 1, 2 ),SUBSTR( tr_date, 4, 2 ),SUBSTR(tr_date, 7, 4 )','%d,%m,%Y') FROM transaction_details where tr_id=3;
But when i execute the query, it returned me NULL. Kindly help me to resolve the issue.
I doubt you need those single quotes. This might be what you are trying to do:
SELECT str_to_date(CONCAT_WS(',' SUBSTR(tr_date, 1, 2 ),
SUBSTR(tr_date, 4, 2 ),
SUBSTR(tr_date, 7, 4 )
), '%d,%m,%Y')
FROM transaction_details
WHERE tr_id = 3;
But really, the string operations are unnecessary. This is the more reasonable solution:
SELECT str_to_date(tr_date, '%d-%m-%Y')
FROM transaction_details
WHERE tr_id = 3;

SQL, build a query using data provided in the query itself

For experimental purposes only.
I would like to build a query but not querying data extracted for any table but querying data provided in the query it self. Like:
select numbers.* from (1, 2, 3) as numbers;
or
select numbers.* from (field1 = 1, field2 = 2, field3 = 3) as numbers;
so I can do things like
select
numbers.*
from (field1 = 1, field2 = 2, field3 = 3) as numbers
where numbers.field1 > 1;
If the solution is specific for a database engine could be interesting too.
If you wanted the values to be on separate rows instead of three fields of the same row, the method is the same, just one row per value linked with a union all.
select *
from(
select 1 as FieldName union all
select 2 union all
select 3 union all
select 4 union all -- we could continue this for a long time
select 5 -- the end
) as x;
select numbers.*
from(
select 1 ,2, 3
union select 3, 4, 5
union select 6, 7, 8
union select 9, 10, 11 -- we could continue this for a long time
union select 12, 13, 14 -- the end
) as numbers;
This works with MySQL and Postgres (and most others as well).
[Edit] Use union all rather than just union as you do not need to remove duplicates from a list of constants. Give the field(s) in the first select a meaningful name. Otherwise, you can't specify a specific field later on: where x.FieldName = 3.
If you don't provide meaningful names for the fields (as in the second example), the system (at least MySQL where this was tested) will assign the name "1" for the first field, "2" as the second and so on. So, if you want to specify one of the fields, you have to write expressions like this:
where numbers.1 = 3
Use the values row constructor:
select *
from (values (1),(2),(3)) as numbers(nr);
or using a CTE.
with numbers (nr) as (
values (1),(2),(3)
)
select *
from numbers
where nr > 2;
Edit: I just noticed that you also taggeg your question with mysql: the above will not work with MySQL, only with Postgres (and a few other DBMS)
You can use a subquery without table like so:
SELECT
numbers.*
FROM (
SELECT
1 AS a,
2 AS b,
3 AS c
UNION
SELECT
4,
5,
6
) AS numbers
WHERE
numbers.a > 1
If you like queries to always have a table referenced there is a Psuedo table that always has 1 row and no columns called DUAL, you can use it like so:
SELECT
numbers.*
FROM (
SELECT
1 AS a,
2 AS b,
3 AS c
FROM
DUAL
UNION
SELECT
4,
5,
6
FROM
DUAL
) AS numbers
WHERE
numbers.a > 1