Professional Documents
Culture Documents
Ms
http://guilhembichot.blogspot.com.es/2013/11/with-recursive-and-mysql.html
Siguiente blog
Crear un blog
Acceder
Este sitio emplea cookies de Google para prestar sus servicios, para personalizar anuncios y para analizar el trfico. Google recibe informacin
sobre tu uso de este sitio web. Si utilizas este sitio web, se sobreentiende que aceptas el uso de cookies. Ms informacin
Entendido
Membres
avec Google Friend Connect
Membres (9)
Archives du blog
2015 (1)
2014 (2)
2013 (2)
novembre (1)
WITH RECURSIVE and MySQL
fvrier (1)
1 de 13
31/07/2015 4:01
http://guilhembichot.blogspot.com.es/2013/11/with-recursive-and-mysql.html
Este sitio emplea cookies de Google para prestar sus servicios, para personalizar anuncios y para analizar el trfico. Google recibe informacin
sobre tu uso de este sitio web. Si utilizas este sitio web, se sobreentiende que aceptas el uso de cookies. Ms informacin
Entendido
Both derived tables are based on the same subquery text, but usually a DBMS is not
smart enough to recognize it. Thus, it will evaluate "SELECT YEAR, SUM(SALES)...
GROUP BY YEAR" twice! A first time to fill D1, a second time to fill D2. This limitation is
sometimes stated as "it's not possible to refer to a derived table twice in the same
query".
Such double evaluation can lead to a serious performance problem. Using WITH, this
limitation does not exist, and the following statement evaluates the subquery only once:
Qui tes-vous ?
Guilhem Bichot
Hi. I'm a developer of the
MySQL open-source
database server, at Oracle
Corporation. More about me:
http://www.linkedin.com/profile
/view?id=14387841 .
Afficher mon profil complet
FROM
D AS D1,
D AS D2
WHERE D1.YEAR = D2.YEAR-1;
DROP VIEW D;
Instead of a view, I could as well create D as a normal table. But not as a temporary table,
because in MySQL a temporary table cannot be referred twice in the same query, as
mentioned in the manual.
2 de 13
31/07/2015 4:01
http://guilhembichot.blogspot.com.es/2013/11/with-recursive-and-mysql.html
Este sitio emplea cookies de Google para prestar sus servicios, para personalizar anuncios y para analizar el trfico. Google recibe informacin
sobre tu uso de este sitio web. Si utilizas este sitio web, se sobreentiende que aceptas el uso de cookies. Ms informacin
Entendido
According to the SQL standard, to use the recursive form, you should write WITH
RECURSIVE. However, looking at some other DBMSs, they seem to not require the
RECURSIVE word.
WITH RECURSIVE is a powerful construct. For example, it can do the same job as
Oracle's CONNECT BY clause (you can check out some example conversions between
both constructs).
Let's walk through an example, to understand what WITH RECURSIVE does.
Assume you have a table of employees (this is a very classical example of WITH
RECURSIVE):
In other words, Yasmina is CEO, John and Tarek report to her. Pedro reports to John,
Sarah and Pierre report to Pedro.
In a big company, they would be thousands of rows in this table.
Now, let's say that you would like to know, for each employee: "how many people are,
directly and indirectly, reporting to him/her"? Here is how I would do it. First, I would
3 de 13
31/07/2015 4:01
http://guilhembichot.blogspot.com.es/2013/11/with-recursive-and-mysql.html
Este sitio emplea cookies de Google para prestar sus servicios, para personalizar anuncios y para analizar el trfico. Google recibe informacin
sobre tu uso de este sitio web. Si utilizas este sitio web, se sobreentiende que aceptas el uso de cookies. Ms informacin
Entendido
Then I would insert the results into a new table named EMPLOYEES_EXTENDED;
EXTENDED stands for "extended with more information", the new information being
the fourth column named REPORTS: it is a count of people who are reporting directly or
indirectly to the employee. Because we have listed people who are not managers, they
have a value of 0 in the REPORTS column.
Then, we can produce the rows for "first level" managers (the direct managers of
non-managers):
Explanation: for a row of M (that is, for an employee), the JOIN will produce zero or
more rows, one per non-manager directly reporting to the employee.
Each such non-manager contributes to the value of REPORTS for his manager, through
two numbers: 1 (the non-manager himself), and the number of direct/indirect reports of
the non-manager (i.e. the value of REPORTS for the non-manager).
Then I would empty EMPLOYEES_EXTENDED, and fill it with the rows produced just
above, which describe the first level managers.
Then the same query should be run again, and it would produce information about the
"second level" managers. And so on.
Finally, at one point Yasmina will be the only row of EMPLOYEES_EXTENDED, and
when we run the above SELECT again, the JOIN will produce no rows, because
E.MANAGER_ID will be NULL (she's the CEO). We are done.
It's time for a recap: EMPLOYEES_EXTENDED has been a kind of "temporary buffer",
which has successively held non-managers, first level managers, second level managers,
4 de 13
31/07/2015 4:01
http://guilhembichot.blogspot.com.es/2013/11/with-recursive-and-mysql.html
Este sitio emplea cookies de Google para prestar sus servicios, para personalizar anuncios y para analizar el trfico. Google recibe informacin
sobre tu uso de este sitio web. Si utilizas este sitio web, se sobreentiende que aceptas el uso de cookies. Ms informacin
Entendido
member" or "the seed". The SELECT query which moves from one step of recursion to
the next one, is the "recursive member". The complete statement looks like this:
WITH RECURSIVE
# The temporary buffer, also used as UNION result:
EMPLOYEES_EXTENDED
AS
(
# The seed:
SELECT ID, NAME, MANAGER_ID, 0 AS REPORTS
FROM EMPLOYEES
WHERE ID NOT IN (SELECT MANAGER_ID FROM EMPLOYEES WHERE MANAGER_ID IS NOT NULL)
UNION ALL
# The recursive member:
SELECT M.ID, M.NAME, M.MANAGER_ID, SUM(1+E.REPORTS) AS REPORTS
FROM EMPLOYEES M JOIN EMPLOYEES_EXTENDED E ON M.ID=E.MANAGER_ID
GROUP BY M.ID, M.NAME, M.MANAGER_ID
)
# what we want to do with the complete result (the UNION):
SELECT * FROM EMPLOYEES_EXTENDED;
MySQL does not yet support WITH RECURSIVE, but it is possible to code a generic
stored procedure which can easily emulate it. Here is how you would call it:
CALL WITH_EMULATOR(
"EMPLOYEES_EXTENDED",
"
SELECT ID, NAME, MANAGER_ID, 0 AS REPORTS
FROM EMPLOYEES
WHERE ID NOT IN (SELECT MANAGER_ID FROM EMPLOYEES WHERE MANAGER_ID IS NOT NULL)
",
5 de 13
31/07/2015 4:01
http://guilhembichot.blogspot.com.es/2013/11/with-recursive-and-mysql.html
Este sitio emplea cookies de Google para prestar sus servicios, para personalizar anuncios y para analizar el trfico. Google recibe informacin
sobre tu uso de este sitio web. Si utilizas este sitio web, se sobreentiende que aceptas el uso de cookies. Ms informacin
Entendido
You can recognize, as arguments of the stored procedure, every member of the WITH
standard syntax: name of the temporary buffer, query for the seed, query for the
recursive member, and what to do with the complete result. The last two arguments - 0
and the empty string - are details which you can ignore for now.
Here is the result returned by this stored procedure:
+------+---------+------------+---------+
| ID
| NAME
| MANAGER_ID | REPORTS |
+------+---------+------------+---------+
|
29 |
0 |
692 | Tarek
333 |
0 |
| 4610 | Sarah
29 |
0 |
72 | Pierre
198 |
2 |
333 | Yasmina |
29 | Pedro
NULL |
1 |
198 | John
333 |
3 |
333 | Yasmina |
NULL |
4 |
+------+---------+------------+---------+
7 rows in set
Notice how Pierre, Tarek and Sarah have zero reports, Pedro has two, which looks
correct... However, Yasmina appears in two rows! Odd? Yes and no. Our algorithm starts
from non-managers, the "leaves" of the tree (Yasmina being the root of the tree). Then
our algorithm looks at first level managers, the direct parents of leaves. Then at second
level managers. But Yasmina is both a first level manager (of the nonmanager Tarek) and
6 de 13
31/07/2015 4:01
http://guilhembichot.blogspot.com.es/2013/11/with-recursive-and-mysql.html
Este sitio emplea cookies de Google para prestar sus servicios, para personalizar anuncios y para analizar el trfico. Google recibe informacin
sobre tu uso de este sitio web. Si utilizas este sitio web, se sobreentiende que aceptas el uso de cookies. Ms informacin
Entendido
contributes 1 direct/indirect report. The second tree branch contributes 4. The right
number, which we want, is the sum of the two: 5. Thus we just need to change the final
query, in the CALL:
CALL WITH_EMULATOR(
"EMPLOYEES_EXTENDED",
"
SELECT ID, NAME, MANAGER_ID, 0 AS REPORTS
FROM EMPLOYEES
WHERE ID NOT IN (SELECT MANAGER_ID FROM EMPLOYEES WHERE MANAGER_ID IS NOT NULL)
",
"
SELECT M.ID, M.NAME, M.MANAGER_ID, SUM(1+E.REPORTS) AS REPORTS
FROM EMPLOYEES M JOIN EMPLOYEES_EXTENDED E ON M.ID=E.MANAGER_ID
GROUP BY M.ID, M.NAME, M.MANAGER_ID
",
"
SELECT ID, NAME, MANAGER_ID, SUM(REPORTS)
FROM EMPLOYEES_EXTENDED
GROUP BY ID, NAME, MANAGER_ID
",
0,
""
);
+------+---------+------------+--------------+
| ID
| NAME
| MANAGER_ID | SUM(REPORTS) |
+------+---------+------------+--------------+
|
7 de 13
29 | Pedro
198 |
2 |
31/07/2015 4:01
http://guilhembichot.blogspot.com.es/2013/11/with-recursive-and-mysql.html
Este sitio emplea cookies de Google para prestar sus servicios, para personalizar anuncios y para analizar el trfico. Google recibe informacin
sobre tu uso de este sitio web. Si utilizas este sitio web, se sobreentiende que aceptas el uso de cookies. Ms informacin
|
692 | Tarek
333 |
0 |
| 4610 | Sarah
29 |
0 |
Entendido
+------+---------+------------+--------------+
6 rows in set
Let's finish by showing the body of the stored procedure. You will notice that it does
heavy use of dynamic SQL, thanks to prepared statements. Its body does not depend on
the particular problem to solve, it's reusable as-is for other WITH RECURSIVE use
cases. I have added comments inside the body, so it should be self-explanatory. If it's
not, feel free to drop a comment on this post, and I will explain further.
UNION ALL
recursive_SELECT)
final_SELECT;
final_SELECT, 0, "").
# ALGORITHM:
# 1) we have an initial table T0 (actual name is an argument
# "recursive_table"), we fill it with result of initial_SELECT.
# 2) We have a union table U, initially empty.
# 3) Loop:
#
add rows of T0 to U,
if T1 is empty
# 4) Drop T0, T1
8 de 13
31/07/2015 4:01
http://guilhembichot.blogspot.com.es/2013/11/with-recursive-and-mysql.html
Este sitio emplea cookies de Google para prestar sus servicios, para personalizar anuncios y para analizar el trfico. Google recibe informacin
sobre tu uso de este sitio web. Si utilizas este sitio web, se sobreentiende que aceptas el uso de cookies. Ms informacin
Entendido
delimiter |
BEGIN
declare new_rows int unsigned;
declare show_progress int default 0; # set to 1 to trace/debug execution
declare recursive_table_next varchar(120);
declare recursive_table_union varchar(120);
declare recursive_table_tmp varchar(120);
set recursive_table_next
= concat(recursive_table, "_next");
= concat(recursive_table, "_tmp");
9 de 13
31/07/2015 4:01
http://guilhembichot.blogspot.com.es/2013/11/with-recursive-and-mysql.html
Este sitio emplea cookies de Google para prestar sus servicios, para personalizar anuncios y para analizar el trfico. Google recibe informacin
sobre tu uso de este sitio web. Si utilizas este sitio web, se sobreentiende que aceptas el uso de cookies. Ms informacin
Entendido
EXECUTE stmt;
SET @str = # create T1
CONCAT("CREATE TEMPORARY TABLE ", recursive_table_next, " LIKE ", recursive_table);
PREPARE stmt FROM @str;
EXECUTE stmt;
if max_recursion = 0 then
set max_recursion = 100; # a default to protect the innocent
end if;
recursion: repeat
# add T0 to U (this is always UNION ALL)
SET @str =
CONCAT("INSERT INTO ", recursive_table_union, " SELECT * FROM ", recursive_table);
PREPARE stmt FROM @str;
EXECUTE stmt;
# we are done if max depth reached
set max_recursion = max_recursion - 1;
if not max_recursion then
if show_progress then
select concat("max recursion exceeded");
end if;
leave recursion;
end if;
# fill T1 by applying the recursive SELECT on T0
SET @str =
CONCAT("INSERT INTO ", recursive_table_next, " ", recursive_SELECT);
PREPARE stmt FROM @str;
EXECUTE stmt;
# we are done if no rows in T1
select row_count() into new_rows;
if show_progress then
select concat(new_rows, " new rows found");
end if;
10 de 13
31/07/2015 4:01
http://guilhembichot.blogspot.com.es/2013/11/with-recursive-and-mysql.html
Este sitio emplea cookies de Google para prestar sus servicios, para personalizar anuncios y para analizar el trfico. Google recibe informacin
sobre tu uso de este sitio web. Si utilizas este sitio web, se sobreentiende que aceptas el uso de cookies. Ms informacin
Entendido
11 de 13
31/07/2015 4:01
http://guilhembichot.blogspot.com.es/2013/11/with-recursive-and-mysql.html
Este sitio emplea cookies de Google para prestar sus servicios, para personalizar anuncios y para analizar el trfico. Google recibe informacin
sobre tu uso de este sitio web. Si utilizas este sitio web, se sobreentiende que aceptas el uso de cookies. Ms informacin
Entendido
EXECUTE stmt;
# No temporary tables may survive:
SET @str =
CONCAT("DROP TEMPORARY TABLE ", recursive_table);
PREPARE stmt FROM @str;
EXECUTE stmt;
# We are done :-)
END|
delimiter ;
In the SQL Standard, WITH RECURSIVE allows some nice additional tweaks
(depth-first or breadth-first ordering, cycle detection). In future posts I will show how to
emulate them too.
Publi par Guilhem Bichot 06:17
2 commentaires:
swanhart 20 novembre 2013 03:20
Very cool.
Rpondre
12 de 13
31/07/2015 4:01
http://guilhembichot.blogspot.com.es/2013/11/with-recursive-and-mysql.html
Este sitio emplea cookies de Google para prestar sus servicios, para personalizar anuncios y para analizar el trfico. Google recibe informacin
sobre tu uso de este sitio web. Si utilizas este sitio web, se sobreentiende que aceptas el uso de cookies. Ms informacin
Entendido
Commentaire :
Publier
Accueil
The views expressed on this blog are my own and do not necessarily reflect the views of Oracle. Modle Watermark. Fourni par Blogger.
13 de 13
31/07/2015 4:01