mysql select substrings and group them by column - mysql

I am trying to divide data in one onf the tables on my MySQL database.
Column contains data like this:
de:"Sweatjacke*";en:"jacket*";pl:"bluza*";
de:"*";en:"*";pl:"bluza*";
fr:"*";de:"*";en:"*";pl:"dres junior*";cz:"*";
pl:"bluza";
And I am trying to divide all of the translations into separate columns. Already came with solution to do this by using:
SELECT
SUBSTRING_INDEX(SUBSTRING_INDEX(name, ';', 1), ';', -1) as tr1,
SUBSTRING_INDEX(SUBSTRING_INDEX(name, ';', 2), ';', -1) as tr2,
SUBSTRING_INDEX(SUBSTRING_INDEX(name, ';', 3), ';', -1) as tr3,
SUBSTRING_INDEX(SUBSTRING_INDEX(name, ';', 4), ';', -1) as tr4,
SUBSTRING_INDEX(SUBSTRING_INDEX(name, ';', 5), ';', -1) as tr5
FROM product;
statement, but that results in:
tr1 tr2 tr3 tr4 tr5
fr:"*" de:"*" en:"*" pl:"bluza*" cz:"*"
fr:"*" de:"Sweatjacke*" en:"jacket*" pl:"bluza*" cz:"*"
de:"Sweatjacke*" en:"jacket*" pl:"bluza*"
And I want to have the results gruped by translation type (pl/de/en) so in each collumn one type of translatoin is present. For example in column1 = pl:, column2 = en: etc.
Any one came across similar problem and knows a way to solve it?

You need to unpivot the data, then select the first and second part of each value and then re-aggregate it.
However, a better form for the data is really to have language/translation. The following produces this:
select substring_index(tr, ':', 1) as l, substring_index(tr, ':', 2) as t, name
from (select SUBSTRING_INDEX(SUBSTRING_INDEX(name, ';', n.n), ';', -1) as tr, n, name
from product p cross join
(select 1 as n union all select 2 union all select 3 union all select 4 union all
select 5
) n
) n
You would probably want an "id" column or "word" column to identify each row, rather than the name column.
You can now pivot this result to get what you want:
select max(case when l = 'en' then name end) as en,
max(case when l = 'fr' then name end) as fr,
max(case when l = 'de' then name end) as de,
max(case when l = 'pl' then name end) as pl,
max(case when l = 'cz' then name end) as cz
from (select substring_index(tr, ':', 1) as l, substring_index(tr, ':', 2) as t, name
from (select SUBSTRING_INDEX(SUBSTRING_INDEX(name, ';', n.n), ';', -1) as tr, n, name
from product p cross join
(select 1 as n union all select 2 union all select 3 union all select 4 union all
select 5
) n
) n
) lt
group by name;

Managed to solve it by using some of the string related functions funcitons:
SELECT
SUBSTRING_INDEX( SUBSTRING( name, LOCATE( "pl:", name ) , 150 ) , ';', 1 ) AS pl,
SUBSTRING_INDEX( SUBSTRING( name, LOCATE( "en:", name ) , 150 ) , ';', 1 ) AS en,
SUBSTRING_INDEX( SUBSTRING( name, LOCATE( "de:", name ) , 150 ) , ';', 1 ) AS de,
SUBSTRING_INDEX( SUBSTRING( name, LOCATE( "fr:", name ) , 150 ) , ';', 1 ) AS fr
FROM product
Thanks to everyone for help.

As far as I understand you want to UNPIVOT your data. There is no such function in MySQL, so you might want to export your data into MSSQL (you can use free MSSQL Express) and use UNPIVOT function: http://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx

Related

MySQL - Split Delimited Right and Left Values

I have a case where I will get input for products and their values separated with a delimited special character. Using this string I need to separate products and their values to MySQL Rows like below.
Input:
{"1301":29.00,"1302":25.01,"1306":50.09,"1678":100.00}
Output:
Product ID Value
1301 29.00
1302 25.01
1306 50.09
1678 100.00
Here the products Id counts are dynamic, we can get n count every time. Please help me getting the above output in MySQL.
MySQL only solution with JSON functions.
Query
SELECT
TRIM(REPLACE(
SUBSTRING_INDEX(
SUBSTRING_INDEX(json_parsed, ',', number_generator.number)
, ','
, -1
)
, '"'
, ''
)) AS 'Product ID'
, JSON_EXTRACT(json, CONCAT('$.', SUBSTRING_INDEX(
SUBSTRING_INDEX(json_parsed, ',', number_generator.number)
, ','
, -1
))) AS 'Value'
FROM (
SELECT
#row := #row + 1 AS number
FROM (
SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row1
CROSS JOIN (
SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row2
CROSS JOIN (
SELECT #row := 0
) init_user_params
) AS number_generator
CROSS JOIN (
SELECT
SUBSTRING(json_keys, 2, json_keys_length - 2) AS json_parsed
, json_keys
, json
, JSON_LENGTH(json_keys) AS json_array_length
FROM (
SELECT
JSON_KEYS(record.json) AS json_keys
, json
, LENGTH(JSON_KEYS(record.json)) AS json_keys_length
FROM (
SELECT
'{"1301":29.00,"1302":25.01,"1306":50.09,"1678":100.00}' AS json
FROM
DUAL
) AS record
) AS json_information
) AS json_init
WHERE
number_generator.number BETWEEN 0 AND json_array_length
Result
| Product ID | Value |
| ---------- | ----- |
| 1301 | 29.0 |
| 1302 | 25.01 |
| 1306 | 50.09 |
| 1678 | 100.0 |
see demo
You really should be treating this like JSON, but you can use brute-force string methods. Here is one method:
select replace(substring_index(str, ':', 1), '"', '') as product_id, substring_index(str, ':', -1) as value
from (select replace(replace(substring_index(#x, ',', 1), '{', ''), '}', '') as str) x
union all
select replace(substring_index(str, ':', 1), '"', '') as product_id, substring_index(str, ':', -1) as value
from (select replace(replace(substring_index(substring_index(#x, ',', 2), ',', -1), '{', ''), '}', '') as str) x
union all
select replace(substring_index(str, ':', 1), '"', '') as product_id, substring_index(str, ':', -1) as value
from (select replace(replace(substring_index(substring_index(#x, ',', 3), ',', -1), '{', ''), '}', '') as str) x
union all
select replace(substring_index(str, ':', 1), '"', '') as product_id, substring_index(str, ':', -1) as value
from (select replace(replace(substring_index(substring_index(#x, ',', 4), ',', -1), '{', ''), '}', '') as str) x;
along with a db<>fiddle.

SQL query with subquery

My data is like this:
data1_qqq_no_abc_ccc
data1_qqq_abc_ccc
data2_qqq_no_abc_ccc
data2_qqq_abc_ccc
data3_qqq_no_abc_ccc
data4_qqq_no_abc_ccc
data4_qqq_abc_ccc
...
Now I want to get the fields where data has substring _no_abc_ccc, but doesn't have _abc_ccc. In the above example, its data3
I am trying to create a query for it.
rough one is
select SUBSTRING_INDEX(name, 'abc', 1)
from table1
where SUBSTRING_INDEX(name, 'abc', 1) not LIKE "%no"
and NOT IN (select SUBSTRING_INDEX(name, '_no_abc', 1)
from table
where name LIKE "%no_abc");
Something like this (?)
create table t (
col text
);
insert into t
values
('data1_qqq_no_abc_ccc'),
('data1_qqq_abc_ccc'),
('data2_qqq_no_abc_ccc'),
('data2_qqq_abc_ccc'),
('data3_qqq_no_abc_ccc'),
('data4_qqq_no_abc_ccc'),
('data4_qqq_abc_ccc');
select f from (
select SUBSTRING_INDEX(col, '_', 1) as f, SUBSTRING_INDEX(col, '_', -3) as s from t
) tt
group by f
having
count(case when s = 'no_abc_ccc' then 1 end) > 0
and
count(case when s like '%qqq_abc%' then 1 end) = 0
demo

Get Values not in the second table using find_in_set

I have two tables and i need to get list of all store_ids that are not in the other table
BusinessUnit Table User Table
StoreId(varchar) StoreId(varchar)
1 1,2
2 3,4
3 1,5
4 4,6
7 4
How to get values of storeid 5,6 which are not present in the business unit table but are present in the user Table? Tried to use several using find_in_set and nothing works.
Use SUBSTRING_INDEX to get all the values from the CSV field. Since there can be up to 6 IDs in the CSV, you need to call it once for each position.
SELECT u.StoreId
FROM (
select substring_index(StoreId, ',', 1) AS StoreID
FROM User
UNION
select substring_index(substring_index(StoreId, ',', 2), ',', -1)
FROM User
UNION
select substring_index(substring_index(StoreId, ',', 3), ',', -1)
FROM User
UNION
select substring_index(substring_index(StoreId, ',', 4), ',', -1)
FROM User
UNION
select substring_index(substring_index(StoreId, ',', 5), ',', -1)
FROM User
UNION
select substring_index(substring_index(StoreId, ',', 6), ',', -1)
FROM User) AS u
LEFT JOIN BusinessUnit AS b ON u.StoreId = b.StoreID
WHERE b.StoreId IS NULL
DEMO
IF you know all the possible values (and the number of them is reasonably manageable) you can populate a new table with them (you can make it TEMPORARY or just DROP it afterwards), and do this
SELECT *
FROM (
SELECT allIDs.Id
FROM allIDs
INNER JOIN `User` AS u
-- ON CONCAT(',', u.StoreID, ',') LIKE CONCAT('%,', allIDs.Id, ',%')
ON FIND_IN_SET(allIDs.Id, u.StoreID)
) AS IDsInUserTable
LEFT JOIN `BusinessUnit` AS b ON IDsInUserTable.Id = b.StoreID
HAVING b.StoreID IS NULL
;
In this example, allIDs is the aforementioned "possible values" table.

How to PHP Explode in MYSQL and return an array?

Suppose I have a string....
$string = "This, Cat, Likes, Lasagna";
Using MYSQL only, would it be possible to get an array result like
"This" "Cat" "Likes" "Lasagna".
Basically, exactly like if I was to do
$result = explode('/',$string);
Just in MYSQL only? I've seen other examples, but they seem to only return the first result. I need all, in an array format.
Ideally, I'd like to avoid creating new MYSQL functions, and just run it as a independent query.
So far, I've got something like...but this doesn't quite work.
SELECT SUBSTRING_INDEX( 'This, Cat, Likes, Lasagna', ',', 1 ) as part1,
SUBSTRING_INDEX( 'This, Cat, Likes, Lasagna', ',', 2 ) as part2,
SUBSTRING_INDEX( 'This, Cat, Likes, Lasagna', ',', 3 ) as part3,
SUBSTRING_INDEX( 'This, Cat, Likes, Lasagna', ',', 4 ) as part4,
SUBSTRING_INDEX( 'This, Cat, Likes, Lasagna', ',', 5 ) as part5
try this..
SET #field = 'This, Cat, Likes, Lasagna'; -- your field
SELECT
SUBSTRING_INDEX(SUBSTRING_INDEX(#field, ',', -5),',',1) as part1,
SUBSTRING_INDEX(SUBSTRING_INDEX(#field, ',', -4),',',1) as part2,
SUBSTRING_INDEX(SUBSTRING_INDEX(#field, ',', -3),',',1) as part3,
SUBSTRING_INDEX(SUBSTRING_INDEX(#field, ',', -2),',',1) as part4,
SUBSTRING_INDEX(#field, ',', -1) as part5;

SQL GROUP_CONCAT split in different columns

I searched a lot, but didn't find a proper solution to my problem.
What do I want to do?
I have 2 tables in MySQL:
- Country
- Currency
(I join them together via CountryCurrency --> due to many to many relationship)
See this for a working example: http://sqlfiddle.com/#!2/317d3/8/0
I want to link both tables together using a join, but I want to show just one row per country (some countries have multiple currencies, so that was the first problem).
I found the group_concat function:
SELECT country.Name, country.ISOCode_2, group_concat(currency.name) AS currency
FROM country
INNER JOIN countryCurrency ON country.country_id = countryCurrency.country_id
INNER JOIN currency ON currency.currency_id = countryCurrency.currency_id
GROUP BY country.name
This has the following result:
NAME ISOCODE_2 CURRENCY
Afghanistan AF Afghani
Ă…land Islands AX Euro
Albania AL Lek
Algeria DZ Algerian Dinar
American Samoa AS US Dollar,Kwanza,East Caribbean Dollar
But what I want now is to split the currencies in different columns (currency 1, currency 2, ...). I already tried functions like MAKE_SET() but this doesn't work.
You can do this with substring_index(). The following query uses yours as a subquery and then applies this logic:
select Name, ISOCode_2,
substring_index(currencies, ',', 1) as Currency1,
(case when numc >= 2 then substring_index(substring_index(currencies, ',', 2), ',', -1) end) as Currency2,
(case when numc >= 3 then substring_index(substring_index(currencies, ',', 3), ',', -1) end) as Currency3,
(case when numc >= 4 then substring_index(substring_index(currencies, ',', 4), ',', -1) end) as Currency4,
(case when numc >= 5 then substring_index(substring_index(currencies, ',', 5), ',', -1) end) as Currency5,
(case when numc >= 6 then substring_index(substring_index(currencies, ',', 6), ',', -1) end) as Currency6,
(case when numc >= 7 then substring_index(substring_index(currencies, ',', 7), ',', -1) end) as Currency7,
(case when numc >= 8 then substring_index(substring_index(currencies, ',', 8), ',', -1) end) as Currency8
from (SELECT country.Name, country.ISOCode_2, group_concat(currency.name) AS currencies,
count(*) as numc
FROM country
INNER JOIN countryCurrency ON country.country_id = countryCurrency.country_id
INNER JOIN currency ON currency.currency_id = countryCurrency.currency_id
GROUP BY country.name
) t
The expression substring_index(currencies, ',' 2) takes the list in currencies up to the second one. For American Somoa, that would be 'US Dollar,Kwanza'. The next call with -1 as the argument takes the last element of the list, which would be 'Kwanza', which is the second element of currencies.
Also note that SQL queries return a well-defined set of columns. A query cannot have a variable number of columns (unless you are using dynamic SQL through a prepare statement).
Use this query to work out the number of currency columns you'll need:
SELECT MAX(c) FROM
((SELECT count(currency.name) AS c
FROM country
INNER JOIN countryCurrency ON country.country_id = countryCurrency.country_id
INNER JOIN currency ON currency.currency_id = countryCurrency.currency_id
GROUP BY country.name) as t)
Then dynamically create and execute prepared statement to generate the result, using Gordon Linoff solution with query result above to in this thread.
Ypu can use dynamic SQL, but you will have to use procedure