Related
We have requirement in Mysql query, to find with partial string from list of comma separated string. Then need to remove a found a string from comma separated list.
As per the below example, we need to find a string starting with "Pending" then need to remove found string from the comma separated list through Mysql query.
|-------------------|-----------------------------------------|
| Id | Tag |
|-------------------|-----------------------------------------|
| 1 | Completed, #4CHD, Pending with ABC |
|-------------------|-----------------------------------------|
| 2 | Open, Pending with Mrg, #4CHD |
|-------------------|-----------------------------------------|
| 3 | Pending with cons, Resolved |
|-------------------|-----------------------------------------|
Output should be:
|-------------------|-----------------------|
| Id | Tag |
|-------------------|-----------------------|
| 1 | Completed, #4CHD |
|-------------------|-----------------------|
| 2 | Open, #4CHD |
|-------------------|-----------------------|
| 3 | Resolved |
|-------------------|-----------------------|
For MySQL 8+
SELECT test.id, GROUP_CONCAT(TRIM(jsontable.value)) Tag
FROM test
CROSS JOIN JSON_TABLE(CONCAT('["', REPLACE(test.Tag, ',', '","'), '"]'),
'$[*]' COLUMNS( value VARCHAR(255) PATH '$')
) jsontable
WHERE TRIM(jsontable.value) NOT LIKE CONCAT(#criteria, '%')
GROUP BY test.id
For MySQL 5.x
SELECT test.id,
GROUP_CONCAT(TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(test.Tag, ',', nums.num), ',', -1))) Tag
FROM test
JOIN (SELECT 1 num UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) nums
ON nums.num <= 1 + LENGTH(test.Tag) - LENGTH(REPLACE(test.Tag, ',', ''))
WHERE TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(test.Tag, ',', nums.num), ',', -1)) NOT LIKE CONCAT(#criteria, '%')
GROUP BY test.id
fiddle
No rows should be ingnored in output. Let us assume 3rd row has Tag value as "Pending with cons" alone. I this case first two is getting displayed in output and 3rd row is ignored. My requirement is 3rd also should be displayed with empty Tag.
If so LEFT JOIN (and moving condition expression from WHERE to ON) needed:
SELECT test.id,
GROUP_CONCAT(TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(test.Tag, ',', nums.num), ',', -1))) Tag
FROM test
LEFT JOIN (SELECT 1 num UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) nums
ON nums.num <= 1 + LENGTH(test.Tag) - LENGTH(REPLACE(test.Tag, ',', ''))
AND TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(test.Tag, ',', nums.num), ',', -1)) NOT LIKE CONCAT(#criteria, '%')
GROUP BY test.id
fiddle
I'm trying to make a link between 2 tables on mySQL but, i think it's a little bit harder than i thought.
I have 3 tables
* One which registers my rules informations
* One which registers my transfers informations
* One which make the pivot between the two first.
CREATE TABLE `rules` (
`id` int,
`Name` varchar(10)
);
INSERT INTO `rules` (`id`, `name`) VALUES
(1,'a'),
(2,'b'),
(3,'c'),
(4,'d');
CREATE TABLE `pivot` (
`id_rule` int,
`id_transfert` int
);
INSERT INTO `pivot` (`id_rule`, `id_transfert`) VALUES
(1,1),
(1,2),
(2,1),
(2,2),
(2,3);
CREATE TABLE `transferts` (
`id` int,
`aeroport` varchar(50),
`station` varchar(50)
);
INSERT INTO `transferts` (`id`, `aeroport`,`station`) VALUES
(1,'GVA','Flaine'),
(2,'GNB','La Tania'),
(3,'GNB','Flaine');
What i'm trying to do is to get all my rules with a column which gather all linked transfers as a JSON string. Like below
------------------------------------------------------------------------
| id | name | transferts |
------------------------------------------------------------------------
| 1 | a | {"GVA": "Flaine"} |
------------------------------------------------------------------------
| 2 | b | {"GVA": "Flaine", "GNB": "Flaine", "La Tania"} |
------------------------------------------------------------------------
What i do actually is this :
SELECT
rule.id, rule.name,GROUP_CONCAT(stations.transferts SEPARATOR ",") as transferts
FROM
rules rule
LEFT OUTER JOIN
pivot pivot
on
(pivot.id_rule = rule.id)
LEFT OUTER JOIN
(
SELECT id,
CONCAT(aeroport, ":",
GROUP_CONCAT(station)
) AS transferts
FROM transferts
GROUP BY aeroport
) stations
on
(pivot.id_transfert = stations.id)
GROUP BY
rule.id
But this is returning me a "null" value. I don't see what i'm doing wrong.
Is there someone who can help me please ?
FYI, I was inspired by this link
MySQL: GROUP_CONCAT with LEFT JOIN
With a MySQL version prior to 5.7.22 you can't use the JSON built-in functions.
You'll have to use a few imbricated GROUP_CONCAT subqueries to obtain your JSON string.
As told in the comments, your expected JSON string is not valid. The following answer will differ from your expected result, to fix this issue.
I suggest you proceed with a first query to get a column with the "aeroport" names, and another column with the associated stations formatted as a list, for each couple of "rule.id + aeroport_name".
This gives the following query:
mysql> select rules.id, name, concat ('"', aeroport, '":') as aeroport_name, group_concat('"', station, '"') as station_list
-> from rules
-> inner join pivot on rules.id = pivot.id_rule
-> inner join transferts on pivot.id_transfert = transferts.id
-> group by rules.id, aeroport_name;
+------+------+---------------+---------------------+
| id | name | aeroport_name | station_list |
+------+------+---------------+---------------------+
| 1 | a | "GNB": | "La Tania" |
| 1 | a | "GVA": | "Flaine" |
| 2 | b | "GNB": | "La Tania","Flaine" |
| 2 | b | "GVA": | "Flaine" |
+------+------+---------------+---------------------+
4 rows in set (0,00 sec)
Then, we are going to use this query as a subquery to associate each "station_list" to its given aeroport, in a rule id context, within a single string.
This give the following encapsulation:
mysql> select id, name, group_concat(aeroport_name, '[', station_list, ']') as aeroport_list
-> from (
-> select rules.id, name, concat ('"', aeroport, '":') as aeroport_name, group_concat('"', station, '"') as station_list
-> from rules
-> inner join pivot on rules.id = pivot.id_rule
-> inner join transferts on pivot.id_transfert = transferts.id
-> group by rules.id, aeroport_name
-> ) as isolated group by id;
+------+------+----------------------------------------------+
| id | name | aeroport_list |
+------+------+----------------------------------------------+
| 1 | a | "GNB":["La Tania"],"GVA":["Flaine"] |
| 2 | b | "GNB":["La Tania","Flaine"],"GVA":["Flaine"] |
+------+------+----------------------------------------------+
2 rows in set (0,00 sec)
And finally, we can now add the final "{}" encapsulation to our string, by adding a top level query over this:
mysql> select id, name, concat('{', aeroport_list, '}') as conf
-> from (
-> select id, name, group_concat(aeroport_name, '[', station_list, ']') as aeroport_list
-> from (
-> select rules.id, name, concat ('"', aeroport, '":') as aeroport_name, group_concat('"', station, '"') as station_list
-> from rules
-> inner join pivot on rules.id = pivot.id_rule
-> inner join transferts on pivot.id_transfert = transferts.id
-> group by rules.id, aeroport_name
-> ) as isolated group by id
-> ) as full_list;
+------+------+------------------------------------------------+
| id | name | conf |
+------+------+------------------------------------------------+
| 1 | a | {"GNB":["La Tania"],"GVA":["Flaine"]} |
| 2 | b | {"GNB":["Flaine","La Tania"],"GVA":["Flaine"]} |
+------+------+------------------------------------------------+
2 rows in set (0,01 sec)
Is this possible with one statement?
Multiple tables in my database.
Using this SELECT to retrieve an array of sub-sku's (aka product listing sku's)
SELECT SKU FROM MasterSKU WHERE '4048' IN (AltSKU, SKU_1, SKU_2, SKU_3);
Returns
+-------------------+
| SKU |
+-------------------+
| 4048 |
| 4048-SET-15 |
| 4699-4528-4048-EA |
+-------------------+
The actual SKU is 4048 and the result set is all the product listing sku's connected to this one sku.
These SKU's are located in a few other tables and generated by an eCommerce API.
This is my second SELECT statement to return where (what channel) the SKUs are located and the associated ItemID.
(SELECT 'store_1' as channel, SKU, ItemID FROM Listings_store1 WHERE SKU = '4048')
UNION
(SELECT 'store_2', SKU, ItemID FROM Listings_store2 WHERE SKU = '4048')
UNION
(SELECT 'store_3', SKU, ItemID FROM Listings_store3 WHERE SKU = '4048')
UNION
(SELECT 'store_4', SKU, ItemID FROM Listings_store4 WHERE SKU = '4048');
This results in
+----------+-------------------+---------------+
| channel | SKU | ItemID |
+----------+-------------------+---------------+
| store_1 | 4048 | 5654515256454 |
| store_1 | 4048-SET-15 | 5654515234536 |
| store_3 | 4699-4528-4048-EA | 5654515243553 |
+----------+-------------------+---------------+
How I'm doing it now is in PHP to turn the first SELECT statement into an array, and then looping the results to retrieve the results. Something like for ($i = 0; $i < $channelskucount; $i++) { $channeldetectquery = "(SELECT 'store_1' as source, SKU FROM Listings_store1 WHERE SKU = '$channelskuarray[$i]') . . .
Is it possible, or feasible, to merge these two queries? As the first select statement normally acts as an array I loop over with the second in PHP, I'm not sure where to begin.
End output something like
+--------------+----------+-------------------+---------------+
| InventorySKU | channel | SKU | ItemID |
+--------------+----------+-------------------+---------------+
| 4048 | store_1 | 4048 | 5654515256454 |
| 4048 | store_1 | 4048-SET-15 | 5654515234536 |
| 4048 | store_3 | 4699-4528-4048-EA | 5654515243553 |
+--------------+----------+-------------------+---------------+
Any tips/help really appreciated. I realize my first table isn't exactly normalized, but trying to finalize my first big database project so I've learned and am learning a lot along the way.
It is certainly possible [to do it in one query] if you do a minor, and much needed, restructuring of your data. In fact, your issue perfectly illuminates the problem with the current structure. The listings for the different stores shouldn't each have their own data table. There should be a 'store' table which has columns for an id key, store name, store description, address, etc. Then you would just have a single 'Listings' table with one additional column, the look-up (a foreign key relationship) to the id key of the 'store' table, followed by the rest of whatever is already there (sku, ItemID, etc.). Shouldn't be hard to do, and you'll only have to do it once. You could create the table and just brute force it:
INSERT into Listings (StoreID, SKU, ItemID,...)
SELECT 1, SKU, ItemID,... FROM Listings1
INSERT into Listings (StoreID, SKU, ItemID,...)
SELECT 2, SKU, ItemID,... FROM Listings2
...
Or do it the inexcusably lazy way for which I should be severely reprimanded for even mentioning (you should definitely double-check your column data types afterward and add indexes):
SELECT 1 as StoreID, *
INTO Listings
FROM Listings1
UNION
SELECT 2, *
UNION
SELECT 3, *
UNION
SELECT 4, *
...
You then simply JOIN this table to the master_sku and store tables... Like this:
DECLARE #InventorySKU as varchar(20)
SET #InventorySKU = '4084'
SELECT #InventorySKU as InventorySKU, s.StoreName as channel, l.SKU, l.ItemID
FROM Listings l
INNER JOIN MasterSKU ms on ms.SKU = l.SKU
INNER JOIN Stores s on s.ID = l.StoreID
WHERE
ms.AltSKU LIKE '%' + #InventorySKU + '%' OR
ms.SKU_1 LIKE '%' + #InventorySKU + '%' OR
ms.SKU_2 LIKE '%' + #InventorySKU + '%' OR
ms.SKU_3 LIKE '%' + #InventorySKU + '%'
I hope this helps.
I have this table:
it produces by this query SELECT DISTINCT code, tariff_diff FROM mytable
.
Now, I want to update tariff_diff = 1 if code appear more than 1. (as example, I want to update tariff_diff = 1 where row Kuta,DPS50xxx)
I have tried :
update mytable SET tariff_diff = 1
WHERE in(select distinct code, tariff_diff from mytable)
But i am getting error syntax.
Operand should contain 1 column
If you want to alter the all the rows with same code you can use this.
UPDATE mytable SET mytable.tariff_diff = 1 WHERE mytable.code IN(SELECT count(*), code, tariff_diff from mytable GROUP BY code HAVING count(*)>1)
It is not possible to use same update table in select statement in subquery , you can find the reason in this link: Reason for not use same table in sub-query.
try below query:
SET #r_code = (select code from mytable GROUP BY code having count(code) > 1);
update mytable SET tariff_diff = 1 WHERE code in (#r_code);
You can find more about variable here in this link.More about Variables.
First of all store the id's into the some variable and then update those id's using in query.
From what I understand, you're wanting to set the tariff_diff to 1 only if more than one of the rows that are prefixed with Kuta,DPS50. exist. Matching on Kuta,DPS50.06, Kuta,DPS50.07, Kuta,DPS50.08, Kuta,DPS50.09, Kuta,DPS50.10.
Assuming all of your records are formatted like: XXX,xxx.###. You can use SUBSTRING_INDEX to parse the prefixed text (Kuta,DPS50.) to use as an identifier.
Then you can use a derived JOIN to match the codes that have duplicates of the prefixed values and update the matching rows.
If there are no duplicate values, no update will occur.
Example: http://sqlfiddle.com/#!9/658034/1 (I added an additional entry for Petang,DPS50.02 to demonstrate it works on other prefixed values.)
Query:
UPDATE mytable AS p
JOIN (
SELECT SUBSTRING_INDEX(code, '.', 1) AS prefix_code
FROM mytable
GROUP BY prefix_code
HAVING COUNT(prefix_code) > 1
) AS c
ON c.prefix_code = SUBSTRING_INDEX(p.code, '.', 1)
SET p.tariff_diff = 1;
Result:
| code | tariff_diff |
|-----------------------|-------------|
| Abiansemal,DPS50.02 | 0 |
| Kuta,DPS50.06 | 1 |
| Kuta,DPS50.07 | 1 |
| Kuta,DPS50.08 | 1 |
| Kuta,DPS50.09 | 1 |
| Kuta,DPS50.10 | 1 |
| Kuta Selatan,DPS50.05 | 0 |
| Kuta Ultara,DPS50.04 | 0 |
| Mengwi,DPS50.01 | 0 |
| Petang,DPS50.02 | 1 |
| Petang,DPS50.03 | 1 |
This will also avoid the SQL Error (1093) https://dev.mysql.com/doc/refman/5.6/en/update.html
You cannot update a table and select from the same table in a subquery.
I have 3 tables: questions, options, comments_to_options(opt_comments).
I want to write a query that returns in each row the following values, concatenated:
A question, all options to it, all comments to each option.
My query is:
select
concat('{', '"qid":"', q.q_id, '", "qt":"', q.q_title,
'", "op":[', group_concat('{"oi":"', o.op_id, '", "ot":"', o.opt_value, '", ', oc_list, '}'
order by o.opt_upvotes desc), ']}')
as r
from questions q, options o,
(select o.op_id as ocid, concat('"oc":[', group_concat('{"oci":"', oc.opt_com_id, '", "occ":"', oc.opt_com_value, '"}'
order by oc.opt_com_added_at), ']')
as oc_list
from options o, opt_comments oc
where oc.opt_com_to=o.op_id
group by o.op_id)
as r2
where o.op_id=r2.ocid
and q.q_id=o.option_to
group by q.q_id
order by q.q_added_at desc
limit 3;
But the above query gives only those options that have at least one comment to them.
How should I modify?
You are using the old JOIN syntax with comma-separated lists of tables and subqueries. That syntax is correct, but generates INNER JOIN operations. Such joins suppress rows that don't match the join criterion.
You need to adopt the LEFT JOIN syntax. Without refactoring your entire query, I will say that you should change
FROM a,
(select something from z) AS b
WHERE a.value=b.value
to
FROM a
LEFT JOIN (select something from z) AS b ON a.value=b.value
Also, beware, you may encounter the character-string length limit in GROUP_CONCAT(). Read this:
MySQL and GROUP_CONCAT() maximum length
Use "left join".
Example:
create table opt (oid int,name varchar(100));
insert into opt values (1,'opt1');
insert into opt values (2,'opt2');
insert into opt values (3,'opt3');
create table optcom (oid int,com varchar(100));
insert into optcom values (1,'opt1_1');
insert into optcom values (1,'opt1_2');
insert into optcom values (3,'opt3_1');
When using "simple join":
select opt.*,optcom.* from opt join optcom on opt.oid=optcom.oid;
+------+------+------+--------+
| oid | name | oid | com |
+------+------+------+--------+
| 1 | opt1 | 1 | opt1_1 |
| 1 | opt1 | 1 | opt1_2 |
| 3 | opt3 | 3 | opt3_1 |
+------+------+------+--------+
When "left join":
select opt.*,optcom.* from opt left join optcom on opt.oid=optcom.oid;
+------+------+------+--------+
| oid | name | oid | com |
+------+------+------+--------+
| 1 | opt1 | 1 | opt1_1 |
| 1 | opt1 | 1 | opt1_2 |
| 2 | opt2 | NULL | NULL |
| 3 | opt3 | 3 | opt3_1 |
+------+------+------+--------+
To follow up on the above responses, the SQL amended to use outer joins:-
SELECT CONCAT('{', '"qid":"', q.q_id, '", "qt":"', q.q_title,'", "op":[', GROUP_CONCAT('{"oi":"', o.op_id, '", "ot":"', o.opt_value, '", ', oc_list, '}' ORDER BY o.opt_upvotes DESC), ']}') AS r
FROM options o
LEFT OUTER JOIN questions q
ON q.q_id = o.option_to
LEFT OUTER JOIN
(
SELECT o.op_id AS ocid,
CONCAT('"oc":[', GROUP_CONCAT('{"oci":"', oc.opt_com_id, '", "occ":"', oc.opt_com_value, '"}' ORDER BY oc.opt_com_added_at), ']') AS oc_list
FROM options o
INNER JOIN opt_comments oc
ON oc.opt_com_to=o.op_id
GROUP BY o.op_id
) r2
ON o.op_id = r2.ocid
GROUP BY q.q_id
ORDER BY q.q_added_at DESC
LIMIT 3;
Looking at this I am unsure about the join to the sub query. This appears to be bringing back an encoded string, but beyond the actual join nothing from this sub query is actually used.
As such I am unsure if that sub query is just being used to narrow down the rows returned (in which case joining against it using an INNER JOIN would be appropriate - and you may as well not bring back the encoded string), or if you have posted a cut down version of the query that you have been trying to debug.