Need help on query on same table in MySQL - mysql

I have a table with some data, and I need to make a query using that table.
I have:
+----+-----------------+--------+--------+
| Id | Name | Parent | Mark |
+----+-----------------+--------+--------+
| 1 | Name 1 | 0 | 0 |
| 2 | Name 2 | 1 | 20 |
| 3 | Name 3 | 2 | 45 |
| 4 | Name 4 | 0 | 50 |
+----+-----------------+--------+--------+
and I need:
+----+-----------------+--------+--------+
| Id | Name | Parent | Mark |
+----+-----------------+--------+--------+
| 2 | Name 2 | Name 1 | 20 |
| 3 | Name 3 | Name 2 | 45 |
+----+-----------------+--------+--------+
How can I run the query in MySQL?

You can get your result by running this query.
Select tablename.*
from tablename t1
inner join tablename t2 on t2.Id = t1.Parent ;

you can do the inner join in the same table and check if the user have parent or not
try something like
select * from marksTbl m inner join marksTbl p on m.Id = p.Parent

You can use the following using a INNER JOIN. Using this solution also exclude all rows without a matching parent.
SELECT t1.Id, t1.Name, t2.Name AS Parent, t1.Mark
FROM table_name t1 INNER JOIN table_name t2 ON t1.Parent = t2.Id
ORDER BY t1.Id ASC
In case you want to see all rows (also the rows without (matching) parents) you can use the following with LEFT JOIN:
SELECT t1.Id, t1.Name, t2.Name AS Parent, t1.Mark
FROM table_name t1 LEFT JOIN table_name t2 ON t1.Parent = t2.Id
ORDER BY t1.Id ASC
You can also use a sub-select instead of a join:
SELECT Id, Name, (SELECT Name FROM table_name t2 WHERE t2.Id = t1.Parent) AS Parent, Mark
FROM table_name t1
WHERE t1.Parent > 0
ORDER BY t1.Id ASC
demo on dbfiddle.uk

Related

Display child and parent relationship (if any) in a same table

I have this table
| id |parent|name|
| 1 | NULL | E |
| 2 | NULL | B |
| 3 | 5 | U |
| 4 | 5 | X |
| 5 | NULL | C |
| 6 | NULL | A |
I would like the list, ordered by parent's name, of all ID whether they have a parent or not:
| id |parent|name|has_child|
| 6 | NULL | A | 0 |
| 2 | NULL | B | 0 |
| 5 | NULL | C | 1 |
| 3 | 5 | U | 0 |
| 4 | 5 | X | 0 |
| 1 | NULL | E | 0 |
Is it possible?
I have tried many things but never get the proper answer, and I don't really know how to add the 'has_child' column
SELECT
t1.parent,
t2.name
FROM tablename AS t1
INNER JOIN
(
SELECT MIN(id) AS id, parent
FROM tablename
GROUP BY parent
) AS t22 ON t22.id = t1.id AND t1.parent = t22.parent
INNER JOIN tablename AS t2 ON t1.parent = t2.id;
I would use a self join here:
SELECT DISTINCT
t1.id,
t1.parent,
t1.name,
1 - ISNULL(t2.id) has_child
FROM tablename t1
LEFT JOIN tablename t2
ON t1.id = t2.parent
ORDER BY
t1.id;
The join condition used here, which matches a given record as a parent to one or more children, is that the current id is also the parent of some other record(s). Note that we need SELECT DISTINCT here, because a given parent might match to more than one child record.
You can use a self join -- because you want the name of the parent and not the id -- and coalesce() for ordering:
select t.*,
(case when exists (select 1 from t tc where tc.parent = t.id)
then 1 else 0
end)
from t left join
t tp
on t.parent = tp.id
order by coalesce(tp.name, t.name), -- group rows by the parent, if any
(tp.name is null) desc, -- put parent first
t.name; -- order by children
I hope that you find this answer a little bit useful. The subquery gets the distinct id of parents and excludes the blanked fills.
SELECT *,
CASE WHEN id IN (SELECT DISTINCT parent
FROM tablename
WHERE parent IS NOT NULL)
THEN '1' ELSE '0'
END AS has_child
FROM tablename
ORDER BY name;
SELECT t1.id, t1.parent, t1.name, MAX(t2.parent is not null) has_child
FROM table t1
LEFT JOIN table t2 ON t1.id = t2.parent_id
GROUP BY t1.id, t1.parent, t1.name

How to select all rows from group, until occurrence of a value

I'm trying to extract all rows from same Group until I hit breakpoint value B. The example data below is ordered virtual table:
+----+--------+------------+
| ID | Group | Breakpoint |
+----+--------+------------+
| 1 | 1 | A |
| 2 | 1 | A |
| 3 | 1 | B |
| 4 | 1 | A |
| 5 | 2 | A |
| 6 | 2 | A |
| 7 | 2 | A |
| 8 | 3 | A |
| 9 | 3 | B |
+----+--------+------------+
This would be my result.
+----+--------+------------+
| ID | Group | Breakpoint |
+----+--------+------------+
| 1 | 1 | A |
| 2 | 1 | A |
| 5 | 2 | A |
| 6 | 2 | A |
| 7 | 2 | A |
| 8 | 3 | A |
+----+--------+------------+
Notice that when there are both A and B breakpoint values within a group, I want to have the rows until the first A value in this order. If there are only A values for a group like in group 2, I want to have all of the items in the group.
Here's a simple solution that uses no subqueries or GROUP BY logic.
SELECT t1.ID, t1.Group, t1.Breakpoint
FROM MyTable AS t1
LEFT OUTER JOIN MyTable AS t2
ON t1.ID >= t2.ID AND t1.`Group` = t2.`Group` AND t2.Breakpoint = 'B'
WHERE t2.ID IS NULL
For each row t1, try to find another row t2 with 'B', in the same Group, with an earlier ID. If none is found, the OUTER JOIN guarantees that t2.ID is NULL. That will be true only up until the desired breakpoint.
From you example above, you are not really grouping the results. you just need to display the records where Breakpoint is A:
Select * From Table
Where Breakpint ='A'
You may use NOT EXISTS
select *
from your_table t1
where not exists (
select 1
from your_table t2
where t1.group = t2.group and t2.id <= t1.id and t2.breakpoint = 'B'
)
or ALL can work as well if you never have NULL in id
select *
from your_table t1
where t1.id < ALL(
select t2.id
from your_table t2
where t1.group = t2.group and t2.breakpoint = 'B'
)
Assuming that we are ordering by ID column, we could do something like this:
SELECT d.*
FROM mytable d
LEFT
JOIN ( SELECT bp.group
, MIN(bp.id) AS bp_id
FROM mytable bp
WHERE bp.breakpoint = 'B'
GROUP BY bp.group
) b
ON b.group = d.group
WHERE b.bp_id > d.id OR b.bp_id IS NULL
ORDER BY d.group, d.id
This takes into account cases where there is no breakpoint='B' row for a given group, and returns all of the rows for that group.
Note that the inline view b gets us the lowest id value from rows with breakpoint='B' for each group. We can outer join that to original table (matching on group), and then conditional tests in the WHERE clause to exclude rows that follow the first breakpoint='B' for each group.
SQL tables represent unordered sets. Hence, there is no "before" or "after" a particular row.
Let me assume that you have some column that specifies the ordering. I'll call it id. You can then do what you want with:
select t.*
from t
where t.id < (select min(t2.id) from t t2 where t2.group = t.group and t2.breakpoint = 'B');
To get all rows when if there are no 'B':
select t.*
from t
where t.id < (select coalesce(min(t2.id), t.id + 1) from t t2 where t2.group = t.group and t2.breakpoint = 'B');

Count records in one table where there are no records in linked table matching certain criteria

I have 2 tables:
T1:
id | name
------ | ------
1 | Bob
2 | John
3 | Joe
T2:
id | T1_id | type
------ | ------ | ------
1 | 1 | call
2 | 1 | email
3 | 1 | fax
4 | 2 | call
5 | 2 | email
6 | 2 | fax
7 | 3 | call
8 | 3 | email
I want to count the number of records in T1 which do not have a record in T2 with a type of 'fax'.
So the answer in this case would be 1 (3|Joe)
Currently I have:
SELECT count(*)
FROM `T1`
JOIN `T2` on `T1`.`id` = `T2`.`T1_id`
WHERE `T2`.`type` != 'fax'
But this is obviously counting all the records which are not 'fax'. I just cant get the logic in my head.
Any help would be appreciated!
A subquery is unnecessary:
SELECT COUNT(DISTINCT t1.id)
FROM t1
LEFT
JOIN t2
ON t2.t1_id = t1.id
AND t2.type = 'fax'
WHERE t2.id IS NULL;
select count(*)
from
(
SELECT t1.id
FROM T1
LEFT JOIN T2 on T1.id = T2.T1_id
GROUP BY t1.id
HAVING sum(T2.type = 'fax') = 0
) tmp
The answers given by Strawberry and juergen d are correct, but for completeness, here's another example using NOT EXISTS. All the queries will have different execution plans, so depending on your data in T1 and T2 YMMV:
SELECT COUNT(*)
FROM `T1`
WHERE NOT EXISTS (
SELECT *
FROM `T2`
WHERE `T2`.`T1_id` = `T1`.`id`
AND `T2`.`type` = 'fax'
)

SQL to group a similar table by name

I have two similar tables
Table 1
| id | name | amount|
| 2 | Mike | 1000 |
| 3 | Dave | 2500 |
Table 2
| id | name | amount|
| 2 | Mike | 1200 |
| 4 | James| 2500 |
I want to query the tables to get a result like this:
| id | name | amount_table1| amount_table2|
| 2 | Mike | 1000 | 1200 |
| 3 | Dave | 2500 | |
| 4 | james| | 2500 |
UNION ALL the tables. Do GROUP BY to get one row per id/name combo.
select id, name, sum(amount1), sum(amount2)
from
(
select id, name, amount as amount1, null as amount2 from table1
union all
select id, name, null, amount from table2
) dt
group by id, name
You need to do union with left and right join
select a.id , a.name , a.amount amount_table1,b.amount amount_table2 from table1 a left join table2 b on (a.id=b.id)
union
select b.id , b.name ,a.amount,b.amount from table1 a right join table2 b on (a.id=b.id)
MySql doesn't support FULL OUTER JOIN.
But it supports LEFT & RIGHT joins and UNION.
select
t1.id, t1.name, t1.amount as amount_table1, t2.amount as amount_table2
from Table1 t1
left join Table2 t2 on t1.id = t2.id
union all
select t2.id, t2.name, t1.amount, t2.amount
from Table2 t2
left join Table1 t1 on t2.id = t1.id
where t1.id is null
The first select will get those only in Table1 and those in both.
The second select will get those only in Table2.
And the UNION glues those resultsets together.
If this were for a database that supports FULL JOIN then it would be simplified to:
select
coalesce(t1.id, t2.id) as id,
coalesce(t1.name, t2.name) as name,
t1.amount as amount_table1,
t2.amount as amount_table2
from Table1 t1
full join Table2 t2 on t1.id = t2.id

How to show parent record Id in child record column-Mysql

I want to show my parent id with child record(duplicate record). Here is my table
ID|Name |Comments|
__|_____|________|_
1 |Test1|Unique |
2 |Test2|Unique |
3 |Test1|Unique |
4 |Test2|Unique |
5 |Test1|Unique |
6 |Test3|Unique |
Expected Result:
ID|Name |Comments |
__|_____|__________________|_
1 |Test1|Unique |
2 |Test2|Unique |
3 |Test1|Duplicate with: 1 |
4 |Test2|Duplicate with: 2 |
5 |Test1|Duplicate with: 1 |
6 |Test3|Unique |
not sure what the exact goal here, but here is a single query that get the job done:
mysql> select ID,tbl.Name,if(no!=ID,concat('Duplicate with: ',no),'Unique') Comments from tbl left join (select ID no,Name from tbl group by Name) T on T.Name=tbl.Name;
+----+-------+-------------------+
| ID | Name | Comments |
+----+-------+-------------------+
| 1 | Test1 | Unique |
| 2 | Test2 | Unique |
| 3 | Test1 | Duplicate with: 1 |
| 4 | Test2 | Duplicate with: 2 |
| 5 | Test1 | Duplicate with: 1 |
| 6 | Test3 | Unique |
+----+-------+-------------------+
Check This Live Demo using 'coalesce' and 'Case when'
Query :
select id
,name
,coalesce(
( select coalesce(case when min(id)>0 then concat('Duplicate with : ',min(id)) else null end,Comments)
from Yourtable t2 where t2.name = t.name and t2.id < t.id group by Comments)
,Comments) as Comments
from Yourtable t
order by id
Output :
hey you can try this query and it is giving expected result.
select t1.id,t1.`name`,
CASE WHEN (select count(`name`) from table_name t2 where t2.`name` = t1.`name` and t2.id <= t1.id ) = 1
then 'unique'
else CONCAT('Duplicate with :',(select min(t3.id) from table_name t3 where t3.name = t1.`name`))
end as 'comments'
from table_name t1
replace table_name with your table.
Hope this works for you. Ask if any doubt
Using only a single sub-query.
select id
,name
,coalesce
(
concat
(
'Duplicate with: '
,(select min(id) from mytable t2 where t2.name = t.name and t2.id < t.id)
)
,'Unique'
) as Comments
from mytable t
order by id