は、次の2つのクエリの違いを考えてみましょう:
SELECT @id := id as id, parent, (
SELECT concat(id, ': ', @id)
) as path
FROM t_hierarchy;
SELECT @id := id as id, parent, (
SELECT concat(id, ': ', _id)
FROM (SELECT @id as _id) as x
) as path
FROM t_hierarchy;
彼らは、ほぼ同じに見えるが、劇的に異なる結果をもたらす。 MySQLの私のバージョンでは、2番目のクエリの_id
は結果セットの各行で同じで、最後の行のid
と同じです。しかし、最後のビットは、私が指定された順序で2つのクエリを実行したためにのみ真です。たとえば、SET @id := 1
の後には、_id
は、SET
ステートメントの値と常に等しいことがわかります。
ここで何が起こっているのですか? EXPLAIN
が手掛かりが得られる:第三の行は、使用されないテーブルとDERIVED
テーブルが、それはいつでも、正確に一度だけ計算することができることをMySQLへ示すこと
mysql> explain SELECT @id := id as id, parent, (
-> SELECT concat(id, ': ', _id)
-> FROM (SELECT @id as _id) as x
-> ) as path
-> FROM t_hierarchy;
+----+--------------------+-------------+--------+---------------+------------------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------------+--------+---------------+------------------+---------+------+------+----------------+
| 1 | PRIMARY | t_hierarchy | index | NULL | hierarchy_parent | 9 | NULL | 1398 | Using index |
| 2 | DEPENDENT SUBQUERY | <derived3> | system | NULL | NULL | NULL | NULL | 1 | |
| 3 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+--------------------+-------------+--------+---------------+------------------+---------+------+------+----------------+
3 rows in set (0.00 sec)
。サーバーは、派生テーブルがクエリ内の別の場所で定義された変数を使用していることに気付かず、行ごとに1回実行するというヒントを持っていません。あなたはuser-defined variablesにMySQLのドキュメントに記載された行動にかまされている:
私の場合は
As a general rule, you should never assign a value to a user variable and read the value within the same statement. You might get the results you expect, but this is not guaranteed. The order of evaluation for expressions involving user variables is undefined and may change based on the elements contained within a given statement; in addition, this order is not guaranteed to be the same between releases of the MySQL Server.
、@id
が(再)外SELECT
によって定義される前に、それは、最初にそのテーブルを計算行うことを選択しました。実際、元の階層データクエリが機能するのはそのことです。 @r
の定義は、クエリー内の他のものよりも前にMySQLによって計算されます。その種の派生テーブルだからです。しかし、ここではクエリ全体に対して一度だけでなく、テーブル行ごとに一度@r
をリセットする必要があります。これを行うには、元のようなクエリが必要で、手で@r
をリセットします。
SELECT @r := if(
@c = th1.id,
if(
@r is null,
null,
(
SELECT parent
FROM t_hierarchy
WHERE id = @r
)
),
th1.id
) AS parent,
@l := if(@c = th1.id, @l + 1, 0) AS lvl,
@c := th1.id as _id
FROM (
SELECT @c := 0,
@r := 0,
@l := 0
) vars
left join t_hierarchy as th1 on 1
left join t_hierarchy as th2 on 1
HAVING parent is not null
このクエリでは、ループオーバーに親サブクエリのための結果に十分な行があることを確認するために、元のクエリがない第2 t_hierarchy
同じ方法を使用しています。また、それ自身を親として含む各_idの行を追加します。それがなければ、すべてのルートオブジェクト(親フィールドにNULL
)は結果にまったく表示されません。
奇妙なことに、GROUP_CONCAT
で結果を実行すると、順序が乱れるようです。
SELECT _id,
GROUP_CONCAT(parent ORDER BY lvl desc SEPARATOR ' > ') as path,
max(lvl) as depth
FROM (
SELECT @r := if(
@c = th1.id,
if(
@r is null,
null,
(
SELECT parent
FROM t_hierarchy
WHERE id = @r
)
),
th1.id
) AS parent,
@l := if(@c = th1.id, @l + 1, 0) AS lvl,
@c := th1.id as _id
FROM (
SELECT @c := 0,
@r := 0,
@l := 0
) vars
left join t_hierarchy as th1 on 1
left join t_hierarchy as th2 on 1
HAVING parent is not null
ORDER BY th1.id
) as x
GROUP BY _id;
フェア警告:これらのクエリは、暗黙のうちに@c
更新する前に起こっ@r
と@l
アップデートに頼る幸いなことに、その関数は、独自のORDER BY
句を持っています。この注文はMySQLによって保証されておらず、どのバージョンのサーバーでも変更される可能性があります。
エラーが表示される: '#1064 - SQL構文に誤りがあります。 '@id int'の近くで使用する正しい構文については、MySQLサーバのバージョンに対応するマニュアルを参照してください。)varchar(400)をbeginとして宣言してください@path varchar(400)@termを '1行目'で宣言し、 ?! –
答えはSQLServer構文です。私はそれをMySQLの構文に変換するのを助けることができます。階層的なデータを持っているので、列を追加したくない、つまり深さ/パスを更新/挿入/削除で更新する必要があります。私はそれがあるのがわかりません。ループなし。 MySQLがCTEを持っていれば、ユーザ定義の関数なしでループなしで同じ問題を解決することができます。 –