Recursive query on same table MySQL and concat column - mysql

Below is my table:
I have the below table structure :
id | parentid | Name
1 | NULL | W
2 | 1 | O
3 | 1 | R
4 | 2 | L
5 | 4 | D
I hope to get the following result, the result will recursively find the parentid until it found parentid of NULL. It will append all the Name and create a new column called Code
id | parentid | Name | Code
1 | NULL | W | W
2 | 1 | O | W,O
3 | 1 | R | W,R
4 | 2 | L | W,O,L
5 | 4 | D | W,O,L,D
This is what I have tried:
WITH RECURSIVE cte as
(
SELECT id,
id nextid,
parentid
FROM table
UNION ALL
SELECT cte.id,
t1.id,
t1.parentid
FROM table
JOIN cte
ON cte.parentid = p.id
)
SELECT Id, nextid RootId
FROM cte
WHERE parentid IS NULL

You can do it as follows :
The CAST() here is to make the Code column wider.
WITH RECURSIVE cte as
(
SELECT id,
parentid,
name,
CAST(name AS CHAR(50)) AS Code
FROM mytable where parentid is null
UNION ALL
SELECT t1.id,
t1.parentid,
t1.name,
CONCAT(c.Code, ',', t1.name)
FROM cte c
INNER JOIN mytable t1
ON c.id = t1.parentid
)
SELECT *
FROM cte;
Demo here

Related

How do I get the first n records, per foreign key with MySQL?

I have the following table:
+----+-----------+------+
| id | table2_id | type |
+----+-----------+------+
| 1 | 100 | A |
| 2 | 100 | B |
| 3 | 100 | C |
| 4 | 100 | A |
| 5 | 250 | A |
+----+-----------+------+
I need a select statement that would get all the records before the first occurrence of type C, per table2_id.
So I want records 1, 2, and 5
I'd do this in code with a loop, but I need to do it in MySQL specifically.
If you are running MySQL 8.0, you can do this with window functions:
select *
from (
select t.*,
min(case when type = 'C' then id end) over(partition by table2_id) min_id
from mytable t
) t
where min_id is null or id < min_id
In all versions, you could use not exists:
select t.*
from mytable t
where not exists (
select 1
from mytable t1
where t1.table2_id = t.table2_id and t1.id <= t.id and t1.type = 'C'
)

How to get data like first capital by lower cases in mysql?

New to mysql,
I have a table like this.
___|____
Id | name
1 | a
2 | b
3 | c
4 | A
5 | B
6 | C
What will be the query to get the result like this in sql
___|____
Id | name
4 | A
1 | a
5 | B
2 | b
6 | C
3 | c
Editted
This seams to be the most easy way changed DDS idea into a more general working one for all MySQL versions..
Query
SELECT
id
, name
FROM
Table1
ORDER BY
CASE
WHEN name COLLATE latin1_bin BETWEEN 'A' AND 'Z'
THEN ASCII(name) + 31
ELSE ASCII(name)
END
Result
| id | name |
| --- | ---- |
| 4 | A |
| 1 | a |
| 5 | B |
| 2 | b |
| 6 | C |
| 3 | c |
View on DB Fiddle
Explaining View so you can see what happens on DB Fiddle
Or the more stabile one because of the unique generated values for the calculated_ascii_value column.
Explaining View so you can see what happens on DB Fiddle
The other query is pretty complex..
Query
SELECT
Table1.id
, Table1.name
FROM (
SELECT
DISTINCT
(
SUBSTRING_INDEX(SUBSTRING_INDEX(#orderList, ',', number_generator.number), ',', -1)
COLLATE utf8mb4_bin
) AS letter
FROM (
SELECT
(#number := #number + 1) AS number
FROM (
SELECT 1 AS number UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10
) AS row_1
CROSS JOIN (
SELECT 1 AS number UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6
) AS row_2
CROSS JOIN (SELECT #number := 0) AS init_user_param
) AS number_generator
CROSS JOIN (SELECT #orderList := 'A,a,B,b,C,c' /* add all to Z,z */) AS init_user_param
) AS letters
INNER JOIN
Table1
ON
letters.letter = Table1.name
;
Results
| id | name |
| --- | ---- |
| 4 | A |
| 1 | a |
| 5 | B |
| 2 | b |
| 6 | C |
| 3 | c |
View on DB Fiddle
Note there are some things you should know
COLLATE utf8mb4_bin might be changed to COLLATE utf8_bin instead when your MySQL uses utf8 charset.
And SELECT #orderList := 'A,a,B,b,C,c' /* add all to Z,z */ you need to might add more to Z,z
this will make a new column 'newid' with the order you need
select newid = row_number() over (order by case when val between 'A' and 'Z'
then ascii(val) -31
else ascii(val) end), *
from yourtable
Try like this. I Just used database from w3schools, need to check if works in MySql.
SELECT * FROM myTable GROUP BY name ORDER BY UPPER(name), LOWER(name);

Select row by column id AND include rows that contain the same value from another column if null

I am trying to get a row by ID but also include rows that have the same value in other column but not including them if the value is null or empty.
Data:
+------+------+------+
|ID GROUP |AREA |
+------+------+------+
| 1 | A | AA |
+------+------+------+
| 2 | | AA |
+------+------+------+
| 3 | A | AA |
+------+------+------+
| 4 | B | AA |
+------+------+------+
| 5 | | BB |
+------+------+------+
| 6 | A | AA |
+------+------+------+
| 7 | B | BB |
+------+------+------+
| 8 | | AA |
+------+------+------+
What I have now:
SELECT * WHERE ID = 1 AND AREA = "AA"
Which returns:
+------+------+------+
| 1 | A | AA |
+------+------+------+
But I also want to get all the rows that contain GROUP "A":
+------+------+------+
| 1 | A | AA |
+------+------+------+
| 3 | A | AA |
+------+------+------+
| 6 | A | AA |
+------+------+------+
But would need to return just the ID requested if the "GROUP" column is Null.
SELECT * WHERE ID = 2 AND AREA = "AA"
+------+------+------+
| 2 | | AA |
+------+------+------+
I've tried everything I can think of. Different joins and sub-queries, but I can't seem to make this work.
You can try to write the condition in the subquery, then do self-join by the subquery.
JOIN condition on AREA and GROUP columns, if GROUP is null addition to check id.
CREATE TABLE T(
ID INT,
`GROUP` VARCHAR(5),
AREA VARCHAR(5)
);
INSERT INTO T VALUES ( 1 , 'A' , 'AA' );
INSERT INTO T VALUES ( 2 , NULL , 'AA' );
INSERT INTO T VALUES ( 3 , 'A' , 'AA' );
INSERT INTO T VALUES ( 4 , 'B' , 'AA' );
INSERT INTO T VALUES ( 5 , NULL , 'BB' );
INSERT INTO T VALUES ( 6 , 'A' , 'AA' );
INSERT INTO T VALUES ( 7 , 'B' , 'BB' );
INSERT INTO T VALUES ( 8 , NULL , 'AA' );
Query 1:
SELECT t1.*
FROM T t1 INNER JOIN (
SELECT *
FROM T
WHERE ID = 2 AND AREA = "AA"
) t2 ON t1.AREA = t2.AREA and t1.GROUP = t2.GROUP or (t2.GROUP is null and t1.id = t2.id)
Results:
| ID | GROUP | AREA |
|----|--------|------|
| 2 | (null) | AA |
Query 2:
SELECT t1.*
FROM T t1 INNER JOIN (
SELECT *
FROM T
WHERE ID = 1 AND AREA = "AA"
) t2 ON t1.AREA = t2.AREA and t1.GROUP = t2.GROUP or (t2.GROUP is null and t1.id = t2.id)
Results:
| ID | GROUP | AREA |
|----|-------|------|
| 1 | A | AA |
| 3 | A | AA |
| 6 | A | AA |
I am not quite sure I understand you fully, but if you want to get ID by GROUP, perhaps you can try
select distinct ID
from TABLE
group by GROUP
Try this:
SELECT * FROM `table_name` WHERE `ID` = 2 AND `AREA` = 'AA' AND `GROUP` IN (SELECT `GROUP` FROM `table_name` WHERE `ID` = 2);
PS: don't forget to change table_name by the table you're fetching records from.
I'd do this with a UNION
SELECT
ID
,`GROUP`
,AREA
FROM
your_table
WHERE ID = 2
UNION
SELECT
ID
,`GROUP`
,AREA
FROM
your_table
WHERE
`GROUP` = (SELECT `GROUP` FROM your_table WHERE ID = 2)
AND NULLIF(`GROUP`, '') IS NOT NULL;

Calculate sum on the records while joining two tables with a third table

I have three tables:
mysql> select * from a;
+----+---------+
| ID | Name |
+----+---------+
| 1 | John |
| 2 | Alice |
+----+---------+
mysql> select * from b;
+------+------------+----------+
| UID | date | received |
+------+------------+----------+
| 1 | 2017-10-02 | 5 |
| 1 | 2017-09-30 | 1 |
| 1 | 2017-09-29 | 4 |
+------+------------+----------+
mysql> select * from c;
+------+------------+------+
| UID | date | sent |
+------+------------+------+
| 1 | 2017-09-25 | 7 |
| 1 | 2017-09-30 | 2 |
| 1 | 2017-09-29 | 3 |
+------+------------+------+
If I try to calculate the total number of sent for John, it would be 12. And for received, it would be 10.
But if I try to join all three tables, the result is weird. Here is my query to join three tables:
mysql> select sum(sent), sum(received) from a
-> join c on c.UID = a.ID
-> join b on b.UID = a.ID
-> where a.ID = 1;
+-----------+---------------+
| sum(sent) | sum(received) |
+-----------+---------------+
| 36 | 30 |
+-----------+---------------+
But I need correct numbers (12 and 10, respectively). How can I have correct numbers?
You should join the aggregated result and not the raw tables
select a.uid, t1.received, t2.sent
from a
inner join (
select uid, sum(received) received
from b
group by uid
) t1 on t1.uid = a.id
inner join (
select uid, sum(sent) sent
from c
group by uid
) t2 on t2.uid = a.id
where a.id = 1
You could try below
select bx.id, recieved, sum(c.sent) sent from
(
SELECT a.id, sum(b.received) recieved
from a
INNER JOIN b
ON a.id=b.uid
group by a.id
) bx
INNER JOIN c
ON c.uid=bx.id
group by bx.id, bx.recieved;
>>>Demo<<<
This gets rid of the subquery, but introduces something else you might not want:
( SELECT uid, 'Received' AS direction, SUM(received) AS HowMany
WHERE uid = 1
GROUP BY uid )
UNION ALL
( SELECT uid, 'Sent' AS direction, SUM(sent) AS HowMany
WHERE uid = 1
GROUP BY uid )

Query MySQL Group by and Having

I am creating an application for my school and I am in trouble constructing the right query.
I have 2 tables,table1 and table2.
table1
---------------------------------------------------------
| StudentID | SubjectID | Present | Type |
---------------------------------------------------------
| 2 | 3 | yes | 1 |
| 2 | 2 | yes | 2 |
| 3 | 1 | no | 3 |
---------------------------------------------------------
table2
---------------------------------------------------------
| SubjectID | SubjectName | Number1 | Number2 |
---------------------------------------------------------
| 1 | Name1 | 6 | 4 |
| 2 | Name2 | 4 | 8 |
| 3 | Name3 | 5 | 2 |
---------------------------------------------------------
SubjectID in table1 is foreign key references table2.
I want to build a query sql that gives me the StudentID`s from table1
that didnt miss any Type 3 subject (i.e no row like this
---------------------------------------------------------
| StudentID | SubjectID | Present | Type |
---------------------------------------------------------
| 3 | 1 | no | 3 |
---------------------------------------------------------
And have completed 75 percent of type 1 (i.e
I find it like this
SELECT t1.StudentID,t1.SubjectID ,t1.Type,t2.Number1 as num
FROM table1 as t1,table2 as t2
WHERE t1.Present=yes and t2.SubjectID=t1.SubjectID
GROUP BY StudentID,SubjectID
HAVING COUNT(*)/num >= 75/100
But I cant combine the two things together.
You can combine queries by giving them aliases and joining as subqueries...
SELECT finisher.StudentID FROM
(
SELECT DISTINCT StudentID
FROM table1 t1
JOIN table2 t2 ON t2.SubjectID = t1.SubjectID
WHERE t1.Present = 'yes' AND t1.Type1 = 1
GROUP BY t1.StudentID, t2.SubjectID
HAVING COUNT(*) / t2.Number2 >= 0.75
) finisher
JOIN
(
SELECT DISTINCT t1.StudentID
FROM table1 t1
LEFT JOIN
(
SELECT DISTINCT StudentID
FROM table1
WHERE Type = 3 AND Present = 'no'
) missed ON missed.StudentID = t1.StudentID
WHERE t1.Type = 3
AND missed.StudentID IS NULL
) notmissed ON finisher.StudentID = notmissed.StudentID
"StudentID`s from table1 that didnt miss any Type 3"... I assume here you don't want to include students without any type 3 rows.
Seems like this is done and duste, but how about...
SELECT x.*
FROM
( SELECT t1.StudentID
, t1.SubjectID
, t1.Type
, t2.Number1 num
FROM table1 t1
JOIN table2 t2
ON t2.SubjectID=t1.SubjectID
WHERE t1.Present='yes'
GROUP
BY t1.StudentID
, t1.SubjectID
HAVING COUNT(*)/num >= 0.75
) x
LEFT
JOIN table1 y
ON y.student_id = x.student_id
AND y.subject_id = x.subject_id
AND y.type = 3
AND y.present = 'no'
WHERE y.student_id IS NULL;