Convert MySQL group concat string to json object structure via PHP - php

I have 2 MySQL tables, the first one is the parent table and second one the child table. These two table share a 1:M relationship. The T_1 contains the list of unique users with some informations. And the T_2 contains all the line data list with coordinates at a specific timestamp for each user. The child table T_2 has a FK that references the PK in the T_1 parent table. I share below the CREATE STATEMENT:
CREATE TABLE `T_1` (
`id` varchar(45) DEFAULT NULL,
`value_1` tinyint(4) NOT NULL,
`personId` varchar(45) NOT NULL,
`value_2` varchar(45) NOT NULL,
`value_3` varchar(45) NOT NULL,
`Value_4` int(11) NOT NULL,
PRIMARY KEY (`personId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `T_2` (
`personId` varchar(45) NOT NULL,
`timestamp` varchar(45) NOT NULL,
`x` int(11) NOT NULL,
`y` int(11) NOT NULL,
KEY `user_fk_idx` (`personId`),
CONSTRAINT `user_fk` FOREIGN KEY (`personId`) REFERENCES `T_1` (`personId`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
I would like to get as final json result something like that:
[
{
"id": "",
"value_1": true,
"personId": "1",
"value_2": "test",
"value_3": "test",
"value_4": 10,
"lineData": [
{
"timestamp": "some_date",
"x": 100,
"y": 250
},
{
"timestamp": "some_date",
"x": 100,
"y": 350
}
]
},
{
"id": "",
"value_1": true,
"personId": "2",
"value_2": "test",
"value_3": "test",
"value_4": 10,
"lineData": [
{
"timestamp": "some_date",
"x": 50,
"y": 450
},
{
"timestamp": "some_date",
"x": 80,
"y": 550
}
]
}
]
This is the first query selection that I have made in order to get the aggregation of all the T_2 records for each T_1.personId:
SELECT T_1.personId,GROUP_CONCAT(CONCAT(T_2.timestamp, T_2.x, T_2.y) SEPARATOR ' ')
FROM T_1
JOIN T_2
ON T_1.personId=T_2.personId
GROUP BY personId;
Except that I need to add also id, value_1, value_2, value_3, value_4, the query aggregation seems correct by I'm still far away from the expected result:
By using the PDO::FETCH_ASSOCquery fetch within PHP I get actually the similar expected array result.
But the main problem is that the linedata aggregation is just in a single string format grouped and concatenated all together, not in an object structure form. (screen below)
I attach the example data below from the query selection:
array(7) {
["id"]=>
NULL
["value_1"]=>
string(1) "0"
["personId"]=>
string(2) "1"
["value_2"]=>
string(9) "test"
["value_3"]=>
string(4) "test"
["value_4"]=>
string(2) "10"
["linedata"]=>
string(359) "2018/09/15 10:00:05 AM1100950 2018/09/15 10:00:07 AM1100850 2018/09/15 10:00:09 AM1180800 2018/09/15 10:00:10 AM1280800 2018/09/15 10:00:15 AM1380800 2018/09/15 10:00:17 AM1480800 2018/09/15 10:00:20 AM1900800 2018/09/15 10:00:25 AM1930750 2018/09/15 10:00:40 AM1910440 2018/09/15 10:00:45 AM1250460 2018/09/15 10:01:25 AM1100455 2018/09/15 10:01:29 AM1100655"
}
}
Maybe is it needed to concatenate the square and curly brackets within the selection query?
Can someone suggest me how to get the correct separated structure for linea data? I supposed that we need to use also some post-process PHP stuff in order to get the exact desired output structure.
Update
Following the #Salman A's suggestion, I have tweak the query as below:
GROUP_CONCAT(CONCAT_WS(', ',timestamp,x, y) SEPARATOR '; ') as linedata_0
And I have stored the result of the query within $query_result.
Then I have made a nested foreach in order to explode each row with ; and each value of each row with , in $query_result['linedata_0']. And I have put the exploded result in a new key called linedata_1. As showed below:
foreach ($query_result as $q_r){
$first_split = explode(";", $q_r["linedata_0"]);
foreach ($first_split as $f_s){
$second_split = explode(",", $f_s);
$query_result["linedata_1"][]= $second_split;
}
}
I have removed the $query_result[0]["linedata_0"] element. And after that I have assigned the key values to each $query_result['linedata_1'] sub-arrays and store in a new element called $query_result['linedata']. After that I have remove the old $query_result['linedata_1'] element with unset command and in the last foreach I have cast the x and y values from string to integer:
unset($query_result[0]["linedata_0"]);
foreach(($query_result["linedata_1"]) as $q_r) {
$keys = array('timestamp', 'x', 'y');
$query_result["linedata"][] = array_combine($keys, $q_r);
}
unset($query_result["linedata_1"]);
$index = 0;
foreach($query_result["linedata"] as $index=>$val) {
$query_result["linedata"][$index]["x"] = (int) $val["x"];
$query_result["linedata"][$index]["y"] = (int) $val["y"];
$index++;
}
With these PHP post-process operations I got exactly the expected result.Probably this post-process steps within php are not well optimized, but they do the trick for me. If someone of us has a more optimized workaround solution please share it. Thanks.

Related

SQL query JSON of MySQL 8.0 comes String instead of Array

I create a table in MySQL 8.0 as follows:
CREATE TABLE `airline_table` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`info` json DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
It contains JSON type data, and I insert some data as:
INSERT INTO airline_table VALUES ('1','{"data": [{"city": "Houston", "state": "TX"},
{"city": "Los Angles", "state": "CA"}], "airline": ["UA", "AA"]}');
And I use php to visit the database, I wish to get value of "airline" as an Array.
<?php
$mysqli = new mysqli("localhost", "root", "aproot2019", "test");
$sql = "SELECT id, info -> '$.airline' AS airline FROM airline_table";
$result = $mysqli->query($sql);
$row = $result->fetch_array();
//print_r($row);
$airline = $row['airline'];
echo $airline . "<br>"; // ["UA", "AA"] , this is a string but not an array, how can I have an Array?
echo is_array($airline) ? 'Array' : 'not an Array' . "<br>"; // not an Array
echo is_string($airline) ? 'String' : 'not a String' . "<br>" ; // String
$mysqli->close();
?>
But it comes out a String, NOT an Array!
This really annoys me, and JSON in MySQL is hard to understand.
Have you considered decoding JSON?
$json = json_decode('{"data": [{"city": "Houston", "state": "TX"}, {"city": "Los Angles", "state": "CA"}], "airline": ["UA", "AA"]}');
// for your case it will be :
// $json = json_decode($row['airline']);
echo var_dump($json->airline);
/**
* array(2) {
[0]=>
string(2) "UA"
[1]=>
string(2) "AA"
}
Querying a JSON from MySQL is not hard, but.... For this kind of tables I would not do it in JSON. 😉
SELECT
j.city,
j.state
FROM airline_table
CROSS JOIN JSON_TABLE(info, '$.data[*]' COLUMNS(
city VARCHAR(20) PATH '$.city',
state VARCHAR(20) PATH '$.state')) as j
output:
city
state
Houston
TX
Los Angles
CA

PDO - get from one to many structure

I have this db structure:
create table article(
id int AUTO_INCREMENT PRIMARY KEY,
title varchar(50),
text text
)
create table comments(
id int AUTO_INCREMENT PRIMARY KEY,
article int not null
username varchar(30) not null,
text text not null,
foreign key(article) references article(id) on delete cascade
)
I would like to get articles with comments and convert to json with this structure:
[
{
id: 1,
title: "article1",
text: "text1",
"comments": [
{
id: 1,
username: "user1",
text: "text"
}
]
}
]
This is my code:
$query = $pdo->query('select * from article as a join comments as c on c.article =a.id');
$query->execute();
var_dump(json_encode($query->fetchAll(PDO::FETCH_ASSOC)));
and result:
[{"id":"1","title":"artile1","text":"comment1","article":"1","username":"user1"}]
It is any way how to get article and comments as inner array? I could do it manually but, I will have a lot of tables with many columns.
Thanks for advices
It looks like it is not possible using PDO fetch modes. They are powerful, but unfortunately, I was not able to get the output you wanted.
You can achieve this outcome using a simple loop. The downside is that you have to create the array manually.
$stmt = $pdo->prepare('SELECT a.id AS aid, a.title, a.text AS atext, c.id AS cid, c.username, c.text AS ctext
FROM article AS a
JOIN comments AS c ON c.article =a.id ');
$stmt->execute();
$stmt->setFetchMode(PDO::FETCH_ASSOC);
$id = null;
$data = [];
foreach ($stmt as $row) {
$comment = [
'id' => $row['cid'],
'username' => $row['username'],
'text' => $row['ctext'],
];
if ($id == $row['aid']) {
// If parent ID still the same append only comment
$data[array_key_last($data)]['comments'][] = $comment;
} else {
// set new id and append a whole new row
$id = $row['aid'];
$data[] = [
'id' => $row['aid'],
'title' => $row['title'],
'text' => $row['atext'],
'comments' => [$comment]
];
}
}
PDO has plenty of fetch modes and you can mix them together, but it looks like none of them can cope with joins the way you would like them too. They are all described here in https://phpdelusions.net/pdo/fetch_modes

Insert Data from a JSON to MySQL database table

I am trying to insert data to 2 different tables in my database.
First table is named business which I have done it to insert data to from the json file to the database.
But when I am trying to insert data into table named business_phone it does not do anything.
Here is my code for inserting the data:
$query = '';
$query_phones='';
$table_data = '';
$filename = "businesses.json";
$businesses = file_get_contents($filename);
$business = json_decode($businesses, true);
foreach($business as $row)
{
$query .= "INSERT INTO business(title, address, website, page) VALUES ('".$row["title"]."', '".$row["address"]."', '".$row["website"]."', '".$row["page"]."'); ";
//data that i will show on page
$table_data .= '
<tr>
<td>'.$row["title"].'</td>
<td>'.$row["address"].'</td>
<td>'.$row["website"].'</td>
<td>'.$row["page"].'</td>
</tr>
';
}
foreach($business as $row)
{
$query_phones .="INSERT INTO business_phones(business_title, phone_number, phone_name) VALUES ('".$row["title"]."', '".$row["number"]."', '".$row["name"]."');";
}
Here is some code from the json file
[
{
"title": "CONSERVE IT LTD",
"address": "12 Truman Ave (10) ",
"phones": [
{
"name": "telephone_1",
"number": "876-754-0220"
},
{
"name": "telephone_2",
"number": "876-754-0221"
}
],
"website": "www.conserveitja.com",
"page": 1
},
{
"title": "Consie Walters Cancer Hospital",
"address": "22 Deanery Rd (3) ",
"phones": [
{
"name": "telephone_1",
"number": "876-930-5016"
}
],
"page": 1
},
...
]
I don't know how you to handle within php but you can create an auxiliary table to be populated from the file, and then use JSON_TABLE function for the key values to be inserted into that table, provided you're using MySQL DB ver. 8+ :
INSERT INTO business(title, address, website, page)
SELECT t.*
FROM tab
CROSS JOIN
JSON_TABLE(jsdata, '$[*]' COLUMNS (
title VARCHAR(100) PATH '$.title',
address VARCHAR(100) PATH '$.address',
website VARCHAR(100) PATH '$.website',
page VARCHAR(100) PATH '$.page')
) t
and
INSERT INTO business_phones(business_title, phone_number, phone_name)
SELECT t.*
FROM tab
CROSS JOIN
JSON_TABLE(jsdata, '$[*]' COLUMNS (
business_title VARCHAR(100) PATH '$.title',
NESTED PATH '$.phones[*]' COLUMNS (
phone_number VARCHAR(100) PATH '$.number',
phone_name VARCHAR(100) PATH '$.name')
)
) t
a side Note concatenations for sql statements are vulnerable to Injection for most of the programming languages as #Magnuss Eriksson mentioned.
Demo
$row["number"] is not valid. It will be $row["phones"][0] or $row["phones"][1]. Because according to your data "number" and "name" inside of "phones" which is array.
You can nested loop through "phones"
foreach($row["phones"] as $contact){
$query .="INSERT INTO business_phones(business_title, phone_number,
phone_name) VALUES ('".$row["title"]."', '".contact."',
'".$row["name"]."');"
}

Add Data from JSON in database in two different tables

I have a JSON file which contains data that i want to store in two different database in sql.
The json file looks like this:
[
{
"title": "CONSERVE IT LTD",
"address": "12 Truman Ave (10) ",
"phones": [
{
"name": "telephone_1",
"number": "876-754-0220"
},
{
"name": "telephone_2",
"number": "876-754-0221"
}
],
"website": "www.conserveitja.com",
"page": 1
},
{
"title": "Consie Walters Cancer Hospital",
"address": "22 Deanery Rd (3) ",
"phones": [
{
"name": "telephone_1",
"number": "876-930-5016"
}
],
"page": 1
},
...
]
I managed to store title, address, website and page in my 1st table in the database but now I want to store in a different table in the same database the phones for each business.
Here is the code I am using to store the information for the first table and I little code for the 2nd table.
$connect = mysqli_connect("localhost", "root", "1234", "businesses"); //Connect PHP to MySQL Database
$query = '';
$query_phones='';
$table_data = '';
$filename = "businesses.json";
$businesses = file_get_contents($filename);
$business = json_decode($businesses, true);
foreach($business as $row)
{
$query .= "INSERT INTO business(title, address, website, page) VALUES ('".$row["title"]."', '".$row["address"]."', '".$row["website"]."', '".$row["page"]."'); ";
//data that i will show on page
$table_data .= '
<tr>
<td>'.$row["title"].'</td>
<td>'.$row["address"].'</td>
<td>'.$row["website"].'</td>
<td>'.$row["page"].'</td>
</tr>
';
}
Code for inserting the phone data in the second table in database
foreach($business as $row)
{
$query_phones .="INSERT INTO business_phones(business_title, phone_number, phone_name) VALUES ('".$row["title"]."', '".$row["number"]."', '".$row["name"]."');";
}
Structure of 1st table (business)
1 idPrimary int(11)
2 title varchar(50)
3 address varchar(50)
4 website varchar(50)
5 page int(11)
Structure of 2nd table (business_phones)
1 idPrimary int(11)
2 business_title varchar(250)
3 phone_number varchar(255)
4 phone_name varchar(255)

How to avoid duplicate data in select box when retrieving data from same table

Here is my mysql query with purpose of retrieving BRAND, PATTERNS, AR, and WIDTH from tblInventory where my SZ is match with my input SZ.
CREATE TABLE `tblInventory` (
`BRAND` varchar(100) DEFAULT NULL,
`PATTERNS` varchar(100) DEFAULT NULL,
`SZ` int(11) DEFAULT NULL,
`AR` int(11) DEFAULT NULL,
`WIDTH` int(11) DEFAULT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `tblInventory` (`BRAND`, `PATTERNS`, `AR`, `WIDTH`) VALUES
('OHTSU FALKEN', 'FKU', 70, 165),
('OHTSU FALKEN', 'ZE912', 60, 165),
('OHTSU FALKEN INDO', 'SN828', 70, 155),
('OHTSU FALKEN', 'SN816', 70, 145),
('OHTSU FALKEN', 'SN807', 80, 145),
('OHTSU FALKEN', 'SN807', 80, 155);
SELECT DISTINCT BRAND, PATTERNS, AR, WIDTH FROM tblInventory WHERE SZ='$b_size' ORDER BY SZ ASC
JSON method used to storing data into array
$result = mysqli_query ($mydatabase, $tyre_query);
$i = 0;
while ($row = mysqli_fetch_assoc($result)) {
$tyreArray[$i] = array(
"BRAND" =>$row['BRAND'],
"PATTERNS" =>$row['PATTERNS'],
"AR" =>$row['AR'],
"WIDTH" =>$row['WIDTH'],
);
++$i;
}
echo json_encode($tyreArray);
Then array will pass back to jQuery and append each record to my select box.
function(data)
{
var json = $.parseJSON(data);
for (var i = 0; i < json.length; i++) {
BRAND = json[i]["BRAND"]
MODEL = json[i]["PATTERNS"]
AR = json[i]["AR"]
WIDTH = json[i]["WIDTH"]
//alert(MODEL)
$('#cmbTBrand').append($('<option/>', {
value: BRAND,
text : BRAND
}));
$('#cmbTModel').append($('<option/>', {
value: MODEL,
text : MODEL
}));
$('#cmbTWidth').append($('<option/>', {
value: WIDTH,
text : WIDTH
}));
$('#cmbTProfile').append($('<option/>', {
value: AR,
text : AR
}));
}
})
count = 1;
When I append each of the FIELD into my select box duplicate data will display.
How to avoid the data duplicate in select box?
You can run separate queries for each SELECT box, and add a GROUP BY clause to your SELECT query to prevent duplicate data.
SELECT BRAND
FROM tblInventory
WHERE SZ='$b_size'
GROUP BY BRAND
SELECT PATTERNS
FROM tblInventory
WHERE SZ='$b_size'
GROUP BY PATTERNS
And so on.
Solution in Javascript
Instead of modifying your queries, you could achieve this all in JavaScript by first building up a set of arrays (objects actually) to represent the unique options of each select box and then another loop to output each select box.
var json = $.parseJSON(data);
var brands = {}, models = {};
for (var i = 0; i < json.length; i++) {
BRAND = json[i]["BRAND"]
MODEL = json[i]["PATTERNS"]
brands[BRAND] = BRAND;
models[MODEL] = MODEL;
}
// now you have a list of unique brands and models

Categories