Mysql - pivot indeterminate number of rows to multiple columns - mysql

Given the following two tables:
+- Members -+
| ID | Name |
+----+------+
| 1 | Bob |
| 2 | Jim |
| 3 | Judy |
etc...
This table represents the members' children. Each parent may have many or no children
+- Children -------------+-----+
| ID | ParentID | Name | Age |
+----+----------+--------+-----+
| 1 | 3 | Jeff | 4 |
| 2 | 3 | Casey | 3 |
| 3 | 1 | Steven | 10 |
| 4 | 2 | Mary | 7 |
| 5 | 1 | Esther | 8 |
| 6 | 2 | Abe | 11 |
| 7 | 3 | Paul | 6 |
etc...
I need to create a table that looks like this:
+----+------+--------+------+---------+------+--------+------+
| ID | Name | Child1 | Age1 | Child2 | Age2 | Child3 | Age3 |
+----+------+--------+------+---------+------+--------+------+
| 1 | Bob | Steven | 10 | Esther | 8 | | |
| 2 | Jim | Abe | 11 | Mary | 7 | | |
| 3 | Judy | Paul | 6 | Jeff | 4 | Casey | 3 |
+----+------+--------+------+---------+------+--------+------+
I've tried various pivot table approaches, but every one that I've seen requires a known number of rows in the second table for each row in the first table. I essentially need an unknown number of columns. A group_concat isn't going to meet my requirements.
Is this possible with MySQL or do I need to do this in the backend?

Related

in a m to n table between PKs of table A and B. how to find a PK of A that has a specific set of entries with PKs of B in the m to n table

Given the two tables teacher and student,
table : teacher
+-------+-----------+
| id | name |
+-------+-----------+
| 1 | John |
| 2 | Mary |
| 3 | Jeff |
| 4 | Bill |
| 5 | Bob |
| 6 | Frieda |
+-------+-----------+
table : student
+-------+-----------+
| id | name |
+-------+-----------+
| 1 | mario |
| 2 | lisa |
| 3 | anna |
| 4 | sara |
| 5 | felix |
+-------+-----------+
and the m to n relationship between them
table : teacherStudent
+---------+---------+
|teacherId|studentId|
+---------+---------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 3 | 1 |
| 3 | 5 |
| 4 | 4 |
| 4 | 5 |
| 5 | 1 |
| 5 | 2 |
| 5 | 3 |
| 6 | 1 |
| 6 | 2 |
| 6 | 3 |
| 6 | 4 |
+---------+---------+
how to find all teacherId that stand in relation to (exactly) studentId 1,2 and 3 ?
I want my final results to look like this:
+----------+
|teacherId |
+----------+
| 1 |
| 5 |
+----------+

Is there a mySQL procedure that can merge duplicate rows of data into one, then allow me to manipulate that data as if it were one row?

I'm trying to come up with a stored procedure that takes multiple rows that are exactly identical, and combines them into one row while summing one column, which can then be run through more stored procedures based on the sum of that one column.
I've tried a GROUP BY statement, but that doesn't actually group the rows together, because if I run the table through another procedure it performs actions as if each row were not combined. Performing a SELECT * FROM mytable query shows that each row was not actually combined into one.
Is there any way to permanently combine multiple rows into one singular row?
To start, I've got a table like this:
+-------+-----+--------+---------+------+-----+-----------+
| RowID | pID | Name | Date | Code | QTY | Purchased |
+-------+-----+--------+---------+------+-----+-----------+
| 1 | 1 | bob | 9/29/20 | 123 | 1 | |
| 2 | 1 | bob | 8/10/20 | 456 | 1 | |
| 3 | 2 | rob | 9/15/20 | 123 | 1 | |
| 4 | 2 | rob | 9/15/20 | 123 | 1 | |
| 5 | 2 | rob | 9/15/20 | 123 | 1 | |
| 6 | 2 | rob | 9/15/20 | 123 | 1 | |
| 7 | 2 | rob | 9/15/20 | 123 | 1 | |
| 8 | 3 | john | 7/12/20 | 987 | 1 | |
| 9 | 3 | john | 7/12/20 | 987 | 1 | |
| 10 | 4 | george | 9/12/20 | 684 | 1 | |
| 11 | 5 | paul | 2/2/20 | 454 | 1 | |
| 12 | 6 | amy | 1/12/20 | 252 | 1 | |
| 13 | 7 | susan | 5/30/20 | 131 | 1 | |
| 14 | 7 | susan | 6/6/20 | 252 | 1 | |
| 15 | 7 | susan | 5/30/20 | 131 | 1 | |
+-------+-----+--------+---------+------+-----+-----------+
By the end, i'd like to have a table like this:
+-------+-----+--------+---------+------+-----+-----------+
| RowID | pID | Name | Date | Code | QTY | Purchased |
+-------+-----+--------+---------+------+-----+-----------+
| 1 | 1 | bob | 9/29/20 | 123 | 1 | |
| 2 | 1 | bob | 8/10/20 | 456 | 1 | |
| 3 | 2 | rob | 9/15/20 | 123 | 5 | |
| 4 | 3 | john | 7/12/20 | 987 | 2 | |
| 5 | 4 | george | 9/12/20 | 684 | 1 | |
| 6 | 5 | paul | 2/2/20 | 454 | 1 | |
| 7 | 6 | amy | 1/12/20 | 252 | 1 | |
| 8 | 7 | susan | 5/30/20 | 131 | 2 | |
| 9 | 7 | susan | 6/6/20 | 252 | 1 | |
+-------+-----+--------+---------+------+-----+-----------+
Where exactly identical rows are combined into one row, and the QTY field is summed, that I can then add purchases to, or make deductions from the quantity as a total. Using GROUP BY statements can achieve this, but when I go to alter the quantity or add purchases to each person, it treats it like the first table, as if nothing was actually grouped.
So you have this table:
| RowID | pID | Name | Date | Code | QTY | Purchased |
+-------+-----+--------+---------+------+-----+-----------+
| 1 | 1 | bob | 9/29/20 | 123 | 1 | |
| 2 | 1 | bob | 8/10/20 | 456 | 1 | |
| 3 | 2 | rob | 9/15/20 | 123 | 1 | |
| 4 | 2 | rob | 9/15/20 | 123 | 1 | |
| 5 | 2 | rob | 9/15/20 | 123 | 1 | |
| 6 | 2 | rob | 9/15/20 | 123 | 1 | |
| 7 | 2 | rob | 9/15/20 | 123 | 1 | |
| 8 | 3 | john | 7/12/20 | 987 | 1 | |
| 9 | 3 | john | 7/12/20 | 987 | 1 | |
| 10 | 4 | george | 9/12/20 | 684 | 1 | |
| 11 | 5 | paul | 2/2/20 | 454 | 1 | |
| 12 | 6 | amy | 1/12/20 | 252 | 1 | |
| 13 | 7 | susan | 5/30/20 | 131 | 1 | |
| 14 | 7 | susan | 6/6/20 | 252 | 1 | |
| 15 | 7 | susan | 5/30/20 | 131 | 1 | |
The best way, as has been suggested, is to create a new table with the content of your query, then to rename the old table, and the new table to the original table's name, to check if everything is all right, and to drop the original table if yes.
CREATE TABLE indata_new AS
WITH grp AS (
SELECT
MIN(rowid) AS orowid
, pid
, name
, MAX(date) AS date
, code
, SUM(qty) AS qty
FROM indata
GROUP BY
pid
, name
, code
)
SELECT
ROW_NUMBER() OVER(ORDER BY orowid ASC) AS rowid
, *
FROM grp;
ALTER TABLE indata RENAME TO indata_old;
ALTER TABLE indata_new RENAME TO indata;
-- if "indata" now contains the data you want ...
SELECT * FROM indata;
-- out rowid | orowid | pid | name | date | code | qty
-- out -------+--------+-----+--------+------------+------+-----
-- out 1 | 1 | 1 | bob | 2020-09-29 | 123 | 1
-- out 2 | 2 | 1 | bob | 2020-08-10 | 456 | 1
-- out 3 | 3 | 2 | rob | 2020-09-15 | 123 | 5
-- out 4 | 8 | 3 | john | 2020-07-12 | 987 | 2
-- out 5 | 10 | 4 | george | 2020-09-12 | 684 | 1
-- out 6 | 11 | 5 | paul | 2020-02-02 | 454 | 1
-- out 7 | 12 | 6 | amy | 2020-01-12 | 252 | 1
-- out 8 | 13 | 7 | susan | 2020-05-30 | 131 | 2
-- out 9 | 14 | 7 | susan | 2020-06-06 | 252 | 1
-- you can ...
DROP TABLE indata_old;

how to update child table in mysql

I have have here an example table.incident table
+-------------+----------------------+
| incident_id | incident_description |
+-------------+----------------------+
| 1 | Accident |
| 2 | Homicide |
| 3 | Theft |
+-------------+----------------------+
incident_detail table:
+--------------------+-------------+-------------+
| incident_detail_id | person_name | incident_id |
+--------------------+-------------+-------------+
| 1 | errol | 1 |
| 2 | neo | 1 |
| 3 | aj | 1 |
| 4 | mark | 2 |
| 5 | calma | 2 |
| 6 | allan | 2 |
| 7 | dave | 3 |
| 8 | paul | 3 |
+--------------------+-------------+-------------+
I am providing a grid view like view that would allow the user to remove and add items in the incident_detail table. My question is, how can i update the incident_detail table? i am ok with adding new items, but removal. I don't know. Should i empty the entire table and insert the new items that the user added. But the problem here is that the existing items that weren't removed will be deleted and inserted again.
If you don't care about the incident_detail_id column incrementing more than it needs to, you can simply delete and re-insert the records, regardless of whether you changed the details for an incident.
It makes for easier SQL code but it does mean on each edit your IDs will go up. Assuming you're auto-incrementing.
After adding detail to Homicide you'll end up with:
+--------------------+-------------+-------------+
| incident_detail_id | person_name | incident_id |
+--------------------+-------------+-------------+
| 1 | errol | 1 |
| 2 | neo | 1 |
| 3 | aj | 1 |
| 9 | mark | 2 |
| 10 | calma | 2 |
| 11 | allan | 2 |
| 12 | new | 2 |
| 7 | dave | 3 |
| 8 | paul | 3 |
+--------------------+-------------+-------------+
If you're happy with that:
DELETE FROM incident_detail WHERE incident_id = 2;
INSERT INTO incident_detail (person_name, incident_id)
VALUES
('mark', 2)
('calma', 2)
('allan', 2)
('new', 2);

How to select all rows which have identical values

Here is my table:
+----+-------+------+
| id | name | code |
+----+-------+------+
| 1 | jack | 1 |
| 2 | peter | 1 |
| 3 | jack | 1 |
| 4 | ali | 2 |
| 5 | peter | 3 |
| 6 | peter | 1 |
| 7 | ali | 2 |
| 8 | jack | 3 |
| 9 | peter | 2 |
| 10 | peter | 4 |
+----+-------+------+
I want to select all rows that satisfy: the number of {those rows which its code value is between 1-3 and its name vale is identical} be more or equal than 4
From the above data, I want this output:
+----+-------+------+
| id | name | code |
+----+-------+------+
| 2 | peter | 1 |
| 5 | peter | 3 |
| 6 | peter | 1 |
| 9 | peter | 2 |
+----+-------+------+
How can I do that?
Use a subquery to figure out which names should be returned, then build your main query on that.
Try this:
select *
from mytable
where name in (
select name
from mytable
where code between 1 and 3
group by name
having count(*) > 3)
and code between 1 and 3

insert into a table having table name derived from subquery

I have tables named Student_Info table, Subject_Info table and an individual table belonging to each student containing information about each subject parameter for that student (like name of the subject, marks he got in that subject etc).
Student_Info
+----+----------+
| Id | Name |
+----+----------+
| 1 | Sam |
| 2 | Taylor |
| 3 | Rick |
+----+----------+
Subject_Info
+---------------+
| Subject_Name |
+---------------+
| Physics |
| Chemistry |
| Mathematics |
+---------------+
1_Info
+---------------+-------+
| Subject_Name | Marks |
+---------------+-------|
| Physics | 60 |
| Chemistry | 40 |
| Mathematics | 80 |
+---------------+-------+
2_Info
+---------------+-------+
| Subject_Name | Marks |
+---------------+-------|
| Physics | 70 |
| Chemistry | 50 |
| Mathematics | 60 |
+---------------+-------+
3_Info
+---------------+-------+
| Subject_Name | Marks |
+---------------+-------|
| Physics | 70 |
| Chemistry | 70 |
| Mathematics | 70 |
+---------------+-------+
Here the tables 1_Info, 2_Info, 3_Info are the tables corresponding to students with Id = 1, Id = 2, Id = 3 respectively.
My questions are
If I insert a subject name into the Subject_Info table I must insert the same subject name in all the individual tables corresponding to each student (1_Info, 2_Info, 3_Info).
I used the following query to get the table name; it works fine.
SELECT CONCAT(Id, '_Info') FROM Student_Info;
I tried to write it using a single query like this
INSERT INTO (SELECT CONCAT(Id, '_Info') FROM Student_Info) VALUES ('Physics');
But getting error.
Is it possibles to do this in single query?
If yes then where am I doing mistake?
Is it a good way to give names to the tables that I am using here for each students subject related parameters (1_Info, 2_Info etc)? If not please suggest a good way.
I think it would be more easy to put the information in 1 Table, like this for example
Student
+----+--------+
| Id | Name |
+----+--------+
| 1 | Sam |
| 2 | Taylor |
| 3 | Rick |
+----+--------+
Subject
+----+-------------+
| Id | Subject |
+----+-------------+
| 1 | Physics |
| 2 | Chemistry |
| 3 | Mathematics |
+----+-------------+
Info
+------------+------------+-------+
| student_id | subject_id | Marks |
+------------+------------+-------+
| 1 | 1 | 60 |
| 1 | 2 | 40 |
| 1 | 3 | 80 |
| 2 | 1 | 70 |
| 2 | 2 | 50 |
| 2 | 3 | 60 |
| 3 | 1 | 70 |
| 3 | 2 | 70 |
| 3 | 3 | 70 |
+------------+------------+-------+
And the INSERT would be
INSERT INTO Student(Name) VALUES('Tom');
INSERT INTO Subject(Subject) VALUES('History');
INSERT INTO Info(4, 4, 80);
EDIT: Alternative, this is what I think is called "Third Normal Form (3NF)"
Student
+----+--------+
| Id | Name |
+----+--------+
| 1 | Sam |
| 2 | Taylor |
| 3 | Rick |
+----+--------+
Subject
+----+-------------+
| Id | Subject |
+----+-------------+
| 1 | Physics |
| 2 | Chemistry |
| 3 | Mathematics |
+----+-------------+
Marks
+----+------+
| Id | Mark |
+----+------+
| 1 | 60 |
| 2 | 40 |
| 3 | 80 |
| 4 | 70 |
| 5 | 50 |
+----+------+
Info
+------------+------------+---------+
| student_id | subject_id | mark_id |
+------------+------------+---------+
| 1 | 1 | 1 |
| 1 | 2 | 2 |
| 1 | 3 | 3 |
| 2 | 1 | 4 |
| 2 | 2 | 5 |
| 2 | 3 | 1 |
| 3 | 1 | 4 |
| 3 | 2 | 4 |
| 3 | 3 | 4 |
+------------+------------+---------+