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

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');

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

Need help on query on same table in 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

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

MySQL - Count only if the last associated_statut = 2

Alright, I have those columns on MySQL :
id
id_conv
associated_statut
The associated_statut is a number between 1 and 7.
What I want to do is to count only the id_conv if the LAST associated_statut for this id_conv is 2 for example.
Example :
-----------------------------------------------
| id | id_conv | associated_statut |
-----------------------------------------------
| 1 | 15 | 1 |
| 2 | 15 | 2 |
| 3 | 15 | 2 |
| 4 | 15 | 4 |
| 5 | 15 | 2 |
| 6 | 15 | 3 |
The id_conv would NOT be counted if I want the associated_statut = 2, because the last associated_statut for this id_conv is 3.
I already tried this query :
SELECT COUNT(DISTINCT id_conv) FROM MyTable WHERE associated_statut = 2
But this doesn't returns what I want.
Is there a way to do this in SQL ?
Thanks.
Maybe, this will work for you:
SELECT count(t1.id) FROM mytable t1
INNER JOIN (SELECT id_conv, MAX(id) id FROM foo GROUP BY id_conv) t2
ON t1.id = t2.id
WHERE t1.associated_statut = 2
SELECT COUNT(sub1.id_conv) FROM MyTable
INNER JOIN
(
SELECT DISTINCT id, FIRST(associated_statut ORDER BY id DESC)
group by id_conv
recent FROM MyTable
) sub1 ON sub1.id = MyTable.id
WHERE sub1.recent_associated_statut = 2
We can do same thing without sub query. It will take less time when you have more data.
SELECT count(t1.id) FROM
mytable t1
LEFT JOIN
mytable t2
ON t1.id_conv = t2.id_conv
AND t1.id < t2.id
WHERE t2.id IS NULL
AND t1.associated_statut = 2;

Mysql - Select at least one or select none

I have a table as so...
----------------------------------------
| id | name | group | number |
----------------------------------------
| 1 | joey | 1 | 2 |
| 2 | keidy | 1 | 3 |
| 3 | james | 2 | 2 |
| 4 | steven | 2 | 5 |
| 5 | jason | 3 | 2 |
| 6 | shane | 3 | 3 |
----------------------------------------
I'm running a select like so:
SELECT * FROM table WHERE number IN (2,3);
The problem im trying to solve is that I want to only grab get results from groups that have 1 or more rows of each number. For instance the above query is returning id's 1-2-3-5-6, when I'd like the results to exclude id 3 since the group of '2' can only return 1 result for the number of '2' and not for BOTH 2 and 3, since there's no row with the number 3 for the group 2 i'd like it to not even select id 3 at all.
Any help would be great.
Try it this way
SELECT *
FROM table1 t
WHERE number IN(2, 3)
AND EXISTS
(
SELECT *
FROM table1
WHERE number IN(2, 3)
AND `group` = t.`group`
GROUP BY `group`
HAVING MAX(number = 2) > 0
AND MAX(number = 3) > 0
)
or
SELECT *
FROM table1 t JOIN
(
SELECT `group`
FROM table1
WHERE number IN(2, 3)
GROUP BY `group`
HAVING MAX(number = 2) > 0
AND MAX(number = 3) > 0
) q
ON t.`group` = q.`group`;
or
SELECT *
FROM table1
WHERE `group` IN
(
SELECT `group`
FROM table1
WHERE number IN(2, 3)
GROUP BY `group`
HAVING MAX(number = 2) > 0
AND MAX(number = 3) > 0
);
Sample output (for both queries):
| ID | NAME | GROUP | NUMBER |
|----|-------|-------|--------|
| 1 | joey | 1 | 2 |
| 2 | keidy | 1 | 3 |
| 5 | jason | 3 | 2 |
| 6 | shane | 3 | 3 |
Here is SQLFiddle demo
On this, you can approach from a fun way with multiple joins for what you WANT qualified, OR, apply a prequery to get all qualified groups as others have suggested, but readability is a bit off for me..
Anyhow, here's an approach going through the table once, but with joins
select DISTINCT
T.id,
T.Name,
T.Group,
T.Number
from
YourTable T
Join YourTable T2
on T.Group = T2.Group AND T2.Group = 2
Join YourTable T3
on T.Group = T3.Group AND T3.Group = 3
where
T.Number IN ( 2, 3 )
So on the first record, it is pointing to by it's own group to the T2 group AND the T2 group is specifically a 2... Then again, but testing the group for the T3 instance and T3's group is a 3.
If it cant complete the join to either of the T2 or T3 instances, the record is done for consideration, and since indexes work great for joins like this, make sure you have one index for your NUMBER criteria, and another index on the (GROUP, NUMBER) for those comparisons and the next query sample...
If doing by more than this simple 2, but larger group, prequery qualified groups, then join to that
select
YT2.*
from
( select YT1.group
from YourTable YT1
where YT1.Number in (2, 3)
group by YT1.group
having count( DISTINCT YT1.group ) = 2 ) PreQualified
JOIN YourTable YT2
on PreQualified.group = YT2.group
AND YT2.Number in (2,3)
Maybe this,if I understand you
SELECT id FROM table WHERE `group` IN
(SELECT `group` FROM table WHERE number IN (2,3)
GROUP BY `group`
HAVING COUNT(DISTINCT number)=2)
SQL Fiddle
This will return all ids where BOTH numbers exist in a group.Remove DISTINCT if you want ids for groups where just one numbers is in.