How do you use the “WITH” clause in MySQL?

前端 未结 7 1292
耶瑟儿~
耶瑟儿~ 2020-11-22 04:02

I am converting all my SQL Server queries to MySQL and my queries that have WITH in them are all failing. Here\'s an example:

WITH t1 AS
(
              


        
7条回答
  •  眼角桃花
    2020-11-22 04:44

    I followed the link shared by lisachenko and found another link to this blog: http://guilhembichot.blogspot.co.uk/2013/11/with-recursive-and-mysql.html

    The post lays out ways of emulating the 2 uses of SQL WITH. Really good explanation on how these work to do a similar query as SQL WITH.

    1) Use WITH so you don't have to perform the same sub query multiple times

    CREATE VIEW D AS (SELECT YEAR, SUM(SALES) AS S FROM T1 GROUP BY YEAR);
    SELECT D1.YEAR, (CASE WHEN D1.S>D2.S THEN 'INCREASE' ELSE 'DECREASE' END) AS TREND
    FROM
     D AS D1,
     D AS D2
    WHERE D1.YEAR = D2.YEAR-1;
    DROP VIEW D;
    

    2) Recursive queries can be done with a stored procedure that makes the call similar to a recursive with query.

    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 * FROM EMPLOYEES_EXTENDED",
    0,
    ""
    );
    

    And this is the code or the stored procedure

    # Usage: the standard syntax:
    #   WITH RECURSIVE recursive_table AS
    #    (initial_SELECT
    #     UNION ALL
    #     recursive_SELECT)
    #   final_SELECT;
    # should be translated by you to 
    # CALL WITH_EMULATOR(recursive_table, initial_SELECT, recursive_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,
    #   run recursive_SELECT based on T0 and put result into table T1,
    #   if T1 is empty
    #      then leave loop,
    #      else swap T0 and T1 (renaming) and empty T1
    # 4) Drop T0, T1
    # 5) Rename U to T0
    # 6) run final select, send relult to client
    
    # This is for *one* recursive table.
    # It would be possible to write a SP creating multiple recursive tables.
    
    delimiter |
    
    CREATE PROCEDURE WITH_EMULATOR(
    recursive_table varchar(100), # name of recursive table
    initial_SELECT varchar(65530), # seed a.k.a. anchor
    recursive_SELECT varchar(65530), # recursive member
    final_SELECT varchar(65530), # final SELECT on UNION result
    max_recursion int unsigned, # safety against infinite loop, use 0 for default
    create_table_options varchar(65530) # you can add CREATE-TABLE-time options
    # to your recursive_table, to speed up initial/recursive/final SELECTs; example:
    # "(KEY(some_column)) ENGINE=MEMORY"
    )
    
    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");
      set recursive_table_union = concat(recursive_table, "_union");
      set recursive_table_tmp   = concat(recursive_table, "_tmp"); 
      # Cleanup any previous failed runs
      SET @str =
        CONCAT("DROP TEMPORARY TABLE IF EXISTS ", recursive_table, ",",
        recursive_table_next, ",", recursive_table_union,
        ",", recursive_table_tmp);
      PREPARE stmt FROM @str;
      EXECUTE stmt; 
     # If you need to reference recursive_table more than
      # once in recursive_SELECT, remove the TEMPORARY word.
      SET @str = # create and fill T0
        CONCAT("CREATE TEMPORARY TABLE ", recursive_table, " ",
        create_table_options, " AS ", initial_SELECT);
      PREPARE stmt FROM @str;
      EXECUTE stmt;
      SET @str = # create U
        CONCAT("CREATE TEMPORARY TABLE ", recursive_table_union, " LIKE ", recursive_table);
      PREPARE stmt FROM @str;
      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;
        if not new_rows then
          leave recursion;
        end if;
        # Prepare next iteration:
        # T1 becomes T0, to be the source of next run of recursive_SELECT,
        # T0 is recycled to be T1.
        SET @str =
          CONCAT("ALTER TABLE ", recursive_table, " RENAME ", recursive_table_tmp);
        PREPARE stmt FROM @str;
        EXECUTE stmt;
        # we use ALTER TABLE RENAME because RENAME TABLE does not support temp tables
        SET @str =
          CONCAT("ALTER TABLE ", recursive_table_next, " RENAME ", recursive_table);
        PREPARE stmt FROM @str;
        EXECUTE stmt;
        SET @str =
          CONCAT("ALTER TABLE ", recursive_table_tmp, " RENAME ", recursive_table_next);
        PREPARE stmt FROM @str;
        EXECUTE stmt;
        # empty T1
        SET @str =
          CONCAT("TRUNCATE TABLE ", recursive_table_next);
        PREPARE stmt FROM @str;
        EXECUTE stmt;
      until 0 end repeat;
      # eliminate T0 and T1
      SET @str =
        CONCAT("DROP TEMPORARY TABLE ", recursive_table_next, ", ", recursive_table);
      PREPARE stmt FROM @str;
      EXECUTE stmt;
      # Final (output) SELECT uses recursive_table name
      SET @str =
        CONCAT("ALTER TABLE ", recursive_table_union, " RENAME ", recursive_table);
      PREPARE stmt FROM @str;
      EXECUTE stmt;
      # Run final SELECT on UNION
      SET @str = final_SELECT;
      PREPARE stmt FROM @str;
      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 ;
    

提交回复
热议问题