MySQL query to bind parent row values to children rows - mysql

I have a table like this (greatly simplified):
| id | firstName | lastName | parent |
|------------------------------------|
| 1 | NULL | Smith | NULL |
| 2 | John | NULL | 1 |
| 3 | Jack | NULL | 1 |
| 4 | Jimmi | Joe | 1 |
| 5 | Jane | Doe | NULL |
And I'm trying to create a query that can return the data like this:
| id | firstName | lastName |
|---------------------------|
| 1 | NULL | Smith |
| 2 | John | Smith |
| 3 | Jack | Smith |
| 4 | Jimmi | Joe |
| 5 | Jane | Doe |
Basically I want it to include all rows, including parent rows. In all child rows (where parent != NULL) I want it to pick the child field value, unless it it NULL, then it should pick the parent field value. Parent rows are not modified.
I've been trying different setups with different joins, but I can't get it right. Is this even possible?

Joining parent with id using the parent as the table 1, this is because if we put data_2.parent = data.id then it would have joined incorrectly and we would have 3 rows with id 1 instead of 1 row.
Once we have the join we just take the first non-null value using coalesce() . If the last name is present in the original table then we take that otherwise we use the new one that we create using the join above
code 1 :
WITH data_ AS (
SELECT data.*, data_2.last_name as new_last_name,
FROM [table name] as data
LEFT JOIN data_2
ON data.parent = data_2.id
)
SELECT
*,
COALESCE(data_.last_name,data_.new_last_name) AS final_name FROM data_
code 2: more direct
SELECT
data.id,
data.first_name,
COALESCE(data.last_name,data_2.new_last_name) as final_LAST NAME
FROM [table name] as data
LEFT JOIN data_2
ON data.parent = data_2.id

you can self join the table with an LEFT JOIN on parent and id
SELECT t1.`id`, t1.`firstName`, COALESCE(t1.`lastName`,t2.`lastName`) FROM table1 t1 LEFT JOIN table1 t2 ON t1.`parent` = t2.id
id | firstName | COALESCE(t1.`lastName`,t2.`lastName`)
-: | :-------- | :------------------------------------
1 | null | Smith
2 | John | Smith
3 | Jack | Smith
4 | Jimmi | Joe
5 | Jane | Doe
db<>fiddle here

Related

I want to select data in 1 table

structure table
Pk id <------------| primary Key
name |
country |
fk parent_id <-----| forigen key
Data
id| name | country | parent_id
1 | Diva | Portugal | 2
2 | Alex | Georgia | 2
3 | Bianca | Palau | 4
4 | Tony | Montenegro | 1
result
id| name | country | parent_id | name_parent_id |
1 | Diva | Portugal | 2 | Alex |
2 | Alex | Georgia | 2 | Alex |
3 | Bianca | Palau | 4 | Tony |
4 | Tony | Montenegro | 1 | Diva |
the result for this case
Any advice on this one?
You can use the same table twice in order to select the parent name:
SELECT
c.id,
c.name,
c.country,
c.parent_id,
p.name AS name_parent_id
FROM
YourTable AS c
INNER JOIN YourTable AS p ON c.id = p.parent_id
I used c as alias for the child and p for the parent.
Note: You can use LEFT JOIN instead of INNER JOIN in case of the existence of the parent is not required.

How to get records from below tables

I have 3 tables
QUESTION table with below 2 properties
1. ID (serial)
2. Question (varchar)
ANSWER table with below 4 properties
1. ID (serial)
2. QuestionID (foreign key to table QUESTION)
3. StudentID (foreign key to table STUDENT)
4. ANSWER (varchar)
5. SubmitDateTime (datetime)
STUDENT table with below properties
1. ID (serial)
2. Name (varchar)
I just want to show records with each student (one record for each student) with every answer. If any question's answer is not given by the student it will show blank.
For example:
QUESTION TABLE
| ID | QUESTION |
|----- | --------- |
| 1 | A FOR? |
| 2 | B FOR? |
| 3 | C FOR? |
ANSWER TABLE
| ID | QuestionID | StudentID | ANSWER | SubmitDateTime |
|----- | ----------- |------------|--------|----------------|
| 1 | 1 | 1 |Apple | something date |
| 2 | 1 | 2 |Ant | something date |
| 3 | 2 | 1 |Book | something date |
| 4 | 3 | 2 |Cat | something date |
STUDENT TABLE
| ID | NAME |
|----- | --------- |
| 1 | Jhon |
| 2 | Lily |
Expected Records
Result table
| ID | NAME | Answers |
|----- | --------- | ---------------------|
| 1 | Jhon | Apple,Book,<blank> |
| 2 | Lily | Ant,<blank>,Cat |
"blank" means no record will be shown instead of a blank space or a hyphane.
My implementation:
SELECT s.ID,s.Name,
GROUP_CONCAT(a.answer SEPARATOR ',') AS answers
FROM student AS s
LEFT JOIN answer AS a ON a.studentID=s.ID
WHERE a.submitdate BETWEEN '<somedate>' AND '<somedate>'
GROUP BY s.ID ORDER BY a.ID ASC
It does not give me a blank answer. How to get these?
Try this:
SELECT sid, sname, GROUP_CONCAT(IFNULL(a.answer,'-') ORDER BY qid)
FROM
(SELECT q.id AS qid, s.id AS sid, s.Name AS sname
FROM question q CROSS JOIN student s) qs
LEFT JOIN answer a ON qs.qid=a.QuestionID AND qs.sid=a.StudentID
GROUP BY sid, sname;
The base query is a CROSS JOIN between question and student tables that will be a subquery and give a result like this:
+-----+-----+-------+
| qid | sid | sname |
+-----+-----+-------+
| 1 | 2 | Lily |
| 1 | 1 | Jhon |
| 2 | 2 | Lily |
| 2 | 1 | Jhon |
| 3 | 2 | Lily |
| 3 | 1 | Jhon |
+-----+-----+-------+
As you can see, each of the student will be paired with all of the existing question regardless of their answer records in answer table. This will be the reference for the LEFT JOIN with answer table.
Demo fiddle

Retrieve a value from a JOIN as if it was in the base table

Let's say I have two tables, one with firstnames, and another with lastnames:
firstnametable
---------------------------------------------
| id | firstname | |
---------------------------------------------
| 1 | John | |
| 2 | Pete | |
---------------------------------------------
lastnametable
---------------------------------------------
| id | lastname | firstname_id | active |
---------------------------------------------
| 1 | Dough | 1 | 0 |
| 2 | Do | 1 | 1 |
| 2 | Sampres | 2 | 1 |
---------------------------------------------
I am looking for Query such that it would appear as if it came from a single table:
firstnametable
---------------------------------------------
| id | firstname | lastname | |
---------------------------------------------
| 1 | John | Do | |
| 2 | Pete | Sampres | |
---------------------------------------------
But it is important for me that the results are delivered in such a fashion that the prefix is also as if the results are all from the original table, ie:firstnametable.firstname and firstnametable.lastname
Is this at all possible?
My current solution is:
SELECT firstnametable.id,
firstnametable.firstname,
CONCAT(jointable.name) as lastname
FROM firstnametable
LEFT JOIN (
SELECT lastname
FROM lastnametable
WHERE active = 1
) as jointable ON (firstnametable.id = jointable.firstname_id)
But I am still missing my desired firstnametable prefix for the lastname column.
You need to include the firstname id in the jointtable select and I don't know what the purpose of the concat is since you aren't concating anything but you haven't selected name anywhere perhaps you meant lastname. If you want to include the table name in the headers use an alias.
SELECT firstnametable.id as 'firstnametable.id',
firstnametable.firstname as 'firstnametable.firstname',
jointable.lastname as 'firstnametable.lastname'
FROM firstnametable
LEFT JOIN (
SELECT firstname_id,
lastname
FROM lastnametable
WHERE active = 1
) as jointable ON firstnametable.id = jointable.firstname_id;
+-------------------+--------------------------+-------------------------+
| firstnametable.id | firstnametable.firstname | firstnametable.lastname |
+-------------------+--------------------------+-------------------------+
| 1 | John | Do |
| 2 | Pete | Sampres |
+-------------------+--------------------------+-------------------------+
2 rows in set (0.001 sec)

Select distinct ordered pair from table join

Please consider two tables with names. They are joined by Table A id, so that two names get associated.
Is there a MySQL query that returns distinct pair of names regardless the order?
First table:
table_a
+-----------+--------------+
| id | name |
+-----------+--------------+
| 1 | John |
+-----------+--------------+
| 2 | Jane |
+-----------+--------------+
| 3 | Jane |
+-----------+--------------+
| 4 | Sammy |
+-----------+--------------+
Second Table:
table_b
+-----------+-------------------+-------------+
| id | id_table_a | name |
+-----------+-------------------+-------------+
| 1 | 1 | Jane |
+-----------+-------------------+-------------+
| 2 | 2 | John |
+-----------+-------------------+-------------+
| 3 | 3 | Sammy |
+-----------+-------------------+-------------+
| 4 | 4 | Tara |
+-----------+-------------------+-------------+
Desired result
(John, Jane)
(Jane, Sammy)
(Sammy, Tara)
Thanks in advance!
Here's one option using least and greatest:
select distinct least(a.name, b.name), greatest(a.name, b.name)
from table_a a
join table_b b on a.id = b.id_table_a
SQL Fiddle Demo

Show the column not null of two tables in SQL

I would like to join two tables and select from two columns the first one if it is not null, of the other if the first is null. As an example imagine that we have the following tables:
names companies_to_names
-------------------------------- -----------------------------
|id_name | name | nickname | | id | id_name | id_company |
-------------------------------- -----------------------------
| 1 | NULL | manu | | 1 | 1 | 1 |
| 2 | Joe A. | NULL | | 2 | 2 | 1 |
| 3 | Bob B. | NULL | | 3 | 3 | 1 |
| 4 | NULL | alice | | 4 | 4 | 1 |
| 5 | NULL | other | | 5 | 5 | 2 |
-------------------------------- -----------------------------
And we want to show either the name, or the nickname of the guys who work for the company with id=1. Then, I want the following result:
--------------------
|id_name | username|
--------------------
| 1 | manu |
| 2 | Joe A. |
| 3 | Bob B. |
| 4 | alice |
--------------------
I was thinking in SELECT CASE WHEN, but I don't know how to do it. Something like:
SELECT NAMES.id_name CASE username
WHEN NAMES.name IS NULL THEN NAMES.nickname
WHEN NAMES.name IS NOT NULL THEN NAMES.name
END
FROM NAMES INNER JOIN COMPANIES_TO_NAMES ON NAMES.id_name = COMPANIES_TO_NAMES.id_name;
Am I right?
Here is a query that shows you how to solve your problem:
SELECT N.id_name
,IFNULL(N.name, N.nickname) AS [username]
,CASE
WHEN N.name IS NOT NULL THEN 'name'
ELSE 'nickname'
END AS [username_source]
FROM NAMES N
INNER JOIN companies_to_names C ON C.id_name = N.id_name
AND C.id = 1
Hope this will help you.