I'm using a SQL Server database. I have a table which has a field that stores the following JSON file structure:
{
"Internal":[
{
"GUID":"7c2fc2b3-7ae2-42c5-b6f0-13137f58348c",
"Type":1,
"ID":155
},
{
"GUID":"8774cbcb-e594-4c64-8ecb-b71d4f97cea4",
"Type":2,
"Link":134
}
],
"External":[
{
"GUID":"be41536b-33ea-4e12-8a11-544aa15c1edb",
"Type":2,
"Link":174
},
{
"GUID":"49383921-5bd8-4cf9-a104-67f957b7fdc7",
"Type":1,
"Link":202
}
]
}
My problem is finding the GUID value. How do I find the value of Link (202), if I know that the GUID I'm looking for is 49383921-5bd8-4cf9-a104-67f957b7fdc7?
I know the solution is to use the CROSS APPLY but I can't come to a solution. I looked through a few threads in the forum, but after a few hours I still can't get the correct query, so I am posting my question on the forum.
Here is my initial query which selects a JSON structure from the database:
SELECT m.Formulardata -- here is a JSON structure
FROM [dbo].[Vorgaenge] v
LEFT JOIN [dbo].[Vorgangsschritte] s ON v.id = s.Vorgang_ID
LEFT JOIN [dbo].[Vorgangsmassnahmen] m ON s.id = m.Vorgangsschritt_ID
WHERE m.Vorgangsmassnahme_ID = 21 AND v.id = #Vorgang_ID
Please try the following solution.
SQL #1 is a minimal reproducible example.
SQL #2 is close as much as possible to your environment.
SQL #1
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, Formulardata NVARCHAR(MAX));
INSERT INTO #tbl (Formulardata) VALUES
(N'{
"Internal":[
{
"GUID":"7c2fc2b3-7ae2-42c5-b6f0-13137f58348c",
"Type":1,
"ID":155
},
{
"GUID":"8774cbcb-e594-4c64-8ecb-b71d4f97cea4",
"Type":2,
"Link":134
}
],
"External":[
{
"GUID":"be41536b-33ea-4e12-8a11-544aa15c1edb",
"Type":2,
"Link":174
},
{
"GUID":"49383921-5bd8-4cf9-a104-67f957b7fdc7",
"Type":1,
"Link":202
}
]
}');
-- DDL and sample data population, end
SELECT ID, u.*
FROM #tbl
CROSS APPLY OPENJSON(Formulardata) x
CROSS APPLY OPENJSON(x.Value)
WITH
(
GUID UNIQUEIDENTIFIER '$.GUID',
Type INT '$.Type',
Link INT '$.Link'
) AS u
-- WHERE u.Link = 202;
SQL #2
;WITH rs AS
(
SELECT m.Formulardata -- here is a JSON structure
FROM [dbo].[Vorgaenge] v
LEFT JOIN [dbo].[Vorgangsschritte] s ON v.id = s.Vorgang_ID
LEFT JOIN [dbo].[Vorgangsmassnahmen] m ON s.id = m.Vorgangsschritt_ID
WHERE m.Vorgangsmassnahme_ID = 21 AND v.id = #Vorgang_ID
)
SELECT ID, u.*
FROM rs
CROSS APPLY OPENJSON(Formulardata) x
CROSS APPLY OPENJSON(x.Value)
WITH
(
GUID UNIQUEIDENTIFIER '$.GUID',
Type INT '$.Type',
Link INT '$.Link'
) AS u
WHERE u.Link = 202;
Output
+----+--------------------------------------+------+------+
| ID | GUID | Type | Link |
+----+--------------------------------------+------+------+
| 1 | 7c2fc2b3-7ae2-42c5-b6f0-13137f58348c | 1 | NULL |
| 1 | 8774cbcb-e594-4c64-8ecb-b71d4f97cea4 | 2 | 134 |
| 1 | be41536b-33ea-4e12-8a11-544aa15c1edb | 2 | 174 |
| 1 | 49383921-5bd8-4cf9-a104-67f957b7fdc7 | 1 | 202 |
+----+--------------------------------------+------+------+
I do not have access right now ot a server that I could test it on, but if you know that it is in the 'external' branch then I think that this should work:
SELECT x.Link
FROM [dbo].[Vorgaenge] v
LEFT JOIN [dbo].[Vorgangsschritte] s ON v.id = s.Vorgang_ID
LEFT JOIN [dbo].[Vorgangsmassnahmen] m ON s.id = m.Vorgangsschritt_ID
CROSS APPLY OPENJSON(m.Formulardata)
WITH (
Internal NVARCHAR(MAX) '$.Internal' AS JSON,
Externals NVARCHAR(MAX) '$.External' AS JSON
) AS a
OUTER APPLY OPENJSON(a.Externals)
WITH (
Guid NVARCHAR(MAX) '$.GUID',
Type INT '$.Type',
Link INT '$.Link'
) AS x
WHERE m.Vorgangsmassnahme_ID = 21 AND v.id = #Vorgang_ID
AND ISJSON(m.Formulardata) > 0
AND x.Type = 2
AND x.GUID = '49383921-5bd8-4cf9-a104-67f957b7fdc7'
Related
I have two tables A and B.
Table A:
no name type
1 shoe 1
2 shirt 2
3 book 3
Table B:
type color size
1 red big
2 yellow small
3 blue medium
When I query where A.no === 1 and A.type === 1, I want to get data like:
{
no: 1,
name: 'shoe',
type: 1,
info: {
color: 'red',
size: 'big'
},
}
I tried something like this:
select a.*, b.* from stores a, types b where a.type = 1 and a.type = b.id
and it returns only plain object, I want to get nested data like the above.
I think it can be done using join and doing any other query tricks.
Here's the sql fiddle link I prepared for you.
http://sqlfiddle.com/#!9/3ad910/2
Thanks in advance.
Model TableA:
public function info()
{
return $this->hasOne(TableB::class, 'type', 'type');
}
Model TableB:
public function tableA()
{
return $this->belongsTo(TableA::class, 'type', 'type');
}
The Query:
TableA::with('info')->where(['type' => 1, 'no' => 1])->get();
So the query that you have (although better written using post-1992 query syntax) is all that you need. The rest of the problem is a simple case of rearranging the resulting array. I don't know eloquent/laravel, and I'm embarrassingly bad at rearranging arrays, but here's an example of the kind of thing I mean using plain old php (perhaps someone will be kind enough to write a more apt array transformation)...
<?php
/*
DROP TABLE IF EXISTS table_a;
CREATE TABLE table_a
(no SERIAL PRIMARY KEY
,name VARCHAR(12) UNIQUE
,type INT NOT NULL
);
INSERT INTO table_a VALUES
(1,'shoe',1),
(2,'shirt',2),
(3,'book',3);
DROP TABLE IF EXISTS table_b;
CREATE TABLE table_b
(type SERIAL PRIMARY KEY
,color VARCHAR(12) NOT NULL
,size VARCHAR(12) NOT NULL
);
INSERT INTO table_b VALUES
(1,'red','big'),
(2,'yellow','small'),
(3,'blue','medium');
SELECT a.no
, a.name
, b.*
FROM table_a a
JOIN table_b b
ON b.type = a.type
ORDER
BY a.no;
+----+-------+------+--------+--------+
| no | name | type | color | size |
+----+-------+------+--------+--------+
| 1 | shoe | 1 | red | big |
| 2 | shirt | 2 | yellow | small |
| 3 | book | 3 | blue | medium |
+----+-------+------+--------+--------+
*/
require('path/to/connection/stateme.nts');
$query = "
SELECT a.no
, a.name
, b.type
, b.color
, b.size
FROM table_a a
JOIN table_b b
ON b.type = a.type
WHERE a.no = 1
AND a.type = 1
ORDER
BY a.no;
";
$result = mysqli_query($db,$query) or die(mysqli_error());
$old_array = array();
while($row = mysqli_fetch_assoc($result)){
$old_array[] = $row;
}
$new_array = array();
foreach ($old_array as $row) {
$new_array[]['name'] = $row['name'];
$new_array[$row['no']]['info']['color'] = $row['color'];
$new_array[$row['no']]['info']['size'] = $row['size'];
}
$new_array = array_values($new_array); // reindex
print_r($new_array);
?>
Outputs:
Array
(
[0] => Array
(
[name] => shoe
)
[1] => Array
(
[info] => Array
(
[color] => red
[size] => big
)
)
)
or, json_encoded...
[{"name":"shoe"},{"info":{"color":"red","size":"big"}}]
I'm trying to construct a query where I would get a leaderboard-like result, then get the position on the leaderboard of a specific player, by their ID, and only if the player isHere = 'true' (stored in another table).
Table here:
userID | isHere |
--------------------
2 | true
--------------------
1 | true
--------------------
3 | false
--------------------
4 | true
Table userdata:
id | data |
------------------------------------
2 | {... "points": 5 ...}
------------------------------------
1 | {... "points": 10 ...}
------------------------------------
3 | {... "points": 2 ...}
------------------------------------
4 | {... "points": 28 ...}
Query:
SET
#row_number = 0;
SELECT
*
FROM
(
SELECT
(#row_number := #row_number +1) AS num,
userdata.id,
userdata.data
FROM
userdata
INNER JOIN
here ON userdata.id = here.userID
WHERE
here.isHere = 'true'
ORDER BY
JSON_EXTRACT(userdata.data,
'$.points') DESC
) AS t
WHERE
t.id = 1
This returns that num is 1... because for some reason it's sorting by userID/id. I double checked by setting WHERE t.id = as 2, and instead of returning 3, it returns 2... Does anyone know what's wrong with my query?
Note: I did try having the column data be just a BIGINT with the point value, but the same issue happened. So that eliminates JSON_EXTRACT() having any issues (I think?)
Note 2: Running the inner query on its own still orders by the ID with the num column, but displays in PHPMyAdmin in the correct order (Screenshot: https://gyazo.com/73177e79f4fedd4ec7e09ea0e70a9d2b)
So here's the query that works:
SET
#row_number = 0;
SELECT
*
FROM
(
SELECT
(#row_number := #row_number +1) AS num,
userdata.id,
userdata.data
FROM
userdata
INNER JOIN
(
SELECT
userdata.id,
userdata.data
FROM
userdata
INNER JOIN
here ON userdata.id = here.userID
WHERE
here.isHere = 'true'
ORDER BY
JSON_EXTRACT(userdata.data,
'$.points') DESC
) AS t ON userdata.id = t.id
) AS t2
WHERE
id = ?
The only reason all of this is needed, is due to the order in which MySQL executes parts of queries. Just like parenthesis are used in math in order of operations, I used a SELECT statement here.
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)
I am trying to populate ElasticSearch with a collection of documents along with a field representing the path to the document based on its parents.
Here is my table layout:
+----+--------+-------+----------+
| Id | Parent | Alias | Contents |
+----+--------+-------+----------+
| 1 | null | Doc1 | Admin |
| 2 | 1 | Doc2 | Use |
| 3 | 2 | Doc3 | Test |
| 4 | 3 | Doc4 | Ask |
| 5 | null | PDF1 | Intro |
| 6 | 5 | PDF2 | Managers |
+----+--------+-------+----------+
Here is the desired output
+----+--------+-------+----------+---------------------+
| Id | Parent | Alias | Contents | Path |
+----+--------+-------+----------+---------------------+
| 1 | null | Doc1 | Admin | Doc1 |
| 2 | 1 | Doc2 | Use | Doc1\Doc2 |
| 3 | 2 | Doc3 | Test | Doc1\Doc2\Doc3 |
| 4 | 3 | Doc4 | Ask | Doc1\Doc2\Doc3\Doc4 |
| 5 | null | PDF1 | Intro | PDF1 |
| 6 | 5 | PDF2 | Managers | PDF1\PDF2 |
+----+--------+-------+----------+---------------------+
I have this query that gets the Path of one document specified by the parameter #child; (aka SET #child = 5; )
SELECT
T2.*
FROM
(SELECT
#r AS _id,
(SELECT
#r:=Parent
FROM
documents
WHERE
id = _id) AS ParentId,
#l:=#l + 1 AS lvl
FROM
(SELECT #r:=#child, #l:=#parent) vars, documents
WHERE
#r <> 0) T1
JOIN
documents T2 ON T1._id = T2.Id
ORDER BY T2.Parent
The problem being is how do I set #child if I put this into a subquery? I have tried GROUP_CONCAT() but it always ends up being the same path for every line. I have tried putting the Id of the current row in subquery but it throws an error: ErrorCode: 1109. Unknown table 'doc' in field list in the following query
SELECT doc.*, (
SELECT GROUP_CONCAT(a.Alias) FROM (SELECT
T2.*
FROM
(SELECT
#r AS _id,
(SELECT
#r:=Parent
FROM
documents
WHERE
id = _id) AS ParentId,
#l:=#l + 1 AS lvl
FROM
(SELECT #r:= doc.Id, #l:=#parent) vars, documents
WHERE
#r <> 0) T1
JOIN
documents T2 ON T1._id = T2.Id
ORDER BY T1.lvl DESC) a
) as Path FROM documents doc
What am I doing wrong? Is there a better way to do this that I'm not seeing?
Though it is not entirely relevant, I will point out, I'm using a logstash script to load the documents into ElasticSearch from my database on a schedule. Also for multiplicities sake I have taken out the majority of the columns as well as the contents and replaced with faux contents.
You get your error because you cannot use an outer variable in a derived table. A derived table is basically every "subquery" for which you have to use an alias, like vars in your case. Try removing that alias, and MySQL will tell you that every derived table has to have an alias.
One way to solve this is to move your whole query into a function, e.g. getpath(child_id int), where you can then freely use this variable whereever you want (assuming you have a working query that can get the path for one specific child, "something with GROUP_CONCAT()").
But in your case, it is actually possible to reorganize your code so you do not need a derived table:
select d.*, t3.path
from (
SELECT t1.id,
group_concat(t2.alias order by t1.rownum desc separator '\\' ) as path
from (
SELECT
current_child.id,
lvls.rownum,
#r := if(lvls.rownum = 1, current_child.id, #r) AS _id,
(SELECT #r:=Parent
FROM documents
WHERE id = _id) AS ParentId
FROM (select #rownum:= #rownum+1 as rownum
from documents, -- maybe add limit 5
(select #rownum := 0) vars
) as lvls
-- or use:
-- (select 1 as rownum union select 2 union select 3
-- union select 4 union select 5) as lvls
straight_join documents as current_child
) as t1
join documents t2
on t2.id = t1._id
group by t1.id
) t3
join documents d
on d.id = t3.id;
I used your inner documents the same way as you did, which is actually quite inefficient and is only used to support an unlimited tree depth. If you know your max dependency level, you could use the alternative code for lvls I added as a comment (which is just a list of numbers) or the limit.
Make sure to set the group_concat_max_len-setting to an appropriate value (with e.g. set session group_concat_max_len = 20000;). By default, it supports a length of 1024, which will usually be enough, but for long aliases or really deep trees you might reach it - and since it will give you neither an error nor a warning, it is sometimes hard to diagnose, so be aware of it.
There is a more straight forward way to solve your problem. It requires you to know the maximum depth of your tree though, but if you do, you can simply join your parents to every child.
select child.*,
concat_ws('\\',p4.Alias,p3.Alias,p2.Alias,p1.Alias,child.Alias) as path
from documents child
left join documents p1 on child.parent = p1.id
left join documents p2 on p1.parent = p2.id
left join documents p3 on p2.parent = p3.id
left join documents p4 on p3.parent = p4.id;
Generally speaking, the tree you used for your hierarchy does not work very well in sql because of the recursive nature of the model (even if other databases actually support recursive queries in a very similar way you simulated with the variables).
For other ways to model your hierarchy, see e.g. Bill Karwins presentation Models for hierarchical data. They make it a lot easier to query a path without recursion.
I have created a decent solution. Its not incredibly fast, but that is too be expected and as this is just for a once a day load, it is acceptable for now.
Essentially, I created a function that gets the Path based on an id, then just run a view (going with a faux materialized view when pushing to production for faster loads to logstash (avoiding the timeouts essentially)) that selects all of the values, and then the path for the appropriate row.
CREATE FUNCTION `get_parent_path` (child int)
RETURNS VARCHAR(1024)
BEGIN
DECLARE path varchar(1024);
SELECT
GROUP_CONCAT(a.Alias)
INTO
path
FROM (
SELECT
T2.*
FROM
(
SELECT
#r AS _id
(
SELECT
#r := Parent
FROM
documents
WHERE
id = _id
) as ParentId,
#l: = #l + 1 as lvl
FROM
(SELECT #r := child, #l := #parent) vars, documents
WHERE
#r <> 0
) T1
JOIN
documents T2
ON
T1._id = T2.Id
ORDER BY T2.Id
) a;
RETURN COALESCE(path, 'invalid child');
END
Then the view I created the view:
CREATE VIEW 'documentswithpath' AS
SELECT *, get_parent_path(Id) FROM documents;
Then I just run SELECT * FROM documentswithpath; from the logstash script. This is also excluding alot of the logic for logstash for a simplistic answer. If anyone has a better, preferably faster, method of doing this, please let me know! Thanks.
This question follows on from MYSQL join results set wiped results during IN () in where clause?
So, short version of the question. How do you turn the string returned by GROUP_CONCAT into a comma-seperated expression list that IN() will treat as a list of multiple items to loop over?
N.B. The MySQL docs appear to refer to the "( comma, seperated, lists )" used by IN () as 'expression lists', and interestingly the pages on IN() seem to be more or less the only pages in the MySQL docs to ever refer to expression lists. So I'm not sure if functions intended for making arrays or temp tables would be any use here.
Long example-based version of the question: From a 2-table DB like this:
SELECT id, name, GROUP_CONCAT(tag_id) FROM person INNER JOIN tag ON person.id = tag.person_id GROUP BY person.id;
+----+------+----------------------+
| id | name | GROUP_CONCAT(tag_id) |
+----+------+----------------------+
| 1 | Bob | 1,2 |
| 2 | Jill | 2,3 |
+----+------+----------------------+
How can I turn this, which since it uses a string is treated as logical equivalent of ( 1 = X ) AND ( 2 = X )...
SELECT name, GROUP_CONCAT(tag.tag_id) FROM person LEFT JOIN tag ON person.id = tag.person_id
GROUP BY person.id HAVING ( ( 1 IN (GROUP_CONCAT(tag.tag_id) ) ) AND ( 2 IN (GROUP_CONCAT(tag.tag_id) ) ) );
Empty set (0.01 sec)
...into something where the GROUP_CONCAT result is treated as a list, so that for Bob, it would be equivalent to:
SELECT name, GROUP_CONCAT(tag.tag_id) FROM person INNER JOIN tag ON person.id = tag.person_id AND person.id = 1
GROUP BY person.id HAVING ( ( 1 IN (1,2) ) AND ( 2 IN (1,2) ) );
+------+--------------------------+
| name | GROUP_CONCAT(tag.tag_id) |
+------+--------------------------+
| Bob | 1,2 |
+------+--------------------------+
1 row in set (0.00 sec)
...and for Jill, it would be equivalent to:
SELECT name, GROUP_CONCAT(tag.tag_id) FROM person INNER JOIN tag ON person.id = tag.person_id AND person.id = 2
GROUP BY person.id HAVING ( ( 1 IN (2,3) ) AND ( 2 IN (2,3) ) );
Empty set (0.00 sec)
...so the overall result would be an exclusive search clause requiring all listed tags that doesn't use HAVING COUNT(DISTINCT ... ) ?
(note: This logic works without the AND, applying to the first character of the string. e.g.
SELECT name, GROUP_CONCAT(tag.tag_id) FROM person LEFT JOIN tag ON person.id = tag.person_id
GROUP BY person.id HAVING ( ( 2 IN (GROUP_CONCAT(tag.tag_id) ) ) );
+------+--------------------------+
| name | GROUP_CONCAT(tag.tag_id) |
+------+--------------------------+
| Jill | 2,3 |
+------+--------------------------+
1 row in set (0.00 sec)
Instead of using IN(), would using FIND_IN_SET() be an option too?
http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_find-in-set
mysql> SELECT FIND_IN_SET('b','a,b,c,d');
-> 2
Here's a full example based on the example problem in the question, confirmed as tested by the asker in an earlier edit to the question:
SELECT name FROM person LEFT JOIN tag ON person.id = tag.person_id GROUP BY person.id
HAVING ( FIND_IN_SET(1, GROUP_CONCAT(tag.tag_id)) ) AND ( FIND_IN_SET(2, GROUP_CONCAT(tag.tag_id)) );
+------+
| name |
+------+
| Bob |
+------+
You can pass a string as array, using a split separator, and explode it in a function, that will work with the results.
For a trivial example, if you have a string array like this: 'one|two|tree|four|five', and want to know if two is in the array, you can do this way:
create function str_in_array( split_index varchar(10), arr_str varchar(200), compares varchar(20) )
returns boolean
begin
declare resp boolean default 0;
declare arr_data varchar(20);
-- While the string is not empty
while( length( arr_str ) > 0 ) do
-- if the split index is in the string
if( locate( split_index, arr_str ) ) then
-- get the last data in the string
set arr_data = ( select substring_index(arr_str, split_index, -1) );
-- remove the last data in the string
set arr_str = ( select
replace(arr_str,
concat(split_index,
substring_index(arr_str, split_index, -1)
)
,'')
);
-- if the split index is not in the string
else
-- get the unique data in the string
set arr_data = arr_str;
-- empties the string
set arr_str = '';
end if;
-- in this trivial example, it returns if a string is in the array
if arr_data = compares then
set resp = 1;
end if;
end while;
return resp;
end
|
delimiter ;
I want to create a set of usefull mysql functions to work with this method. Anyone interested please contact me.
For more examples, visit http://blog.idealmind.com.br/mysql/how-to-use-string-as-array-in-mysql-and-work-with/