Flattening a Table in prep for Json - mysql

Maybe it's that I'm tired but this is escaping me.
Let's say that I want to flatten this table:
a_id a_val b_id b_val c_id c_val d_id d_val
1 a 10 b 100 c 1000 f
1 a 20 d 200 g null null
2 e 30 h 300 i null null
2 j 40 k null null null null
3 l null null null null null null
Into this query result:
id mystring
1, (1:a,10:b,100:c,1000:f),(1:a,20:d,200:g)
2, (2:e,30:h,300:i),(2:j,40:k)
3, (3:l)
The table only renders four levels deep (a, b, c, d) so no dynamic sql issue.
Now I'd usually just use GROUP_CONCAT(CONCAT(...)) but that won't work with the Nulls present. And maybe using coalesce somehow will solve this but... I feel pretty stupid at the moment... and I can't figure it out.
Unfortunately I can't use mysql json services on this installation so I need to construct the data. thanks.

The solution here will probably just be a combination of clever concatenation and IFNULL calls. My shot in the dark:
SELECT a_id, GROUP_CONCAT(CONCAT('(',
a_id, ':', a_value, ',',
IFNULL(b_id, ''), IF(b_id IS NOT NULL, ':', ''), IFNULL(b_val, ''),
...repeat for c and d
')'
) SEPARATOR ',')
FROM table
GROUP BY a_id;

select a_id as id,
group_concat(concat(
case isnull(a_id) when true then '' else '(' end,
coalesce(a_id, ''),
case isnull(a_id) when true then '' else ':' end,
coalesce(a_val, ''),
case isnull(b_id) when true then '' else ',' end,
coalesce(b_id, ''),
case isnull(b_id) when true then '' else ':' end,
coalesce(b_val, ''),
case isnull(c_id) when true then '' else ',' end,
coalesce(c_id, ''),
case isnull(c_id) when true then '' else ':' end,
coalesce(c_val, ''),
case isnull(d_id) when true then '' else ',' end,
coalesce(d_id, ''),
case isnull(d_id) when true then '' else ':' end,
coalesce(d_val,''),
case isnull(a_id) when true then '' else ')' end
) separator ',')
from table
group by a_id;

Related

Best way to ignore null values in MYSQL

My data looks like this,
I have multiple values in the Table field with each having its own unique values for the Data field. However, Data values for TableA will be the same format in the table myTable. Same applies to all records in the Table field.
I am using JSON_EXTRACT to get the value in conjunction with CASE statements.
SELECT
CASE
WHEN
Table = 'table A'
THEN
CONCAT_WS('\n','Name: ',
JSON_EXTRACT(Data, '$.name'))
END AS 'tableA',
CASE
WHEN
Table = 'table B'
THEN
CONCAT_WS('\n','Location: ',
JSON_EXTRACT(Data, '$.fieldType'))
END AS 'tableB' from myTable
The problem with this is that I will be getting null values.
I am getting the below results,
I am expecting the below results,
I want to avoid the null values. Is there any other way to extract the data ? I am using MYSQL.
Option one:
Select *
from ( SELECT CASE WHEN Table_c = 'table A' THEN
CONCAT_WS('\n','Name: ', JSON_EXTRACT(data_c, '$.name'))
END AS 'TableA'
from test_t) A,
( SELECT CASE WHEN Table_c = 'table B' THEN
CONCAT_WS('\n','Location: ', JSON_EXTRACT(data_c, '$.fieldType'))
END AS 'TableB'
from test_t) B
where TableA is not null
and TableB is not null
Option two:
SELECT CASE WHEN Table_c = 'table B' THEN
CONCAT_WS('\n','Location: ', JSON_EXTRACT(data_c, '$.fieldType'))
ELSE
CONCAT_WS('\n','Name: ', JSON_EXTRACT(data_c, '$.name'))
END AS 'Data'
, CASE WHEN Table_c = 'table B' THEN
'Table B'
ELSE
'Table A'
END AS 'Table'
from test_t
Here is the demo
Please note that I have changed the names of the table and columns in my demo.
I think you just want conditional aggregation:
SELECT MAX(CASE WHEN Table = 'table A'
THEN CONCAT_WS('\n', 'Name: ', JSON_EXTRACT(Data, '$.name'))
END) AS tableA,
MAX(CASE WHEN Table = 'table B'
THEN CONCAT_WS('\n','Location: ', JSON_EXTRACT(Data, '$.fieldType'))
END) AS tableB
from myTable;
If there could be multiple matches in the JSON, then you might want GROUP_CONCAT() instead:
SELECT GROUP_CONCAT(CASE WHEN Table = 'table A'
THEN CONCAT_WS('\n', 'Name: ', JSON_EXTRACT(Data, '$.name'))
END) AS tableA,
GROUP_CONCAT(CASE WHEN Table = 'table B'
THEN CONCAT_WS('\n','Location: ', JSON_EXTRACT(Data, '$.fieldType'))
END) AS tableB
from myTable;
Note: you have to understand these are not actual null values in database but because your requirement is to read and convert the rows to columns and thus each row in the final result will have null value for all columns other than the corresponding derived one. We can use max on each column (each "CASE" as in your query).
You could try with below,
SELECT MAX(IF(data_type = 'TableA', CONCAT_WS('\n', 'Name: ',
JSON_EXTRACT(data, '$.name'))
, ''))
TableA,
MAX(IF(data_type = 'TableB', CONCAT_WS('\n', 'Location: ',
JSON_EXTRACT(data, '$.fieldType')), ''))
TableB
FROM mytable

SQL Convert a char to boolean

I have in my table one row with a char value. When the value is NULL then a false should be outputted. If the value is not NULL then a true should be outputted.
So when I try to set user_group.tUser to 0 or 1 then I'm getting this error:
Invalid column name 'false'.
Invalid column name 'true'.
SELECT COALESCE((SELECT name
FROM v_company
WHERE companyId = userView.companyId), ' ') AS company,
userView.value AS companyUser,
userView.display AS displayedUser,
CASE
WHEN user_group.tUser IS NULL THEN 0
ELSE 1
END AS userIsMemberOfGroup
FROM v_user userView
LEFT OUTER JOIN cr_user_group user_group
ON ( user_group.group = 'Administrators'
AND user_group.tUser = userView.value )
ORDER BY company ASC,
displayedUser ASC
I think this is the logic you want:
SELECT COALESCE(v.name, ' ') as company,
u.value as companyUser, u.display as displayedUser,
(EXISTS (SELECT 1
FROM cr_user_group ug
WHERE ug.group = 'Administrators' AND
ug.tUser = uv.value
)
) as userIsMemberOfGroup
FROM v_user u LEFT JOIN
v_company c
ON c.companyId = v.companyId
ORDER BY company ASC, displayedUser ASC ;
In general, MySQL is very flexible about going between booleans and numbers, with 0 for false and 1 for true.
You can use MySQL IF function to return 'false' when name IS NULL, else 'true':
SELECT IF(name IS NULL, 'false', 'true')
FROM table;
A simple CASE expression would work here:
SELECT
name,
CASE WHEN name IS NOT NULL THEN true ELSE false END AS name_out
FROM yourTable;
We could also shorten the above a bit using IF:
IF(name IS NOT NULL, true, false)
SELECT
CASE
WHEN name IS NULL THEN 'false'
ELSE 'true'
END
FROM
table1;

Separate comma separated list into query columns

I have a table which has the column containing the comma separated list like
ID : List
1 : 1,2,44,5 --row# 1
2 : 4,3,5,2,56,66 --row# 2
and so on. I want to write a select query which would have at max 10 columns Item1, Item2, Item3 .... Item10 and each column has a number from the corresponding comma separated list.
For example: for ID = 1
Item1 = 1, Item2 = 2, Item3 = 44, Item4 = 55 and all other columns would be null or empty
How can I write this in SQL?
You can do it like this:
select
substring_index(substring_index(str,',',1),',',-1)AS c1
, CASE WHEN LENGTH(str)-LENGTH(REPLACE(str,',','')) >= 1 THEN substring_index(substring_index(str,',',2),',',-1) ELSE NULL END AS c2
, CASE WHEN LENGTH(str)-LENGTH(REPLACE(str,',','')) >= 2 THEN substring_index(substring_index(str,',',3),',',-1) ELSE NULL END AS c3
, CASE WHEN LENGTH(str)-LENGTH(REPLACE(str,',','')) >= 3 THEN substring_index(substring_index(str,',',4),',',-1) ELSE NULL END AS c4
, CASE WHEN LENGTH(str)-LENGTH(REPLACE(str,',','')) >= 4 THEN substring_index(substring_index(str,',',5),',',-1) ELSE NULL END AS c5
, CASE WHEN LENGTH(str)-LENGTH(REPLACE(str,',','')) >= 5 THEN substring_index(substring_index(str,',',6),',',-1) ELSE NULL END AS c6
, CASE WHEN LENGTH(str)-LENGTH(REPLACE(str,',','')) >= 6 THEN substring_index(substring_index(str,',',7),',',-1) ELSE NULL END AS c7
, CASE WHEN LENGTH(str)-LENGTH(REPLACE(str,',','')) >= 7 THEN substring_index(substring_index(str,',',8),',',-1) ELSE NULL END AS c8
, CASE WHEN LENGTH(str)-LENGTH(REPLACE(str,',','')) >= 8 THEN substring_index(substring_index(str,',',9),',',-1) ELSE NULL END AS c9
, CASE WHEN LENGTH(str)-LENGTH(REPLACE(str,',','')) >= 9 THEN substring_index(substring_index(str,',',10),',',-1) ELSE NULL END AS c10
from test
Demo.
The expressions have two common parts:
LENGTH(str)-LENGTH(REPLACE(str,',','')) >= K - this subexpression determines if the string has at least K delimiters
substring_index(substring_index(str,',',K),',',-1) - this subexpression cuts out the element after the K-th delimiter
This can be done by creating a user-defined function reference from MySQL Split String Function ,pass your column which contains the comma separated string as first parameter to this function,in second parameter pass the separator in your case comma is separator and in third parameter pass your desired position to get the string
SPLIT_STR(string, separator, position)
CREATE FUNCTION SPLIT_STR(
x VARCHAR(255),
delim VARCHAR(12),
pos INT
)
RETURNS VARCHAR(255)
RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1),
delim, '');
And then you can query your data a below
SELECT
SPLIT_STR(List, ',', 1) item1,
SPLIT_STR(List, ',', 2) item2,
SPLIT_STR(List, ',', 3) item3,
SPLIT_STR(List, ',', 4) item4,
SPLIT_STR(List, ',', 5) item5,
SPLIT_STR(List, ',', 6) item6,
SPLIT_STR(List, ',', 7) item7,
SPLIT_STR(List, ',', 8) item8,
SPLIT_STR(List, ',', 9) item9,
SPLIT_STR(List, ',', 10) item10
FROM t
Fiddle Demo

Counting most common word in SQL table with exclusions

I'm trying to count the most common words from a table full of text (strings) in a MySQL database (using MYSQL workbench). I got this code working from reading another post (written by Kickstart).
This code uses a separate table called integer with 10 columns from 0 to 9 for counting.
Table Schema for the main table. I'm mainly only interested in data the "Text" column.
'Id', 'int(11)', 'NO', 'PRI', '0', ''
'PostId', 'int(11)', 'YES', 'MUL', NULL, ''
'Score', 'int(11)', 'YES', 'MUL', NULL, ''
'Text', 'varchar(4000)', 'YES', '', NULL, ''
'CreationDate', 'varchar(25)', 'YES', '', NULL, ''
'UserId', 'int(11)', 'YES', 'MUL', NULL, ''
'UserDisplayName', 'varchar(255)', 'YES', '', NULL, ''
SQL query:
SELECT aWord, COUNT(*) AS WordOccuranceCount
FROM (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(concat(Text, ' '), ' ', aCnt), ' ', -1) AS aWord
FROM table
CROSS JOIN (
SELECT a.i+b.i*10+c.i*100 + 1 AS aCnt
FROM integers a, integers b, integers c) Sub1
WHERE (LENGTH(Body) + 1 - LENGTH(REPLACE(Text, ' ', ''))) >= aCnt) Sub2
WHERE Sub2.aWord != ''
GROUP BY aWord
ORDER BY WordOccuranceCount DESC
LIMIT 10
It lists out the top 10 words, but they are full of short words like a, the, you, me... etc.
How can I change it to skip certain words like those?
How can I make it so that say, only words 5 characters and up are counted?
Schema of integers table
'i', 'int(11)', 'NO', 'PRI', NULL, ''
Original post and code taken from this post. I am new and couldn't post anything on it so I had to ask here.
determining most used set of words php mysql
Thank you so much for your help!
You should be able to just add another condition to your WHERE clause:
SELECT aWord, COUNT(*) AS WordOccuranceCount
FROM (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(concat(Text, ' '), ' ', aCnt), ' ', -1) AS aWord
FROM table
CROSS JOIN (
SELECT a.i+b.i*10+c.i*100 + 1 AS aCnt
FROM integers a, integers b, integers c) Sub1
WHERE (LENGTH(Body) + 1 - LENGTH(REPLACE(Text, ' ', ''))) >= aCnt) Sub2
WHERE Sub2.aWord != '' AND
LENGTH(Sub2.aWord) >= 5
GROUP BY aWord
ORDER BY WordOccuranceCount DESC
LIMIT 10
Just checking to see if the length of aWord is at least 5 chars, and if so, include it in the result set. The LIMIT will be applied to the result set (post-filtering) and you should have what you need.

Multiple IF statements on MYSQL

I'm trying to Display somes values in my database result, I am using this code but I can not succeed:
SELECT
item_code,
IF(category_code = 'HERR1', 'NO', 1) OR (category_code = 'COLN5', 'NO', 2) AS category_code,
item_name,
item_quantity
FROM qa_items
EDIT :
I Want to display for example:
If category_code = 'HERR1'
Display = 1
else if category_code = 'COLN5'
Display = 2
End If
If anyone has any idea, would greatly appreciate it
I'd rather use CASE :
SELECT item_code,
CASE category_code
WHEN 'HERR1' THEN 1
WHEN 'COLN5' THEN 2
ELSE 'NO'
END as category_code, item_name, item_quantity
FROM qa_items
But IF will also work : IF(category_code='HERR1',1, IF(category_code='COLN5',2,'NO'))
You need to nest the if statements
SELECT item_code, IF(category_code = 'HERR1', 'NO', IF(category_code = 'COLN5', 1, 2)) AS category_code, item_name, item_quantity FROM qa_items
Then the first if will fail and the nested if will evaluate
Is this what you were after?
SELECT
item_code,
CASE category_code
WHEN 'HERR1' THEN 1
WHEN 'COLN5' THEN 2
ELSE 'NO'
END AS category_code,
item_name,
item_quantity
FROM qa_items
Try the following
SELECT item_code, CASE category_code WHEN 'HERR1' THEN 1 WHEN 'COLN5' THEN 0 ELSE 'NONE' END AS category_code, item_name, item_quantity FROM qa_items
You can try this.
Use IF in select query and update the table you want ;)
create table student(marks int,grade char);
insert into student values(200,null),(120,null),
(130,null);
UPDATE student a
INNER JOIN (select s.marks, IF(s.marks>=200,'A',IF(s.marks>=130,'B','P')) AS Grade from student s) b on a.marks= b.marks
SET a.Grade = b.Grade;
SELECT MOBILE,
CASE (SUBSTRING(mobile, LENGTH(MOBILE), 1)) WHEN ','
THEN SUBSTRING(mobile, 1, LENGTH(MOBILE) - 1)
ELSE SUBSTRING(mobile, 1, LENGTH(MOBILE))
END AS newmobile
FROM (SELECT CONCAT(IFNULL(`mob1`, ''), IF(`mob1` IS NULL, '', ','),
IFNULL(`mob2`, ''), IF(`mob2` IS NULL, '', ','),
IFNULL(`mob3`, ''), IF(`mob3` IS NULL, '', ','),
IFNULL(`mob4`, ''), IF(`mob4` IS NULL, '', ','),
IFNULL(`mob5`, ''), IF(`mob5` IS NULL, '', ','),
IFNULL(`mob6`, ''))
AS mobile
FROM `temp_consignordata`) AS T
SELECT item_code,
-- First if
IF(category_code = 'HERR1', 1,
-- second else IF
IF(category_code = 'COLN5', 2,
-- last else
'NO')
AS category_code,
item_name,
item_quantity
FROM qa_items;
Explanation
first if evalutes for value 'HERR1' and if found assigns 1
Second (else ) IF evalues for Value 'COLN5' and if found assigns 2
Last (else) default case assigns 'NO'
to category_code