How to split row into multiple rows from the MySQL? - php

I have a MySQL data table, in which I have more than 2 columns. First column has a unique value clinical trial value whereas second column has disease information. There are, in most of the cases, more than 2 disease names in one cell for a single id. I want to spilt those rows which cell contains two or more than two diseases. There is a pattern for searching also, i.e. small character is immediately followed by capital character., e.g. MalariaDengueTuberculosis like this. Suppose for these three diseases there is unique id, it should show like the following:
NCT-ID disease
4534343654 Maleria
4534343654 Dengue
4534343654 Tubercoulsosis

If you want to store one or more data in one String column, you could use JSON data formatting.

It is not very clear to me what you're trying to achieve. Since you're using PHP you can try to read the tables using PDO and print the results. For example:
$sql = 'SELECT diseases FROM diseases_info_table';
foreach($pdo->query($sql) as $row) {
$diseases = preg_split('/(?=[A-Z])/', $row['diseases']);
$sql2 = 'SELECT * FROM diseases WHERE disease_name IN ("' . implode('","', $diseases) . '")';
foreach ($pdo->query($sql2) as $row) {
print $row['NCT-ID '] . "\t";
print $row['disease'] . "\t";
}
}
But this way you're generating a lot of queries. If it is possible for you to rethink the database structure than I would recommend doing that.

To answer your question:
If you insist on multi data in one column you can use php explode:
$diseases = explode("|", $row['disease']); //Changing | to whatever separates your diseases.
$diseases is now an array of your diseases which you can do:
foreach ($diseases as $disease)
{
echo $disease;
}
However
Personally I would normalise your database now before trying to hack around solutions. Use an ID against your table and then have a diseases table to link to it. ie:
Main table
NCT-ID
4534343654
5768788544
3i33i3i078
Disease Table
disease_id nct_id disease
1 4534343654 Broken Wind
2 4534343654 Chronic Nosehair
3 4534343654 Corrugated Ankles
4 5768788544 Discrete Itching
5 3i33i3i078 Gastric Ejections
6 3i33i3i078 Bloaty Head
This allows multiple diseases against one nct-id. disease_id would be the primary key.

Related

Query Reports from comma separated JSON mysql field

this is my first question. Apologies if I don't conform with the required rules. I have tried my best and
Problem--
I have a problem with a project am working on. It is medical based. I have these three tables that I need to get reports from
DB structure--
diseaseTable which has columns
diseaseID | diseaseName
diseaseID stores an integer i.e 1, 3, 4,..
dieseaseName stores name of disease e.g Hypertension, Malaria, ..e.tc
I also have patientsTable which has these key columns
patientID - which is an autoincrement int
patientName - name of patient
created_at - date and time of registration of the patient
Lastly there is treatment table which contains
treatmentID -autoincrement int
patient - contains the id of the patient which is a foreing key of patientsTable
created_at and updated_at for timestamps
diseaseTreated (which is my column of interest) Stores values in json comma separated fields.
I have already checked all results from https://stackoverflow.com/search?q=mysql+comma+separated+fields but none seems to solve my problem. I have checked on ways to restructure mysql schema but i would have too many tables to store the diseases diagnised from each patient. In addition one patient might be diagnosed with multiple diseases and a disease can be added any time. I might have almost 100 of them. If ID of Hypertension is 1 and arthritis is 2 a patient with both will have the
diseaseTreated field with "1","2" in the treatment table
Solution Expected--
I need to get reports for first time a specific disease has been treated for the first time or second time and more i.e revisit for treatment of the same disease (if i wanted to know how many cases of hypertension are for the first time, and how many cases of hypertension are recurrent)
Example - New cases report
Disease | New Cases
Hypertension | 4
Example - Recurrent cases report
Disease | New Cases
Hypertension | 1
Arthritis | 2
Am using Laravel I have attempted a lot of solutions but this is what i find too close to get what I need: Others I have attempted was looping through different array results i have queried from the database by using joins but I havent posted them because they have yielded nothing important.
$diseaseID
$countedDiseaseCases = DB::select('select * from treatmentTable where JSON_CONTAINS(diseases, \'[\"'.$diseaseID.'\"]\')')
This one would get me the rows with a specific disease and by using php count() on the result I get an int as 3 which is OK but I haven't got to put it as I expect filter and differentiate new and recurrent cases.
Thanks,
Any suggestion will be highly appreciated
If the deseaseTreated column is json, couldn't you add an isNew key and when the patient comes in for a second visit, flip that key to false or something similar?
Now when you want to make the report you can get counts on all of them.
//example deseaseTreated value
{
"deseases": [
{
"deseaseID":"Value",
"isNew":Boolean,
...
},
{...}
]
}
$records = DB::select('select * from treatment');
$diseases = DB::select('select * from diseases');
foreach ($diseases as $disease) {
$disease['count_new'] = 0;
}
foreach ($records as $record) {
$json = json_decode($record['diseaseTreated']);
foreach ($json['diseases'] as disease_index) {
if ($disease_index['isNew']) {
foreach ($diseases as &$d) {
if ($d['diseaseID'] == $disease_index['diseaseID']) {
$d['count_new']++;
}
}
}
}

Insert one array value to a mysql insert statement

People enter values into a form and on one entry of the form I have multiple values.
Eg:
One Entry for Name
and Multiple entries for hobbies.
I could enter into the db by running a for loop but then that would be multiple entries for the same name for each different hobby.
How can I enter one name and all hobbies with 'space' into one DB field 'TWIG'. I could use arrays but it shows up as ARRAY but then its back to FOR loop.
for ($t=0; $t<=$_POST['tc']-1; $t++) {
echo "<BR> ts:".$_POST[$t]."<BR>";
$ths[]=$_POST[$t];
}
print_r ($ths);
$statement = $link->prepare("INSERT INTO quest(cnos, dte, twig)
VALUES(:q, :d, :t )");
$statement->execute(array(
":q" => htmlspecialchars ($_POST['iht']),
":d" => $_SERVER['REQUEST_TIME'],
":t"=> $ths
));
One possibility is to implode your hobbies / concatinate your string into one...
for ($t=0; $t<=$_POST['tc']-1; $t++) {
$ths = $_POST[$t] . " "; //Concatinate string, do no use array!
}
//Cut off last character " " to avoid ugly space at the end:
$ths = substr($ths, 0, strlen($ths) - 1);
However a more clean solution is to make a more clear database structure if you want for atomic values.
This is an 1:n relation (Each of your entries in table A relates to n instances in table B).
Here is an example that can be adapted into your schema very easy:
Table User(id[PK], name, age);
Table User_Hobbies: (user_id, hobby_descr);
--
INSERT INTO User(2, "Chris", 19);
INSERT INTO USER_User_Hobbies(2, "videogames");
INSERT INTO USER_User_Hobbies(2, "music");
--
Example query:
SELECT u.name, u.age, GROUP_CONCAT(uh.hobby_descr) AS hobbies
FROM User u
LEFT JOIN User_Hobbies uh ON u.id = uh.user_id
WHERE u.id = 123 /*Where is optional, if you want to filter for a specific user*/
GROUP BY u.id;
Possible result:
| name | age | hobbies |
chris 18 videogames, music
steve 22 computer, programming, php
Use the implode function for this as follows:
":t"=> implode(" ", $ths);
I am not sure, if I understand your question right, but if you want to insert an array as a comma-seperated string (or separated by whatever), why don't use the php implode function: http://php.net/manual/de/function.implode.php
For example:
$myHobbies = array("football", "basketball", "running");
$statement = $link->prepare("INSERT INTO quest(cnos, dte, twig)
VALUES(:q, :d, :t )");
$statement->execute(array(
":q" => htmlspecialchars ($_POST['iht']),
":d" => $_SERVER['REQUEST_TIME'],
":t"=> implode(" ", $myHobbies)
));
I think to use comma's or semicolons as separator, is better than whitespaces, since some of the hobbies could consists of two words (for example "pc gaming").
I think that better solution is to use json_encode and not implode, since it provides more robust structure.
":t" => json_encode($myHobbies)
You can use join function to store multiple value in same field in database may be it will works:-
":t"=>join(",", $ths);//if you want ,(coma) between two string
or
":t"=>join(" ", $ths);//if you want space between two string

Query find value in comma seperated list mysql

in my script a "restaurant" can have multiple locations so, i made a column in the restaurant table containing a coma seperated list with locations.
Now i want to make a msql query that checks if the id can be fount is this column (comma seperated list) and if so then select it. i came up with this
SELECT restaurant_id,restaurant_name
FROM restaurant WHERE ('.$locIdList.') IN (locationRes)
ORDER BY restaurant_name ASC'
It does work... but i have some restaurants where I added location 16 and 17 so (16,17) now when i do this query for location 16 it shows the restaurant but when i dot this for location 17 it does not... but the whole point was to get the multi values from the comma seperated list.
So how to do this ?
You can use PHP to generate the query for each comma-delimited value. i.e., run a PHP loop on comma-delimited comparison string, convert it into individual items and compare each item through LIKE Operator and an IN () function.
SELECT restaurant_id,restaurant_name
FROM restaurant WHERE ('16') IN (locationRes)
OR
FROM restaurant WHERE ('17') IN (locationRes)
ORDER BY restaurant_name ASC'
The best solution would be to create a relation table that implements the many-to-many relationship between restaurants and locations. Then you can use a solution like How to return rows that have the same column values in MySql to find all the restaurants that are in all locations.
To search for a value in a comma-separated list, you use FIND_IN_SET. But this can only search for one value at a time. If you want to find restaurants that are in all locations, you need to combine multiple calls:
$locArray = explode(',', $locIdList);
$locQuery = implode(' AND ', array_map(function($loc) { return "FIND_IN_SET($loc, locationRes)"; }, $locArray));
$query = "SELECT restaurant_id,restaurant_name
FROM restaurant
WHERE $locQuery
ORDER BY restaurant_name ASC";
If you want to find restaurants that are in any of the locations instead of all locations, change AND to OR.
You should avoid putting multiple values inside a single column.
Instead, it's recommended to create another table locations(location_id, col1, col2, restaurant_id), while the restaurant_id field references to the primary key in table restaurant.
If you have a comma separated list you are pulling from a database, you could use PHP to separate the list and create an array of each item.
$result_of_sql = "restaurant 1, restaurant 2, restaurant 3, restaurant 4, restaurant 5, restaurant 6";
$restaurants = explode(',', $result_of_sql);
echo '<ul>';
foreach ($restaurants as $restaurant) {
echo '<li>' . trim($restaurant) . '</li>';
}
echo '</ul>';
What happens here is first, you pull out all of you restaurants (the comma separated list). Then you use explode to take away the commas and create an array. Then you use the foreach loop to echo the entire array out. trim is just to clean everything up by removing whitespace you might have before and after the restaurant name.

Computing an average from different tables

I am sorry for a verlo long question, just trying to explain in details. My formatting is not very good, sorry for that as well. I had a PHP/ MySQL App that essentially was not truly relational as I had one large table for all student scores. Among other things, I was able to calculate the average score for each subject, such that the average appeared alongside a student's score. Now I have since split the table up, to have a number of tables which I am successfully querying and creating School Report Cards as before. The hardship is that I can no longer calculate the avaerages for any subject.
Since I had one table with 5 subjects and each of the subjects had 2 tests, I queried for data and calculated the average as follows:
The one table (Columns):
id date name exam_no term term year eng_mid eng_end mat_mid mat_end phy_mid phy_end bio_mid bio_end che_mid che_end
The one query:
$query = "SELECT * FROM pupils_records2
WHERE grade='$grade' && class='$class' && year = '$year' && term ='$term'";
$result = mysqli_query($dbc, $query);
if (mysqli_num_rows($result) > 0) {
$num_rows=mysqli_num_rows($result);
while($row = mysqli_fetch_array($result)){
//English
$eng_pupils1{$row['fname']} = $row['eng_mid'];
$eng_pupils2{$row['fname']} = $row['eng_end'];
$mid=(array_values($eng_pupils1));
$end=(array_values($eng_pupils2));
$add = function($a, $b) { return $a + $b;};
$eng_total = array_map($add, $mid, $end);
foreach ($eng_total as $key => $value){
if ($value==''){
unset ($eng_total[$key]);
}
}
$eng_no=count($eng_total);
$eng_ave=array_sum($eng_total)/$eng_no;
$eng_ave=round($eng_ave,1);
//Mathematics
$mat_pupils1{$row['fname']} = $row['mat_mid'];
$mat_pupils2{$row['fname']} = $row['mat_end'];
$mid=(array_values($mat_pupils1));
$end=(array_values($mat_pupils2));
$add = function($a, $b) { return $a + $b;};
$mat_total = array_map($add, $mid, $end);
foreach ($mat_total as $key => $value){
if ($value==''){
unset ($mat_total[$key]);
}
}
print_r($mat_total);
$mat_no=count($mat_total);
echo '<br />';
print_r($mat_no);
$mat_ave=array_sum($mat_total)/$mat_no;
$mat_ave=round($mat_ave,1);
}
}
//Biology
etc
I split the table into separate tables and have names in a separate table, not needed for calculating avaerages, so I will not show it here. Each subject table tajkes the following form:
id date exam_no term year grade class test*
*Test would be eng_mid or eng_end or mat_mid etc.
Because I had only one query which returned 10 rows (5 subjects each with two tests: e.g. eng_mid (English Mit exam), eng_end (english end of term test), I was able to capture all rows in one call and pack each subject into an array, and then work out the class average, with the help of array_map. It may not be elegant, but it worked very well. Now, I have each test in it's own table.
I was trying to write a joint so as to get a signle resultset but the query fails. The columns as like:
I know that the database design is not anything to be proud off, but coming from a huge single table, this is a massive step (worthy a pat on the shoulder).
What I wish to do is to be able to query all my data and calculate class averages (about 30 students in each class). I tried to use separate queries but I ran into a wall, in that previously I would use the WHILE conditional as shown after the query for it to pull all rows and create an array from which I could get desired results. Now several queries just makes me confused as to how I can archieve the same results since a join is not working. Also I am having a separate $row variable, and that throws me further off balance!
Is it even possible to do averages as I did on my infamous one table (from the dark side) or is my table design so messed up, what I want just isn't humanly possible?
Please any help will be deeply appreciated.
Try using union. It would be something like
select grade, test from math
union all
select grade, test from english
union all
....
Also, in my opinion, better design would be to have table exams something like that (warning, pseudo-DML):
id int primary key,
student_id int foreign key students
subject_id int foreign key subjects
exam_type_id int foreign key exam_types
grade int(????)
exam_types table would be just midterm and final, but you'll be able to easily support more types in future, if required.
subjects table will store all kinds of subjects you have (at this time there will be only five of them: math, eng, phy, etc.
The averaging query would be as simple as (yes, you can actually do aggregation in the query itself)
select student_id, avg(grade)
from exams
group by student_id

What's the most efficient way to pull the data from mysql so that it is formatted as follows:

I have a table that has patient information (name, dob, ssn, etc.) and a table that has lists of medications that they take. (aspirin, claritin, etc.) The tables are related by a unique id from the patient table. So, it's easy enough to pull all of Mary Smith's medications.
But, what I need to do is to show a paginated list of patients that shows their name, other stuff from the patient table and has a column with a line-separated list of their medications. Roughly, this:
If I do a simple left join, I get 3 repeated rows of Mary Smith with one medication per row.
The patient table can have thousands of records, so I don't want to do a query to get all the patients and then loop through and get their meds. And, because it's paginated based on patient, I can't figure out how to get the correct number of patients for the page, along with all their medications.
(The patients/medications thing is just a rough example of the data; so please don't suggest restructuring how the data is stored.)
GROUP_CONCAT to the rescue!
SELECT patients.first_name, patients.last_name, GROUP_CONCAT(prescriptions.medication SEPARATOR ", ") AS meds FROM patients LEFT JOIN prescriptions ON prescriptions.patient_id = patients.id GROUP BY patients.id;
You've got a few choices.
rowspan clauses with one drug per cell per user. You'd need to run two SQL queries to precalculate how big each user's span would have to be, or suck the query results into PHP and do the counting there.
Simple state machine - start a new row each time the user changes, then just keep adding more drug names seperated with <br /> while the user's name stays constant.
The second one's probably easiest:
$previous_name = null;
$first = true;
echo "<table";
while($row = mysql_fetch_assoc($results)) {
if ($row['name'] <> $previous_name) {
if (!$first) {
echo "</td></tr>"; // end previous row, if it's not the first row we've output
$first = false;
}
echo "<tr><td>$row[name]</td><td>"
$previous_name = $row['name'];
}
echo "$row['drug']<br />";
}
echo "</td></tr></table>";
I think what you are looking for is referred to a 'concation of subquery'.
Check http://forums.mysql.com/read.php?20,157425,157796#msg-157796 and http://mysql.bigresource.com/SELECT-CONCAT-Subquery-S5cIpzqO.html

Categories