Как вы используете предложение «WITH» в MySQL?

Я конвертирую все свои запросы SQL Server в MySQL, и мои запросы, в которых есть WITH в них не работают. Вот пример:

 WITH t1 AS ( SELECT article.*, userinfo.*, category.* FROM question INNER JOIN userinfo ON userinfo.user_userid = article.article_ownerid INNER JOIN category ON article.article_categoryid = category.catid WHERE article.article_isdeleted = 0 ) SELECT t1.* FROM t1 ORDER BY t1.article_date DESC LIMIT 1, 3 

MySQL не поддерживает предложение WITH (CTE на языке SQL Server, подзапрос факторинга в Oracle), поэтому вам остается использовать:

  • ВРЕМЕННЫЕ таблицы
  • ТАБЛИЦА
  • inline views (эффективно то, что представляет предложение WITH – они взаимозаменяемы)

Запрос на эту функцию относится к 2006 году.

Как уже упоминалось, вы предоставили плохой пример: нет необходимости выполнять подзаголовок, если вы не изменяете вывод столбцов каким-либо образом:

  SELECT * FROM ARTICLE t JOIN USERINFO ui ON ui.user_userid = t.article_ownerid JOIN CATEGORY c ON c.catid = t.article_categoryid WHERE t.published_ind = 0 ORDER BY t.article_date DESC LIMIT 1, 3 

Вот лучший пример:

 SELECT t.name, t.num FROM TABLE t JOIN (SELECT c.id COUNT(*) 'num' FROM TABLE c WHERE c.column = 'a' GROUP BY c.id) ta ON ta.id = t.id 

Команда разработчиков Mysql объявила, что версия 8.0 будет иметь общие выражения таблиц в MySQL (CTE) . Таким образом, можно будет писать запросы следующим образом:

 WITH RECURSIVE my_cte AS ( SELECT 1 AS n UNION ALL SELECT 1+n FROM my_cte WHERE n<10 ) SELECT * FROM my_cte; +------+ | n | +------+ | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | | 7 | | 8 | | 9 | | 10 | +------+ 10 rows in set (0,00 sec) 

В Sql оператор with задает временный именованный набор результатов, известный как общее табличное выражение (CTE). Он может использоваться для рекурсивных запросов, но в этом случае он определяет как подмножество. Если mysql разрешает подзапросы, я бы попытался

 select t1.* from ( SELECT article.*, userinfo.*, category.* FROM question INNER JOIN userinfo ON userinfo.user_userid=article.article_ownerid INNER JOIN category ON article.article_categoryid=category.catid WHERE article.article_isdeleted = 0 ) t1 ORDER BY t1.article_date DESC Limit 1, 3 

Функция «Common Table Expression» недоступна в MySQL, поэтому вам нужно пойти, чтобы сделать просмотр или временную таблицу для решения, здесь я использовал временную таблицу.

Хранимая процедура, упомянутая здесь, решит вашу потребность. Если я хочу получить всех членов моей команды и связанных с ними членов, эта хранимая процедура поможет:

 ---------------------------------- user_id | team_id ---------------------------------- admin | NULL ramu | admin suresh | admin kumar | ramu mahesh | ramu randiv | suresh ----------------------------------- 

Код:

 DROP PROCEDURE `user_hier`// CREATE DEFINER=`root`@`localhost` PROCEDURE `user_hier`(in team_id varchar(50)) BEGIN declare count int; declare tmp_team_id varchar(50); CREATE TEMPORARY TABLE res_hier(user_id varchar(50),team_id varchar(50))engine=memory; CREATE TEMPORARY TABLE tmp_hier(user_id varchar(50),team_id varchar(50))engine=memory; set tmp_team_id = team_id; SELECT COUNT(*) INTO count FROM user_table WHERE user_table.team_id=tmp_team_id; WHILE count>0 DO insert into res_hier select user_table.user_id,user_table.team_id from user_table where user_table.team_id=tmp_team_id; insert into tmp_hier select user_table.user_id,user_table.team_id from user_table where user_table.team_id=tmp_team_id; select user_id into tmp_team_id from tmp_hier limit 0,1; select count(*) into count from tmp_hier; delete from tmp_hier where user_id=tmp_team_id; end while; select * from res_hier; drop temporary table if exists res_hier; drop temporary table if exists tmp_hier; end 

Это можно назвать, используя:

 mysql>call user_hier ('admin')// 

Я пошел по ссылке, разделенной лиссаченко, и нашел еще одну ссылку на этот блог: http://guilhembichot.blogspot.co.uk/2013/11/with-recursive-and-mysql.html

В статье излагаются способы эмуляции двух видов использования SQL WITH. Действительно хорошее объяснение того, как эти работы выполняют аналогичный запрос, как SQL WITH.

1) Используйте WITH, чтобы вам не приходилось выполнять один и тот же подзапрос несколько раз

 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) Рекурсивные запросы могут выполняться с помощью хранимой процедуры, которая делает вызов похожим на рекурсивный с запросом.

 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, "" ); 

И это код или хранимая процедура

 # 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 aka 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 ; 

Эта функция называется общим табличным выражением http://msdn.microsoft.com/en-us/library/ms190766.aspx

Вы не сможете выполнить точную вещь в mySQL, самая легкая вещь, вероятно, сделает вид, который отражает этот CTE, и просто выберите из представления. Вы можете сделать это с помощью подзапросов, но это будет работать очень плохо. Если вы столкнетесь с любыми CTE, которые рекурсивно, я не знаю, как вы сможете воссоздать это без использования хранимых процедур.

EDIT: Как я уже сказал в своем комментарии, этот пример, который вы опубликовали, не нуждается в CTE, поэтому вы должны упростить его для вопроса, так как он может быть просто написан как

 SELECT article.*, userinfo.*, category.* FROM question INNER JOIN userinfo ON userinfo.user_userid=article.article_ownerid INNER JOIN category ON article.article_categoryid=category.catid WHERE article.article_isdeleted = 0 ORDER BY article_date DESC Limit 1, 3 
Interesting Posts
Давайте будем гением компьютера.