Pop the last item in a JSON Array in MySQL 5.7 - mysql

I've got an array of dates in a field called from. It can look something like this.
['2016-05-01', '2016-05-03', '2016-05-04']
I want to SELECT the last item (here 2016-05-04).
I've tried this:
SELECT `from`->"$[JSON_LENGTH(`from`) - 1]" FROM `table` WHERE `id` = 3;
but got that error:
ERROR 3143 (42000): Invalid JSON path expression. The error is around character position 2.
I've tried using a variable like this :
SET #count = (SELECT JSON_LENGTH(`from`) - 1 FROM `table` WHERE `id` = 3);
SELECT `from`->"$[#count]" FROM `table` WHERE `id` = 3;
but got the exact same error. But if I do:
SELECT `from`->"$[2]" FROM `table` WHERE `idx` = 3;
It works fine.

you can use :
SELECT JSON_EXTRACT(`from`,CONCAT("$[",JSON_LENGTH(`from`)-1,"]")) FROM `table`;
to get the last item in a json array.

MySQL 8 brings a very straight forward way to accomplish this:
select json_extract(json_array(1, 2, 3, 4, 5), '$[last]');
which returns
5
Can also do cool ranges, like everything but the last one:
select json_extract(json_array(1, 2, 3, 4, 5), '$[0 to last-1]');
which returns
[1, 2, 3, 4]

Related

Is there a way to make an AND operation over a column of TINYINT(1) in MYSQL?

I got the following table and I need to return 1 if all rows have disponibilidad = 1
The following QUERY works just fine, but i was looking for a more efficient way of doing it.
QUERY:
SELECT IF(AVG(disponibilidad) < 1, 0, 1) AS newResult
FROM pasteleria.compone
RIGHT JOIN pasteleria.ingredientes
ON pasteleria.compone.id_ingrediente = pasteleria.ingredientes.id_ingrediente
WHERE id_componente = 1;
RESULT:
As I see it, with an 'AND' it would be far more efficient, since it wouldn't have to do AVG().
MySql does not support a boolean AND aggregate function like Postgresql's bool_and.
Why not a simple MIN():
SELECT MIN(disponibilidad) AS newResult
FROM pasteleria.compone
RIGHT JOIN pasteleria.ingredientes
ON pasteleria.compone.id_ingrediente = pasteleria.ingredientes.id_ingrediente
WHERE id_componente = 1;
This will return 1 only if all values of the column are 1 (provided the column is not nullable) and 0 if there is at least one row with 0.
How about something like
SELECT IF(COUNT(*)>0,0,1) AS newResult
FROM pasteleria.compone
RIGHT JOIN pasteleria.ingredientes
ON pasteleria.compone.id_ingrediente = pasteleria.ingredientes.id_ingrediente
WHERE id_componente = 1
AND disponibilidad <> 1
so that if there are any rows where disponibilidad is not 1, you output 0, otherwise if it's zero (so all disponibilidad values are 1) you output 1?

Where in with array of values MYSQL

Lets say of I have this query SELECT * FROM table WHERE id IN (1,2,3,4);
In my current use case my ids are in an array like so [1,2,3,4]
How could I do the same query using the array data structure?
i.e. SELECT * FROM table WHERE id IN (myarray);
Edit: this is in ruby :)
Try this:
SELECT * FROM table WHERE id IN (myarray.map {|i| "\"#{i}\""}.join(", "));
You could convert the array to a comma separated string using the myarray.join(',') method.
The final code would look like this:
query = "SELECT * FROM table WHERE id IN (#{myarray.join(',')})"
You might run into some problems if the array values are strings, but it works just fine for integers.
User.where(id: [1, 2, 3, 4, nil]).to_sql
# SELECT "users".* FROM "users"
# WHERE ("users"."id" IN (1, 2, 3, 4) OR "users"."id" IS NULL)
or, if you can't/don't want to use where, you can drop into Arel to get just the IN string:
User.arel_table[:id].in([1,2,3,4]).to_sql
# => "users"."id" IN (1, 2, 3, 4)
though with this, you don't automatically get that nifty nil handling. If you don't have an ActiveRecord Model, but just using ActiveRecord::Base to execute queries in your database (as mentioned in the comments) you can do:
table = Arel::Table.new(:table) # :table is the name of the table in db
table[:id].in([1,2,3,4]).to_sql
# => "table"."id" IN (1, 2, 3, 4)
table.where(table[:id].in([1,2,3,4])).project(Arel.sql('*')).to_sql
# => SELECT * FROM "table" WHERE "table"."id" IN (1, 2, 3, 4)
And, avoiding Arel/ActiveRecord as much as possible, you can just do
ActiveRecord::Base.send(:sanitize_sql, ['id in (?)', [1,2,3,4]])
# => "id in (1,2,3,4)"

MySQL query works as normal SELECT but not inside CREATE TABLE AS..?

I'm trying to figure out why this query SELECTs just fine, but I can't use it to create a temporary table... The IF = '', 0 stuff didn't used to exist in the original query so I added it to make sure that I wasn't trying to sum anything that could potentially be an empty string, but it didn't fix the problem. This select works and returns the correct result set:
SELECT
SUM(((IF(reserve_transactions.Revenue = '',
0,
reserve_transactions.Revenue) * IF(reserve_transactions.Confidence = '',
0,
reserve_transactions.Confidence)) / 100)) AS rawadjusted
FROM
(store_locations
LEFT JOIN reserve_transactions ON ((store_locations.ID = location_sales)))
GROUP BY store_locations.Loc_Long
But this does not:
CREATE TEMPORARY TABLE tmptable_which_doesnt_work AS SELECT
SUM(((IF(reserve_transactions.Revenue = '',
0,
reserve_transactions.Revenue) * IF(reserve_transactions.Confidence = '',
0,
reserve_transactions.Confidence)) / 100)) AS rawadjusted
FROM
(store_locations
LEFT JOIN reserve_transactions ON ((store_locations.ID = location_sales)))
GROUP BY store_locations.Loc_Long
The error is: Error Code: 1292. Truncated incorrect DECIMAL value: ''
I have other queries where the situation is the same, actually. Sometimes with other truncated incorrect types. What am I missing here?
I think that the problem could be data and the logic of selecting only the numeric entries - You could try to define a functions as follows:
CREATE FUNCTION IsNumeric (sIn varchar(1024))
RETURNS tinyint
RETURN sIn REGEXP '^(-|\\+){0,1}([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+|[0-9]+)$';
and apply it in the where clause for the two arguments, eventually trim unnecessary white spaces (as per How do I check to see if a value is an integer in MySQL? )

how to select from mysql table from matlab variable (array/cell)

I have a table like this
The table in mysql
and I have a variable x in matlab
x=[1 5 6 8 10 21 99];
now I want to select like this
select * from tablename where key1 = x
I know mysql query must be string, and my variable x in matlab may be too long.
So how to do this in matlab? I failed in searching. Thanks
conn = database('instancename','username','password');
I know I can do this like
sql = 'select * from tablename where key in (1,5,6,8,10,21,99)'
The question is my x isn't constant and sometime could be a 1*N cell ( whose element is char), I want to put it into a script.
In SQL Server you write your query like this:
select * from tablename where key1 in {1, 5, 6, 8, 10, 21, 99}
I don't know about MY SQL but I imagine it is very similar. Which means all you need to do is convert x to the string '1, 5, 6, 8, 10, 21, 99' which you could do like this:
x_str = strjoin(cellstr(num2str(x'))',',')
and now the whole query becomes
query = sprintf('SELECT * FROM tablename WHERE key1 IN {%s}', x_str);
As an aside, another way to create x_str could be:
x_str = sprinft('%d, ', x);
x_str(end) = [];

How can I combine ANDs and ORs in my SQL statement

I have this SQL statement:
SELECT * FROM `table` WHERE type = 3 OR type = 5 OR type = 4 and table.deleted = 1;
I've read that I can use parenthesis to accomplish this but I was wondering if then this would be valid:
SELECT * FROM `table` WHERE (type = 3 OR type = 5 OR type = 4) and table.deleted = 1;
OR
SELECT * FROM `table` WHERE (type = 3 OR type = 5) OR type = 4 and table.deleted = 1;
Both of these would be valid, but since AND has higher precedence than OR, they would mean different things:
Your first parenthesized query would pick deleted rows with types 3, 4, 5
Your second parenthesized query would select all rows with types 3, 5, in addition to deleted rows of type 4; this is the same meaning as in the original query without parentheses.
You can avoid the confusion altogether by using operator IN, like this:
SELECT * FROM `table` WHERE type IN (3, 4, 5) AND table.deleted = 1;
or if you wanted the second meaning
SELECT * FROM `table` WHERE type IN (3, 5) OR (type = 4 AND table.deleted = 1)
What you need is IN operator like
SELECT * FROM `table`
WHERE type IN ( 3, 5, 4) and deleted = 1;
AND has higher precedence than OR, so your first and third filters are equivalent to:
type = 3 OR type = 5 OR (type = 4 and table.deleted = 1)
Your second filter could equivalently be expressed using IN():
type IN (3, 5, 4) and table.deleted = 1