MySQL 子查詢(三) 派生表、子查詢錯誤

  From MySQL 5.7 ref:13.2.10.8 Derived Tableshtml

 

8、派生表mysql

  派生表是一個表達式,用於在一個查詢的FROM子句的範圍內生成表。sql

  例如,在一個SELECT查詢的FROM子句中的子查詢,就是一個派生表。數據庫

SELECT ... FROM (subquery) [AS] tbl_name ...

  [AS] tbl_name子句是必需的,由於FROM子句中的每一個表都必須具備名稱。且派生表中的任何列都必須具備惟一名稱。函數

  爲了便於說明,假設如今有這樣一個表:優化

CREATE TABLE t1 (s1 INT, s2 CHAR(5), s3 FLOAT);

  使用示例表,在FROM子句中使用子查詢:spa

INSERT INTO t1 VALUES (1,'1',1.0);
INSERT INTO t1 VALUES (2,'2',2.0);

SELECT sb1,sb2,sb3
  FROM (SELECT s1 AS sb1, s2 AS sb2, s3*2 AS sb3 FROM t1) AS sb
  WHERE sb1 > 1;

+------+------+------+
| sb1  | sb2  | sb3  |
+------+------+------+
|    2 | 2    |    4 |
+------+------+------+

  下面是另外一個例子:假設您想知道分組表的一組總和的平均值。但下面的沒法運行:code

SELECT AVG(SUM(column1)) FROM t1 GROUP BY column1;

  而下面這個能夠取回你想要的信息:htm

SELECT AVG(sum_column1)
  FROM (SELECT SUM(column1) AS sum_column1
        FROM t1 GROUP BY column1) AS t1;

  請注意,子查詢中使用的列名稱(sum_column1)能夠在外部查詢中被識別。blog

 

  派生表能夠返回一個標量、一列數據,一行數據或者一個表。

  派生表受這些限制的約束:

  • 派生表不能是相關子查詢;
  • 派生表不能包含對同一SELECT中的其餘表的引用;
  • 派生表不能包含外部引用。這是MySQL的限制,而不是SQL標準的限制。

  See Section 8.2.2.4, 「Optimizing Derived Tables and View References with Merging or Materialization」.

 

  在某些狀況下,使用EXPLAIN SELECT可能會修改表數據。當外部查詢訪問任意表而且內部查詢調用了一個更改表的一行或者多行數據的存儲函數。

  假設數據庫d1中有兩個表t1和t2,以及一個修改t2的存儲函數f1,以下所示:

CREATE DATABASE d1;
USE d1;
CREATE TABLE t1 (c1 INT);
CREATE TABLE t2 (c1 INT);
CREATE FUNCTION f1(p1 INT) RETURNS INT
  BEGIN
    INSERT INTO t2 VALUES (p1);
    RETURN p1;
  END;

  直接在EXPLAIN SELECT中引用該函數對t2沒有影響,以下所示:

mysql> SELECT * FROM t2;
Empty set (0.02 sec)

mysql> EXPLAIN SELECT f1(5)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: NULL
   partitions: NULL
         type: NULL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
     filtered: NULL
        Extra: No tables used
1 row in set (0.01 sec)

mysql> SELECT * FROM t2;
Empty set (0.01 sec)

  這是由於SELECT語句沒有引用任何表,如輸出中的table和Extra列所示那樣。如下嵌套SELECT也是如此:

mysql> EXPLAIN SELECT NOW() AS a1, (SELECT f1(5)) AS a2\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: NULL
         type: NULL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
     filtered: NULL
        Extra: No tables used
1 row in set, 1 warning (0.00 sec)

mysql> SHOW WARNINGS;
+-------+------+------------------------------------------+
| Level | Code | Message                                  |
+-------+------+------------------------------------------+
| Note  | 1249 | Select 2 was reduced during optimization |
+-------+------+------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT * FROM t2;
Empty set (0.00 sec)

  可是,若是外部SELECT引用任何表,優化器也會執行子查詢中的語句,結果是修改了t2:

mysql> EXPLAIN SELECT * FROM t1 AS a1, (SELECT f1(5)) AS a2\G
*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: <derived2>
   partitions: NULL
         type: system
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: NULL
*************************** 2. row ***************************
           id: 1
  select_type: PRIMARY
        table: a1
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: NULL
*************************** 3. row ***************************
           id: 2
  select_type: DERIVED
        table: NULL
   partitions: NULL
         type: NULL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
     filtered: NULL
        Extra: No tables used
3 rows in set (0.00 sec)

mysql> SELECT * FROM t2;
+------+
| c1   |
+------+
|    5 |
+------+
1 row in set (0.00 sec)

  這也意味着EXPLAIN SELECT語句(例如此處顯示的語句)可能須要很長時間才能執行,由於會對t1中的每一行執行一次BENCHMARK()函數:

EXPLAIN SELECT * FROM t1 AS a1, (SELECT BENCHMARK(1000000, MD5(NOW())));

 

 

9、子查詢錯誤

  有些錯誤僅適用於子查詢。本節介紹它們。

  9.1 不被支持的子查詢語法

ERROR 1235 (ER_NOT_SUPPORTED_YET)
SQLSTATE = 42000
Message = "This version of MySQL doesn't yet support
'LIMIT & IN/ALL/ANY/SOME subquery'"

  這意味着MySQL不支持如下形式的語句:

SELECT * FROM t1 WHERE s1 IN (SELECT s2 FROM t2 ORDER BY s1 LIMIT 1)

  

  9.2 子查詢中的列數不正確

ERROR 1241 (ER_OPERAND_COL)
SQLSTATE = 21000
Message = "Operand should contain 1 column(s)"

  這個錯誤會在如下狀況下發生:

SELECT (SELECT column1, column2 FROM t2) FROM t1;

  若是目的是行比較,則能夠使用返回多個列的子查詢。而在其餘上下文中,子查詢必須是標量操做數。參考前面的第五小節。

  本身的試驗以下(student表中有12行數據):

mysql> select ( select SId from sc) from student;
ERROR 1242 (21000): Subquery returns more than 1 row

select (select SId,CId from sc) from student;
ERROR 1241 (21000): Operand should contain 1 column(s)

mysql> select ( select distinct SId from sc where SId="01") from student;
+-----------------------------------------------+
| ( select distinct SId from sc where SId="01") |
+-----------------------------------------------+
| 01                                            |
| 01                                            |
| 01                                            |
| 01                                            |
| 01                                            |
| 01                                            |
| 01                                            |
| 01                                            |
| 01                                            |
| 01                                            |
| 01                                            |
| 01                                            |
+-----------------------------------------------+
12 rows in set (0.00 sec)

 

 

  9.3 子查詢中的行數不正確

ERROR 1242 (ER_SUBSELECT_NO_1_ROW)
SQLSTATE = 21000
Message = "Subquery returns more than 1 row"

  對於子查詢必須最多返回一行但返回多行的語句,會發生此錯誤。參考如下示例:

SELECT * FROM t1 WHERE column1 = (SELECT column1 FROM t2);

  若是SELECT column1 FROM t2只返回一行,則前一個查詢將起做用。若是子查詢返回多行,則會發生錯誤1242。

  這種狀況下,能夠這麼修改:

SELECT * FROM t1 WHERE column1 = ANY (SELECT column1 FROM t2);

 

  9.4 子查詢中的表使用不正確:

Error 1093 (ER_UPDATE_TABLE_USED)
SQLSTATE = HY000
Message = "You can't specify target table 'x'
for update in FROM clause"

  當一個語句嘗試修改表,而這個語句的子查詢卻從同一個表執行SELECT操做:

UPDATE t1 SET column2 = (SELECT MAX(column1) FROM t1);

  您能夠在UPDATE語句中使用子查詢進行賦值,由於子查詢在UPDATE和DELETE語句以及SELECT語句中都是合法的。可是子查詢的FROM子句中不是出現UPDATE的目標表。

 

  對於事務存儲引擎,子查詢失敗會致使整個語句失敗。

  對於非事務性存儲引擎,將保留在遇到錯誤以前進行的數據修改。

相關文章
相關標籤/搜索