Create sum of Elements that are not grouped by - mysql

I'm trying to sum up all values from the values column by the timestamp "ts".
What I got so far:
select ts, item, value
from statistics
where indicator = "something"
actual:
"1", "item1", "100"
"1", "item2", "200"
"1", "item3", "300"
"2", "item1", "101"
"2", "item2", "202"
"2", "item3", "303"
expected:
"1", "item1", "100"
"1", "item2", "200"
"1", "item3", "300"
"1", "sum", "600"
"2", "item1", "101"
"2", "item2", "202"
"2", "item3", "303"
"2", "sum", "606"

Use GROUP BY and WITH ROLLUP.
select ts, item, SUM(value)
from statistics
where indicator = "something"
GROUP BY ts, item WITH ROLLUP
You have to group by both columns to get the original rows in the result, then WITH ROLLUP adds the total lines. This will add a row with item = NULL for the ts totals, and a row at the end with both ts = NULL, item = NULL for the grand total.

I think Barmar's solution should be accepted, but just for the sake of giving an alternative solution, I would describe this one:
select ts, item, value
from
(
(
select ts as ts, item, value
from statistics
where indicator = "something"
)
union
(
select ts as ts, 'zzzz_sum' as item, count(*) as value
from statistics
group by ts
)
) t
order by ts, item;
We basically union two result sets and order them accordingly

Related

How to get a specific object in an JSON array in MySQL?

I have a JSON column "jobs" that looks like this:
[
{
"id": "1",
"done": "100",
"target": "100",
"startDate": "123123132",
"lastAction": "123123132",
"status": "0"
},
{
"id": "2",
"done": "10",
"target": "20",
"startDate": "2312321",
"lastAction": "2312321",
"status": "1"
}
]
I want to filter the array by object key values. For example: To find all items that have target > done, status != 0 and lastAction is yesterday to get response like this:
[
{
"id": "1",
"done": "19",
"target": "100",
"startDate": "123123132",
"lastAction": "123123132",
"status": "0"
}
]
I know I can extract the data to a JSON_TABLE() to do the filtering but I don't get the original object back(unless I recreate it back) and the solution is not dynamic.
Can this kind of array filtering can really be done in MySQL?
SELECT JSON_PRETTY(JSON_EXTRACT(jobs.jobs, CONCAT('$[', j.rownum-1, ']'))) AS object
FROM jobs
CROSS JOIN JSON_TABLE(
jobs.jobs, '$[*]' COLUMNS(
rownum for ordinality,
done int path '$.done',
target int path '$.target',
status int path '$.status'
)
) as j
WHERE j.target > j.done AND j.status != 0;
You also mentioned a condition on lastAction, but the example values you gave are not valid dates, so I'll leave that enhancement to you. The example above demonstrates the technique.
Yes it is possible to do it using the JSON_EXTRACT and JSON_SEARCH functions.
Let's say your table is named tbl_Jobs and the jobs column is of type JSON.
SELECT * FROM tbl_Jobs
WHERE JSON_EXTRACT(jobs, "$[*].target") = JSON_EXTRACT(jobs, "$[*].done")
AND JSON_EXTRACT(jobs, "$[*].status") != 0
AND JSON_SEARCH(jobs, 'one', DATE_SUB(CURDATE(), INTERVAL 1 DAY), NULL, "$[*].lastAction") IS NOT NULL

Check if an element is contained in the values ​(array) of a json column in MySql

I have the following values ​​inside a cell of a json column in MySql:
{
"produttori": [
"8",
"9"
],
"articoli_alternativi": [
"3",
"9"
],
"articoli_accessori": [
"5",
"6",
"7",
"8"
],
"tecnologie": [],
"fornitori": [
"9",
"8"
],
"classificazioni": [
"3",
"4"
]
}
I would like to make a query that extracts data based on the existence of a value in the array at the fornitori key.
For now I've tried this:
query = 'SELECT nome, formulati_commerciali FROM articolo WHERE JSON_CONTAINS(JSON_EXTRACT(dati, "$.fornitori"), "' + \
value+'", "$")'
Which print is:
SELECT name, data FROM articolo WHERE JSON_CONTAINS(JSON_EXTRACT(data, "$.fornitori"), "8", "$")
Basically the condition is that value ("8") must be inside the fornitori list, otherwise skips the element.
Unfortunately, the query did not produce any results.
I would like to know how you can formulate such a query in MySql. I will need them often!
Thanks in advance!
This should do it:
SELECT name, data
FROM articolo
WHERE JSON_CONTAINS(data, '"8"', '$.fornitori')
The double quotes around 8 are important, in order to properly match the JSON data. On the other hand, the query consistently uses single quotes for string literals.
You can use
SELECT data
FROM
(
SELECT #i := #i + 1 AS rn,
JSON_UNQUOTE(JSON_EXTRACT(data,CONCAT('$.fornitori[',#i-1,']'))) AS elm,
data
FROM information_schema.tables
CROSS JOIN articolo
CROSS JOIN (SELECT #i := 0) r
) q
WHERE elm = 8
in order to search for the spesific value within a spesific
array("fornitori")
Demo

Manipulating the JSON column and split the values

I have the following text in one of my Postgres table as TEXT datatype:
[
{"type": "text", "values": ["General"], "valueType": "string", "fieldType": "text", "value": ["General"], "customFieldId": "ee", "name": "customer_group"},
{"type": "text", "values": ["Vienna"], "valueType": "string", "fieldType": "text", "value": ["Vienna"], "customFieldId": "eU", "name": "customer_city"},
{"type": "text", "values": ["Mario"], "valueType": "string", "fieldType": "text", "value": ["Mario"], "customFieldId": "eZ", "name": "first_name"},
{"type": "text", "values": ["2019-06-30"], "valueType": "date", "fieldType": "text", "value": ["2019-06-30"], "customFieldId": "ea", "name": "created_at_date"}
]
I need to split the values of this TEXT field to columns and rows. For that I have converted the TEXT column to JSON as below:
SELECT CAST( "customFieldValues" as JSON) "customFieldValues" FROM fr.contacts
But when I tried to manipulate this JSON value I'm getting NULL as result.
WITH CTE AS(SELECT CAST( "customFieldValues" as JSON) "customFieldValues" FROM fr.contacts
)
SELECT
"customFieldValues" ->>'customer_city' as dd
FROM CTE
Does anyone have any suggestions on this? How to get the column names and it's values in rows. I want to create a TABLE based on this data.
Any suggestions would be of great help.
below is the expected result,
customer_group customer_city first_name created_at_date
General Vienna Mario 2019-06-30
Disclaimer: It is still not clear:
Why is there one element values and one value? What is the difference?
Why are these elements arrays?
step-by-step demo:db<>fiddle
SELECT
MAX(value) FILTER (WHERE column_name = 'customer_group') AS customer_group,
MAX(value) FILTER (WHERE column_name = 'customer_city') AS customer_city,
MAX(value) FILTER (WHERE column_name = 'first_name') AS first_name,
MAX(value) FILTER (WHERE column_name = 'created_at_date') AS created_at_date
FROM (
SELECT
elems ->> 'name' AS column_name,
elems -> 'value' ->> 0 AS value,
data
FROM
mytable,
json_array_elements(data::json) elems
) s
GROUP BY data
Cast text to json with ::json
Expand the JSON array: One row for each element with json_array_elements()
Getting the value: -> 'value' gets the array, ->> 0 gets the text representation of the first array element (the only one here)
Getting the column: ->> 'name' gets the text representation of the column name
Classical pivot algorithm (turning rows to columns) with the FILTER clause.

Search for a value in a JSON field in MYSQL

I am trying to understand the "new" MYSQL JSON field.
I have this table:
id (int-11, not_null, auto_inc)
customer_id (int-11, not null)
labels (json)
With this data:
id: 1
customer_id: 1
labels: [{"isnew": "no", "tagname": "FOO", "category": "CAT_1", "isdeleted": "no"}, {"isnew": "yes", "tagname": "BAR", "category": "CAT_2", "isdeleted": "no"}]
JSON beautify
[
{
"tagname": "FOO",
"category": "CAT_1",
"isnew": "no",
"isdeleted": "no"
},
{
"tagname": "BAR",
"category": "CAT_2",
"isnew": "yes",
"isdeleted": "no"
}
]
And now I want to SELECT all the customers (by customer_id) in the table that have a specific category and a specific tagname
I tried this one:
SELECT * FROM labels_customers_json
WHERE JSON_SEARCH(labels, 'all', 'BAR') IS NOT NULL
But this is not what I want. This one is searching in every json attribute.
I have seen some examples of JSON_EXTRACT:
SELECT * FROM `e_store`.`products`
WHERE
`category_id` = 1
AND JSON_EXTRACT(`attributes` , '$.ports.usb') > 0
AND JSON_EXTRACT(`attributes` , '$.ports.hdmi') > 0;
SELECT c, c->"$.id", g, n
FROM jemp
WHERE JSON_EXTRACT(c, "$.id") > 1
ORDER BY c->"$.name";
So I tried this
SELECT * FROM labels_customers_json
WHERE JSON_EXTRACT(labels, '$.tagname') = 'BAR'
SELECT labels, JSON_EXTRACT(labels, "$.customer_id"), customer_id
FROM labels_customers_json
WHERE JSON_EXTRACT(labels, "$.customer_id") > 0
You could probably try using SELECT * FROM labels_customers_json WHERE JSON_SEARCH(labels, 'all', "BAR", NULL, "$[*].tagname") is not null - although i cannot say if that is the best way to perform this query.
You can use JSON_SEARCH to search on a specific path too. So you can use the following query:
SELECT *
FROM labels_customers_json
WHERE JSON_SEARCH(labels, 'all', 'BAR', NULL, '$[*].tagname') IS NOT NULL
You can also use JSON_EXTRACT and JSON_CONTAINS together:
SELECT *
FROM labels_customers_json
WHERE JSON_CONTAINS(JSON_EXTRACT(labels, '$[*].tagname'), '["BAR"]') > 0;
You can also use only JSON_CONTAINS to check:
SELECT *
FROM labels_customers_json
WHERE JSON_CONTAINS(labels, '{"tagname":"BAR"}') > 0;
demos: https://www.db-fiddle.com/f/rufrThAQPfXHrK9YyibFSm/2

Mysql SELECT distinct Group by and Order by

I have problem with my SQL SELECT statement. I get in right order, right drivers, but my other columns are incorrect! And I can't get it right way.
I have data like this:
id, races_id, drivers_id, drive_nr, lap_nr, time, dnf
"231", "9", "41", "1", "1", "00:00:04.750", "0"
"232", "9", "41", "1", "2", "00:00:06.030", "0"
"233", "9", "41", "1", "3", "00:00:01.740", "0"
"234", "9", "42", "1", "1", "00:00:05.440", "0"
"235", "9", "42", "1", "2", "00:00:05.400", "0"
"236", "9", "42", "1", "3", "00:00:02.300", "0"
"237", "9", "43", "1", "1", "00:00:00.620", "0"
"238", "9", "43", "1", "2", "00:00:00.290", "0"
"239", "9", "43", "1", "3", "00:00:00.280", "0"
"240", "9", "44", "1", "1", "00:00:00.600", "0"
"241", "9", "44", "1", "2", "00:00:00.190", "0"
"242", "9", "44", "1", "3", "00:00:00.220", "0"
"243", "9", "45", "1", "1", "00:00:02.830", "0"
"244", "9", "45", "1", "2", "00:00:01.890", "0"
"245", "9", "45", "1", "3", "00:00:03.200", "0"
"246", "9", "46", "1", "1", "00:00:03.580", "0"
"247", "9", "46", "1", "2", "00:00:04.550", "0"
"248", "9", "46", "1", "3", "00:00:01.060", "0"
"249", "9", "47", "1", "1", "00:00:02.920", "0"
"250", "9", "47", "1", "2", "00:00:03.950", "0"
"251", "9", "47", "1", "3", "00:00:00.320", "0"
"252", "9", "48", "1", "1", "00:00:02.150", "0"
"253", "9", "48", "1", "2", "00:00:05.720", "0"
"254", "9", "48", "1", "3", "00:00:04.530", "0"
"255", "9", "49", "1", "1", "00:00:01.530", "0"
"256", "9", "49", "1", "2", "00:00:04.360", "0"
"257", "9", "49", "1", "3", "00:00:07.110", "0"
"258", "9", "50", "1", "1", "00:00:00.450", "0"
"259", "9", "50", "1", "2", "00:00:03.550", "0"
"260", "9", "50", "1", "3", "00:00:07.900", "0"
with query this:
SELECT `id` ,
`races_id` ,
`drivers_id` ,
`drive_nr` ,
`lap_nr` ,
MIN( `time` ) AS TIME,
`dnf`
FROM `laps`
WHERE `races_id` =9
GROUP BY drivers_id`
ORDER BY MIN( `time` ) ASC
I get:
id, races_id, drivers_id, drive_nr, lap_nr, time, dnf
240, 9, 44, 1, 1, 00:00:00.190, 0
237, 9, 43, 1, 1, 00:00:00.280, 0
249, 9, 47, 1, 1, 00:00:00.320, 0
258, 9, 50, 1, 1, 00:00:00.450, 0
246, 9, 46, 1, 1, 00:00:01.060, 0
255, 9, 49, 1, 1, 00:00:01.530, 0
231, 9, 41, 1, 1, 00:00:01.740, 0
243, 9, 45, 1, 1, 00:00:01.890, 0
252, 9, 48, 1, 1, 00:00:02.150, 0
234, 9, 42, 1, 1, 00:00:02.300, 0
so I get correct time column in correct order, but not others columns like ID, drive_nr, lap_nr, dnf
how to fix my query to get distinct drivers_id with min time with correct other data?
And if you remove "GROUP BY"
SELECT `id` , `races_id` , `drivers_id` , `drive_nr` , `lap_nr` , MIN( `time` ) AS TIME, `dnf` FROM `laps` WHERE `races_id` = 9 ORDER BY MIN( `time` ) ASC
Take a look on this link, use of group by and min
The problem is that GROUP BY groups rows together for an aggregate function. In standard SQL every column returned must be a mentioned in the group by clause or an aggregate field, but MySQL extends this.
However although MySQL does allow extra columns to be returned, it does not specify which row the value of those columns comes from. While there is a pattern (seems to be the last row inserted I think), this is not defined and could change.
To get the other fields you have a couple of options.
Simplest is to have a sub query that gets the driver id and the min time for a lap for that driver id, then join that back against the laps table (joining on the driver id and the time) top get the values of the others fields for the matching row. There are a couple of minor downs sides to this. Firstly MySQL will not use an index on the fields on the sub query to join to the main table, but with limited data probably not an issue (beyond the annoyance of the queries popping up in the slow query log). The 2nd issue is if someone has a best lap time shared between 2 laps.
Simple example of the sql:-
SELECT a.id,
a.races_id,
a.drivers_id,
a.drive_nr,
a.lap_nr,
mt.min_time,
a.dnf
FROM laps a
INNER JOIN
(
SELECT drivers_id ,
MIN( `time` ) AS min_time
FROM laps
WHERE races_id = 9
GROUP BY drivers_id
) mt
ON a.drivers_id = mt.drivers_id
AND a.`time` = mt.min_time
WHERE a.races_id = 9
ORDER BY min_time ASC
If you do have 2 laps with the same min lap time then you need to specify which ones details to return (or you might not care, and could just misuse GROUP BY on the outer query as well).
A second solution is that you generate a sequence number for each row on the results ordered by driver id and lap time, resetting the sequence number on change of driver id. Then discard any lap which does not have a sequence of 1. However harder to read, and likely to be very slow when you have lots of data.
Example as follows (not tested):-
SELECT id,
races_id,
drivers_id,
drive_nr,
lap_nr,
`time`,
dnf
FROM
(
SELECT id,
races_id,
drivers_id,
drive_nr,
lap_nr,
`time`,
dnf,
#ctr := IF(drivers_id = #did, #ctr + 1, 1) AS ctr,
#did := drivers_id
FROM
(
SELECT id,
races_id,
drivers_id,
drive_nr,
lap_nr,
`time`,
dnf
FROM laps
WHERE races_id = 9
ORDER BY drivers_id, `time`
)
CROSS JOIN
(
SELECT #ctr := 0, #did := 0
) sub1
) sub2
WHERE ctr = 1
ORDER BY `time`