有沒有人曾告訴你,你的SQL又報錯了?

發表於2016-01-23

引語:線上執行的真實環境總是變幻莫測,明明你在本地測試的時候各種情況都是OK得不要不要的,也許你還在為自己某個地方炫酷的效果以及神奇的設計感到激動不已!但是,到線上以後,他就會偶爾跟抽風一樣的跟你say Hello,World!然後會有人跟你說,這裡出問題了,那裡出問題了!反應往往是這樣,“不可能!”,“媽蛋,怎麼可能?”,“我就納悶了,怎麼可能出現這種問題呢?”。哈哈,這也許就是大多數攻城獅朋友們最經常發出的感嘆吧!

那麼,今天我們就來聊聊怎樣發現你的錯誤以及解決一些錯誤的快速定位方法,而不是等到使用者來你這裡反饋,因為那時候可能已經錯太久了!我們要做的,應該是將錯誤發現在第一時間,解決在萌芽之中,作出事後總結以避免以後再犯類似錯誤!因為,錯不可怕,可怕的是一直犯同樣的錯誤,那樣的話,你和新手有何差別?

說明幾點:

1. 本文主要解決的問題是SQL相關的錯誤;

2. 本文以PHP微視角出發;(媽蛋,誰叫我是從事PHP開發呢?)

3. 本文解決的問題為,1 如何第一時間發現問題;2 如何解決問題;

4. 歡迎質疑、補充;

正題一、如何發現問題?

測試什麼的那就不用說了,誰TM敢不過測試就直接上線?如果真是那樣,我只能說,你牛逼!測試是一道很重要的防線保障,一般來說,經過測試後的功能,上線之後,Bug不會太多,或者說不會太明顯!好吧,我就假設測試一個問題都沒有發現,那麼, 我們就上線吧!其實,上線之後,我們心裡是沒有底的,尤其是在某些還沒有一套完善的部署系統的公司或專案中,上線前和上線後總會出各種稀奇古怪。到底開發和使用者是兩個層面的人,鬼知道會發生什麼呢?我們懸著的一顆心,竟然要完全依賴於使用者的操作,使用者的反饋?噢,不,那樣,太被動了!

主動發現問題。  一、上線之後,你也不可能再進行測試了,你現在就是一個普通使用者,那麼,你自己去操作就是必須的,一個大概走下來,基本功能可以確定了,Ok,接下來,真正交給使用者!  二、如果沒有後手,就真的完全交給使用者,那你還是太Low了,因為,必要的監控措施是一定要有的!這裡指的監控是程式級別的,也就是所謂的報錯。怎樣記錄報錯資訊?怎樣知道報錯了?都說了嘛,我是從事PHP開發的。PHP中有一個記錄錯誤日誌的功能,error_reporting,把這個給開啟,指定錯誤日誌位置,級別,就可以記錄PHP的錯誤了,但是一定要關閉錯誤的頁面顯示,否則,使用者看到此類錯誤,你就完蛋了!既然本文說的是SQL錯誤,那不應該是這裡,沒錯。但是我要說的是,當SQL報錯的時候,PHP也已經發出報錯了,通常是一個警告級別的錯誤,而且這種錯誤往往是關聯性的發生,如下面的語句依賴於上面的查詢,而上面已經報錯,那麼後續也會跟著報錯。PHP知道錯誤了,但是隻是大概,那SQL具體哪裡錯了?只有他自己知道,這種記錄就交給他吧!大概原理就是,在查詢出錯的地方,記錄錯誤日誌,錯誤日誌主要記錄資訊有:錯誤資訊,檔案位置(追根溯源一個個檔案查上去直到入口),下面是一段示例記錄錯誤資訊的PHP程式碼供參考:

這樣,你就有了一個可供參考的東西了。但是,還有一個關鍵的問題,就是你怎麼知道報錯了?那就是報警提示了,你可以使用微信提供的測試號通知自己、你可以使用公司的簡訊平臺給自己發簡訊、你可以發郵件給自己,然後設定郵件簡訊提醒!收到資訊,趕緊解決去吧,更改的時候,你只需去檢視這個檔案指示的地方,你就大概知道是什麼錯了,都知道什麼錯了,我想,要想解決問題,應該只是個時間問題了,而且是個短時間問題!

正題二、如何解決問題?

其實前面我已經說了,既然已經找到問題根源了,要解決問題只是個短時間問題,但是,我還是將給出一些解決方案作參考,畢竟,大家都這麼忙,哪有時間破解你那爛程式碼!如下是一經常會出現的錯誤:

1. 報錯:[2002] 由於目標計算機積極拒絕,無法連線。解析:資料庫連線資訊錯誤,你可能把測試環境連線部署到線上去了,這問題大發了,趕緊恢復吧? 附註:如果沒有報錯是因為你線上機器可以連線測試庫,那問題也大發了;

2. 報錯:[1146] Table ‘bbbq’ doesn’t exist。解析:表不存在,趕緊查查,是不是本次新加的表沒有被新增到線上吧,或者表名寫錯了? 附註:一般過了測試的不太可能是表名寫錯了;

3. 報錯:[1054] Unknown column ‘column_x’ in ‘field list’。解析:指定的列不存在,要麼庫中沒有,要麼寫錯了,趕緊查! 附註: 同上;

4. 報錯:[1366] Incorrect string value: ‘xA9x96’ for column ‘x_name’ at row 1。解析:字符集問題,如一個gbk的字被欄位設定為gb2312接收則會出現此問題,趕緊改回來吧!附註:請儘量使用utf-8編碼,程式碼與入庫都方便;

5. 報錯:[1364] Field ‘pid’ doesn’t have a default value。解析:指定欄位沒有預設值,請確認應該獲取到的值是否未獲取從而為null,為某些不必要欄位指定資料庫預設值。   附註: 如有索引關係,請一定設定not null 選項,否則索引將可能失效;

6. 報錯:[1366] Incorrect integer value: ” for column ‘townId’ at row 1。解析:給定的值不符合欄位型別要求,在入庫之前先確認該欄位需要什麼型別,可做相應強制轉換,再入庫。  附註:某些版本的mysql可以自行強制轉換型別處理此問題,但是結果可能已經超出你的預期;

7. LOAD DATA INFILE ,從檔案直接匯入資料到資料庫

7.1. 報錯:[2] File ‘D:/wamp/www/a/area.csv’ not found (Errcode: 2)。解析:找不到指定的csv檔案,確認目錄位置是否給定正確,如果正確,確認該檔案是否為動態生成而目前尚未生成。  附註:小問題;

7.2. 報錯:[13] Can’t get stat of ‘/opt/app/mysql5/var/D:/wamp/www/a/area.csv’ (Errcode: 2)或者提示沒有許可權運算元據庫。解析:資料庫與web不在同一臺伺服器,需要指定關鍵字LOCAL,從而將web與資料庫分開。  附註:LOCAL引數是在必要時候使用,因為不指定LOCAL的操作將看起來更安全;

7.3. 報錯:[1366] Incorrect integer value: ‘北京’ for column ‘provinceId’ at row 1。解析:這裡使用不指定欄位的方式插入資料庫,因此,如果csv檔案的值順序與資料庫欄位順序不對應的話,將會有很多類似的報錯,而這則是致命的,因為全部都錯了,即使偶有個別正確入庫的,那也是錯的你也不會想要的結果,請以正確的順序寫入csv檔案或者指定欄位;  附註:這裡的1366 與前面的1366 意義是不一樣的;

7.4. 示例LOAD:  LOAD DATA LOCAL INFILE ‘D:/wamp/www/a/area.csv’ REPLACE INTO TABLE address CHARACTER SET utf8  FIELDS TERMINATED BY ‘,’ (provinceId, provinceName, cityId, cityName, areaId, areaName, townId, townName);  附註:要求每個欄位都是有值的,即幾個欄位就要幾個’,’;

8. [1064] You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘LIMIT 0, 1000’ at line 1。解析:好吧,這是個最通用的錯誤解釋,就是說你的語法寫錯了。比如 where a_id= order by id desc;這裡你原本是想獲取一個ID然後去查詢,但是後面的ID得到空值,所以整個語法就錯了。其實,如果是直接接收引數去查詢,這本身可能報錯,也是一個注入點,請一定要處理傳入的值,一般要求’=’號後給”號,整型引數用intval()格式化等等安全意識!

9. 講解:REPLACE與INSERT 功能其實是差不多的(唯一鍵是必須的),但是REPLACE會在資料庫中存在此記錄時先刪除再插入,如有自增ID,將會被迅速變大,從而不必手動執行刪除操作,而INSERT重複資料時則將報錯(可配合UPDATE使用)。適當使用兩個功能,解決問題!

在真實開發中,其實遇到很多有意思的問題,但一時間也難以想起來,想起來再補充,你也可以下方回覆你遇到的問題及解決方案,大家一起參考!

相關文章