天天看點

子查詢中有個不存在的列居然不報錯是bug嗎?問題描述原因風險正确又安全的寫法參考

問題描述

有開發問我這樣一個問題:

mysql> select * from aaa;
+----+---------------------+----------+---------------------+
| id | dt                  | name     | dtt                 |
+----+---------------------+----------+---------------------+
|  1 | 2019-01-14 18:15:39 | aaaaaaaa | 2019-01-30 17:14:08 |
+----+---------------------+----------+---------------------+
1 row in set (0.00 sec)

mysql> select * from bbb;
+----+---------------------+----------+
| id | dt                  | name     |
+----+---------------------+----------+
|  1 | 2019-01-14 18:19:19 | aaaaaaaa |
|  2 | 2019-01-14 18:20:49 | aaaaaaaa |
+----+---------------------+----------+
2 rows in set (0.00 sec)

mysql> select * from aaa where id in (select id from bbb where dtt<now());
+----+---------------------+----------+---------------------+
| id | dt                  | name     | dtt                 |
+----+---------------------+----------+---------------------+
|  1 | 2019-01-14 18:15:39 | aaaaaaaa | 2019-01-30 17:14:08 |
+----+---------------------+----------+---------------------+
1 row in set (0.00 sec)           

上面内容裡dtt這個字段在bbb表中并不存在,但是在外表中存在,但是為什麼不報錯反而查出來結果了呢?

原因

我請PostgreSQL的同僚也做了同樣的操作,也是不報錯的;

這個在SQLServer和Oracle裡面同樣成立,why?

一般規則是,語句中的列名由FROM子句中引用的表隐式地限定在同一級别。如果子查詢的FROM子句中引用的表中不存在列,則由外部查詢的FROM子句中引用的表隐式地限定列。

即:在塊結構語言計算子查詢時,它開始在本地查找以解析列名。如果失敗,則轉到外部範圍,直到找到具有該名稱的列或失敗為止。

風險

如果有開發這樣執行delete語句,可能會導緻全表被誤删!

mysql> delete from aaa where id in (select id from bbb where dtt<now());           

正确又安全的寫法

帶上表名

mysql> select aaa.* from aaa where aaa.id in (select bbb.id from bbb where bbb.dtt<now());
mysql> ERROR 1054 (42S22): Unknown column 'bbb.dtt' in 'where clause'           

參考

https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2008-r2/ms178050(v=sql.105) https://social.msdn.microsoft.com/Forums/sqlserver/en-US/4cfebb2e-6fdf-4ebf-9a6f-ee1911615eba/query-returning-wrong-data-even-inner-subquery-has-syntax-error?forum=transactsql

繼續閱讀