Как вы используете предложение "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
6 ответов:
MySQL не поддерживает предложение WITH (CTE на языке SQL Server; Факторинг подзапросов в Oracle), поэтому вы остаетесь с использованием:
- временные таблицы
- производные таблицы
- встроенные представления (фактически, что представляет собой предложение 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 (CTEs). Таким образом, можно будет писать такие запросы:
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
сообщение излагает способы эмуляции 2 использования SQL С. Действительно хорошее объяснение о том, как они работают, чтобы сделать аналогичный запрос, как SQL С.
1) Используйте с таким образом, вам не нужно выполнять один и тот же подзапрос несколько раз
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 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 ;
эта функция называется обобщенным табличным выражением http://msdn.microsoft.com/en-us/library/ms190766.aspx
вы не сможете сделать точную вещь в mySQL, проще всего, вероятно, сделать представление, которое отражает этот CTE и просто выбирает из представления. Вы можете сделать это с помощью подзапросов, но это будет работать очень плохо. Если вы столкнетесь с любыми CTE, которые делают рекурсию, я не знаю, как вы сможете воссоздать это без использования сохраненных процедуры.
изменить: Как я уже сказал в своем комментарии, этот пример, который вы опубликовали, не нуждается в 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