Mysql: insert multiple values with order number - mysql

I want to split a string like ('abc', 'dev', dfg') and insert into a table with its order number like that:
index value
1 abc
2 dev
3 dfg
Is anyway I can do this in mysql? The size is unknown.

You probably can do something like this:
WITH RECURSIVE cte AS (
SELECT 1 idx, LENGTH(REGEXP_REPLACE(stringVal,'[^,]',''))+1 AS maxLen
FROM mytable UNION
SELECT idx+1, maxLen FROM cte WHERE idx+1 <= maxLen)
SELECT idx,
SUBSTRING_INDEX(SUBSTRING_INDEX(stringVal,',',idx),',',-1) AS val
FROM cte
CROSS JOIN mytable;
Generating idx using cte based on how many strings were separated by comma in the column. The steps are:
Use REGEXP_REPLACE() to replace any value that is not comma; returns ,,.
Use LENGTH() on the regexp to get the total of comma; returns 2.
Add 1 (+1) to the length result returned; final value 3.
The cte result are:
+-----+--------+
| idx | maxLen |
+-----+--------+
| 1 | 3 |
| 2 | 3 |
| 3 | 3 |
+-----+--------+
From the generated cte , we're going to use idx value with SUBSTRING_INDEX() to separate the comma-separated, single-row string into multiple rows.
Understanding what the query is doing:
This, we can emulate with 3 of the same query repeated UNION ALL together like the following:
SELECT 1 AS 'index',
SUBSTRING_INDEX(SUBSTRING_INDEX(stringVal,',',1),',',-1) AS val
FROM mytable
UNION ALL
SELECT 2,
SUBSTRING_INDEX(SUBSTRING_INDEX(stringVal,',',2),',',-1)
FROM mytable
UNION ALL
SELECT 3,
SUBSTRING_INDEX(SUBSTRING_INDEX(stringVal,',',3),',',-1)
FROM mytable;
Which also can be done as such; Same idea as the cte approach:
SELECT idx,
SUBSTRING_INDEX(SUBSTRING_INDEX(stringVal,',',idx),',',-1) AS val
FROM (SELECT 1 idx UNION
SELECT 2 UNION
SELECT 3 ) i
CROSS JOIN mytable;
Both of these query return the same result as the cte approach however it's not an ideal one; since we have to manually check how many string separated by comma and define (hard-code) it in the query. Imagine if a single row of string consists of something like a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z - all the 26 alphabet; then that means the UNION query need to be repeated 26 times.
Demo fiddle

Related

How to preserve a json_array when creating a JSON object using json_objectagg in MySQL?

I'm trying to build a JSON object with some data that I need aggregated. I first aggregate values into an array, and then I aggregate them into a json object. I have not been able to figure out how to prevent it from turning the JSON array into a string.
Here's an example, which mimics my schema, and shows the problem:
select
rd.r,
json_objectagg(rd.f, rd.r_data) as rdata
from (
select
r,
f,
CASE WHEN (count(v) > 1) THEN json_arrayagg(v) ELSE v END as r_data
from (
select 1 as r, 1 as f, 1 as v
union all
select 1 as r, 2 as f, 'a string' as v
union all
select 1 as r, 2 as f, 3 as v
union all
select 2 as r, 1 as f, 1 as v
union all
select 2 as r, 2 as f, 2 as v
union all
select 2 as r, 2 as f, '2023-01-01' as v
union all
select 3 as r, 1 as f, 1 as v
union all
select 3 as r, 2 as f, 2 as v
union all
select 3 as r, 2 as f, 'true' as v
) row_data
group by row_data.r, row_data.f
order by row_data.r, row_data.f
) rd
GROUP BY rd.r
The json generated is as follows: {"1":"1","2":"["a string", "3"]"}
Notice the array is quoted, making it not an array, but a string that looks like an array. How do I get it to be an actual array?
I've tried some different approaches, but they all result in the JSON array being treated as a string.

MySQL 8 Recursive CTE Error Code: 1054. Unknown column

This MySQL 8 query:
WITH RECURSIVE cte_count (n)
AS (
SELECT 1
UNION ALL
SELECT
n + 1
FROM cte_count
WHERE n < 3
)
SELECT *
FROM cte_count;
Produces:
+------+
| n |
+------+
| 1 |
| 2 |
| 3 |
+------+
But this one:
WITH RECURSIVE cte_count (n)
AS (
SELECT 1
,"one" as `one`
UNION ALL
SELECT
n + 1
,"one" as `one`
FROM cte_count
WHERE n < 3
)
SELECT *
FROM cte_count;
generates the error:
Error Code: 1054. Unknown column 'n' in 'field list'
Can you explain why and how to solve that error?
What is the meaning of (n) after WITH RECURSIVE cte_count?
There are 2 ways that you can write this.
Without declaring the column names alongside with the cte's name:
WITH RECURSIVE cte_count
AS (
SELECT 1 as n
,'one' as `one`
UNION ALL
SELECT
n + 1
,'one'
FROM cte_count
WHERE n < 3
)
SELECT *
FROM cte_count;
and declaring them without the need to alias them inside the query's body:
WITH RECURSIVE cte_count (n, `one`)
AS (
SELECT 1
,'one'
UNION ALL
SELECT
n + 1
,'one'
FROM cte_count
WHERE n < 3
)
SELECT *
FROM cte_count;
You can look at WITH RECURSIVE cte_count (n) in a more abstract way.
You always need WITH RECURSIVE for your recursive query followed by a name for it (in your case cte_count).
What follows next is a comma separated list which defines the number of attributes for your query as well as the respective names for your columns (e.g. (n)).
The problem in the second query is that you introduced a new column with "one" as 'one'.
Try to only write "one" instead of "one" as 'one' (both times!) and make (n) to (n, one) to fix your problem.

way to populate number of rows in mysql without selecting table

I am looking for a way to populate values in row in mysql
for example if i execute query
select 1,2,3,4
it gives response
+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+
that is in row
is there a possible way to produce this data in column
like
1
2
3
4
With the introduction of Recursive with clause in MySQL version 8.0 or higher, We now have a row generator in MySQL -
WITH RECURSIVE cte_count (n)
AS (
SELECT 1
UNION ALL
SELECT n + 1
FROM cte_count
WHERE n < 100
)
SELECT n
FROM cte_count;
Before version 8.0 MySQL doesn't have any inbuilt row_generator functionality.
If you want values in a column, you can use:
select 1 as n union all
select 2 as n union all
select 3 as n union all
select 4 as n
This construct is often used in derived tables (or more recently in CTEs).
If you just need a set number of values, then an existing table (that is big enough) can be used:
select (#rn := #rn + 1) as n
from t cross join
(select #rn := 0) params
limit 4;

MySQL: Retrieve Values and Counts For Each

How can I count the occurrence of the field/column in SQL?
Example dataset:
A
A
A
A
B
B
C
I want:
A | 4
A | 4
A | 4
A | 4
B | 2
B | 2
C | 1
Is there anyway to do it without using GROUP BY? So far all answer I get my query retuns the following:
A | 4
B | 2
C | 1
select value, count(*) from table group by value
Use HAVING to further reduce the results, e.g. only values that occur more than 3 times:
select value, count(*) from table group by value having count(*) > 3
You could use a nested sub-select for this desired result set.
If the example table name is my_table and the column called col1:
select col1,
(select count(*) from my_table where col1 = t.col1) as Count
from my_table t;
Or if you want to remove the duplicates, use the distinct statement. It removes the duplicates of your result set.
select distinct col1,
(select count(*) from my_table where col1 = t.col1) as Count
from my_table t;

Mysql - rearrange column values with sql

I have a table with several columns, and four of them are just generic integer values (num1, num2, num3, num4) and the source of data has these values in random order. In php I would fetch the row, assign the four ints to an array, sort the array, assign the sorted values back and save the row. I would like to do this with sql. So, with a table like
id, num1, num2, num3, num4
1 6 2 9 1
2 12 3 8 4
...
I would like the results to be
id, num1, num2, num3, num4
1 1 2 6 9
2 3 4 8 12
...
I have Googled everything I can think of and scoured the Mysql ref manual but can not seem to find a way to do this. Any thoughts or should I just write a php script?
Well that's an unpivot, a sort and a pivot, but last I heard not in mysql so
maybe
Set #position = 0
CREATE TEMPORARY TABLE OrderedUnpivot AS (
Select Id,#position = #position + 1, #position % 4 as Position, Value From
(
Select Id,Position,Value From
(
select Id, 1 as Position, num1 as Value
Union
select Id, 2, num2
union
select Id, 3, num3
union
select Id, 4, num4
) depivot
Order by Id, Value
)
Select ID,V1.Value as Num1, V2.Value as Num2, V3.Value as Num3, V4.Value as Num4
From OrderedUnpivot v1
Inner join OrderedUnpivot v2 ON v1.Id = v2.ID
Inner join OrderedUnpivot v3 ON v1.Id = v3.ID
Inner join OrderedUnpivot v4 ON v1.Id = v4.ID
Messy but food for thought, oh and not tested or even syntax checked.