The hard workaround without using regex, this post is just meant to show it's possible.
The trick is to make a MySQL number generator and use nested SUBSTRING_INDEX()
functions to cut the strings up into tokens.
Query
SELECT
separated_key_values.query
, SUBSTRING_INDEX(
SUBSTRING_INDEX(
separated_key_values.separated_property
, '='
, 1
)
,'='
, -1
) AS property_key
, SUBSTRING_INDEX(
SUBSTRING_INDEX(
separated_key_values.separated_property
, '='
, 2
)
,'='
, -1
) AS property_value
FROM (
SELECT
DISTINCT
search.query
, SUBSTRING_INDEX(
SUBSTRING_INDEX(
search.query
, '&'
, number_generator.row_number
)
,'&'
, -1
) separated_property
FROM (
SELECT
@row := @row + 1 AS row_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
) AS init_user_params
) AS number_generator
CROSS JOIN
search
) AS separated_key_values
ORDER BY
separated_key_values.query ASC
Result
| query | property_key | property_value |
| ------------------------------------------ | ------------ | -------------- |
| city=1&propertyType=HOUSE&serviceType=SALE | city | 1 |
| city=1&propertyType=HOUSE&serviceType=SALE | serviceType | SALE |
| city=1&propertyType=HOUSE&serviceType=SALE | propertyType | HOUSE |
| city=1&serviceType=SALE&propertyType=HOUSE | city | 1 |
| city=1&serviceType=SALE&propertyType=HOUSE | propertyType | HOUSE |
| city=1&serviceType=SALE&propertyType=HOUSE | serviceType | SALE |
| city=2&propertyType=HOUSE&serviceType=SALE | serviceType | SALE |
| city=2&propertyType=HOUSE&serviceType=SALE | city | 2 |
| city=2&propertyType=HOUSE&serviceType=SALE | propertyType | HOUSE |
| propertyType=HOUSE&city=2&serviceType=SALE | city | 2 |
| propertyType=HOUSE&city=2&serviceType=SALE | serviceType | SALE |
| propertyType=HOUSE&city=2&serviceType=SALE | propertyType | HOUSE |
| propertyType=HOUSE&serviceType=SALE&city=1 | city | 1 |
| propertyType=HOUSE&serviceType=SALE&city=1 | serviceType | SALE |
| propertyType=HOUSE&serviceType=SALE&city=1 | propertyType | HOUSE |
| serviceType=RENTAL | serviceType | RENTAL |
| serviceType=RENTAL&propertyType=HOUSE | propertyType | HOUSE |
| serviceType=RENTAL&propertyType=HOUSE | serviceType | RENTAL |
| serviceType=SALE | serviceType | SALE |
| serviceType=SALE&propertyType=FARM&city=1 | serviceType | SALE |
| serviceType=SALE&propertyType=FARM&city=1 | propertyType | FARM |
| serviceType=SALE&propertyType=FARM&city=1 | city | 1 |
| serviceType=SALE&propertyType=HOUSE | propertyType | HOUSE |
| serviceType=SALE&propertyType=HOUSE | serviceType | SALE |
| serviceType=SALE&propertyType=HOUSE&city=1 | city | 1 |
| serviceType=SALE&propertyType=HOUSE&city=1 | propertyType | HOUSE |
| serviceType=SALE&propertyType=HOUSE&city=1 | serviceType | SALE |
| serviceType=SALE&propertyType=HOUSE&city=2 | propertyType | HOUSE |
| serviceType=SALE&propertyType=HOUSE&city=2 | city | 2 |
| serviceType=SALE&propertyType=HOUSE&city=2 | serviceType | SALE |
| serviceType=SALE&propertyType=UNIT | propertyType | UNIT |
| serviceType=SALE&propertyType=UNIT | serviceType | SALE |
see demo
After that it just as simple as adding as conditional aggregation.
Query
SELECT
separated_key_values.query
,
(
SUM(separated_key_values.property_key = 'serviceType') > 0
AND
SUM(separated_key_values.property_value = 'SALE') > 0
) AS has_serviceType_SALE
,
(
SUM(separated_key_values.property_key = 'propertyType') > 0
AND
SUM(separated_key_values.property_value = 'HOUSE') > 0
) AS has_propertyType_HOUSE
,
(
SUM(separated_key_values.property_key = 'City') > 0
AND
SUM(separated_key_values.property_value = '1') > 0
) AS has_City_1
, (
(
SUM(separated_key_values.property_key = 'serviceType') > 0
AND
SUM(separated_key_values.property_value = 'SALE') > 0
)
+
(
SUM(separated_key_values.property_key = 'propertyType') > 0
AND
SUM(separated_key_values.property_value = 'HOUSE') > 0
)
+
(
SUM(separated_key_values.property_key = 'City') > 0
AND
SUM(separated_key_values.property_value = '1') > 0
)
) AS has_mask
, COUNT(*)
FROM (
SELECT
search_alias.query
, SUBSTRING_INDEX(
SUBSTRING_INDEX(
search_alias.separated_property
, '='
, 1
)
,'='
, -1
) AS property_key
, SUBSTRING_INDEX(
SUBSTRING_INDEX(
search_alias.separated_property
, '='
, 2
)
,'='
, -1
) AS property_value
FROM (
SELECT
DISTINCT
search.query
, SUBSTRING_INDEX(
SUBSTRING_INDEX(
search.query
, '&'
, number_generator.row_number
)
,'&'
, -1
) separated_property
FROM (
SELECT
@row := @row + 1 AS row_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
) AS init_user_params
) AS number_generator
CROSS JOIN
search
) AS search_alias
) AS separated_key_values
GROUP BY
separated_key_values.query
HAVING
has_mask = COUNT(*)
Result
| query | has_serviceType_SALE | has_propertyType_HOUSE | has_City_1 | has_mask | COUNT(*) |
| ------------------------------------------ | -------------------- | ---------------------- | ---------- | -------- | -------- |
| city=1&propertyType=HOUSE&serviceType=SALE | 1 | 1 | 1 | 3 | 3 |
| city=1&serviceType=SALE&propertyType=HOUSE | 1 | 1 | 1 | 3 | 3 |
| propertyType=HOUSE&serviceType=SALE&city=1 | 1 | 1 | 1 | 3 | 3 |
| serviceType=SALE | 1 | 0 | 0 | 1 | 1 |
| serviceType=SALE&propertyType=HOUSE | 1 | 1 | 0 | 2 | 2 |
| serviceType=SALE&propertyType=HOUSE&city=1 | 1 | 1 | 1 | 3 | 3 |
see demo
You could also place the column output into the HAVING
clause so you don't output those columns.
See demo
Note
This will not scale on large tables most likely the regex query will also not scale because indexes most likely can't be used.
A workaround method might be to use a temporary table with a correct index and use the first query to prefill and do the conditional aggregation on the temporary table which is indexed.