MySQL で「WITH」句はどのように使用しますか?
-
21-09-2019 - |
質問
すべての 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
解決
バージョン 8.0 より前の MySQL WITH 句はサポートされていません (SQL Server 用語での CTE。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 の共通テーブル式 (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文と共通テーブル式(CTE)として知られている一時的な名前の結果セットを、指定します。これは、再帰クエリのために使用することができるが、この場合には、それはサブセットとして指定します。 mysqlはsubselectesを可能にした場合、私がしようとするだろう。
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
私はlisachenkoで共有リンクに続き、このブログへの別のリンクを見つけました: http://guilhembichot.blogspot.co.uk/2013 /11/with-recursive-and-mysql.htmlする
ポストは、SQL WITHの2回の使用をエミュレートする方法をレイアウト。これらの作業は、SQL WITHと同様のクエリを実行する方法については本当に良い説明ます。
あなたは同じサブクエリを複数回実行する必要はありませんので、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 ;
「共通テーブル式」機能を使用すると、解決するためのビューまたは一時テーブルを作るために行かなければならないので、ここで私は、一時テーブルを使用していた、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://msdn.microsoft.com/en-us/library/ms190766。 ASPXする
あなたは最も簡単な方法は、おそらくミラーがCTEは、ちょうどビューから選択することという見解を作るだろう、MySQLで正確なことを行うことができなくなります。あなたはサブクエリでそれを行うことができますが、それは本当に悪い実行されます。あなたは再帰を行う任意の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
私はこのスレッドするからブラッドの答え@言っていますが、保存する方法を望んでいましたさらなる処理のための結果(のMySql 8):
-- May need to adjust the recursion depth first
SET @@cte_max_recursion_depth = 10000 ; -- permit deeper recursion
-- Some boundaries
set @startDate = '2015-01-01'
, @endDate = '2020-12-31' ;
-- Save it to a table for later use
drop table if exists tmpDates ;
create temporary table tmpDates as -- this has to go _before_ the "with", Duh-oh!
WITH RECURSIVE t as (
select @startDate as dt
UNION
SELECT DATE_ADD(t.dt, INTERVAL 1 DAY) FROM t WHERE DATE_ADD(t.dt, INTERVAL 1 DAY) <= @endDate
)
select * FROM t -- need this to get the "with"'s results as a "result set", into the "create"
;
-- Exists?
select * from tmpDates ;
生成
dt |
----------|
2015-01-01|
2015-01-02|
2015-01-03|
2015-01-04|
2015-01-05|
2015-01-06|