01/10/2018, 11:19

Vài lỗi ngớ ngẩn của mình khi gõ truy vấn MySQL trong PHP

Mình học PHP/MySQL mới được hơn nửa năm nay. Bây giờ thì mình đang làm bài tập nhóm cuối khóa.
Làm thì mình vướng vài chuyện không hay: 1 là phải gánh team (nhưng thôi bỏ qua vấn đề này), 2 là dù làm nhiều nhưng vẫn hay mắc mấy lỗi ngớ ngẩn. Trong post này, mình chia sẻ điều số 2.

  1. Lỗi chính tả, cú pháp
    Khi có bug, điều đầu tiên phải làm là kiểm tra lỗi chính tả, mình luôn tự nhắc nhở mình như thế: dấu chấm phẩy, dấu phẩy, tên cột trong bảng có viết đúng không, viết có đúng phương thức không…
  2. Lỗi truy vấn
    Khi mà chẳng tìm thấy lỗi chính tả và cú pháp nào mà vẫn sai thì mình chắc chắn đến 80% là có lỗi trong câu truy vấn rồi.
    2.1) Tên cột không rõ ràng
    Điều này xảy ra khá thường xuyên khi mình thực hiện truy vấn SELECT có phép JOIN. JOIN dùng được khi hai bảng có kết nối bằng khóa ngoại. Vấn đề này sẽ chỉ xảy ra nếu tên cột chứa khóa ngoại của bảng này lại giống hệt với tên cột chứa khóa chính của bảng kia. Trong tình huống đó thì phải chỉ rõ cột thuộc bảng nào: table_name.col_name. Một cách giải quyết khác là đặt tên hai cột khác nhau(mình chọn cách này).
    2.2) Thừa, thiếu dấu ngoặc tròn, dấu nháy tên cột trong câu lệnh INSERT INTO.
  3. Prepare Statement
    Khi thực hiện truy vấn MySQL trong PHP, 100% mình đều sử dụng Prepare Statement và mình hay vướng vào mấy lỗi kiểu này:
    3.1) Thừa thiếu mấy dấu ?
    3.2) Phép toán LIKE
    Lần đầu dùng phép LIKE cùng với Prepare Statement, mình đã gõ kiểu này:
SELECT * FROM table_name WHERE col_name LIKE "%?%"

Mình đã hi vọng là nó sẽ lấp kí tự vào khi thực hiện Prepare Statement thế nhưng không được. Hóa ra là phải gõ thế này:

SELECT * FROM table_name WHERE col_name LIKE ?

Dấu ? đó sẽ được lấp vào bằng chuỗi %từ_tìm_kiếm%
3.3) Dùng REGEXP BINARY
Phép toán này mình mới biết hôm qua khi tìm kiếm một thôi một hồi trên google về cách tìm kiếm có dấu tiếng Việt. Đại đa số đề cập đến FULLTEXT search(tìm kiếm toàn văn) nhưng mình cứ loay hoay cho tới khi đọc một bài viết khác có đề xuất sử dụng REGEXP BINARY. Ưu điểm của cách này là nó rất dễ hiểu (bản chất là quy đổi hết thành chuỗi nhị phân để so sánh), không cần tạo FULLTEXT KEY, bảng có kiểu lưu trữ là InnoDB cũng được - trong khi FULLTEXT chỉ áp dụng cho MyISAM thì vài phiên bản gần đây InnoDB mới áp dụng.
Tìm kiếm khớp có dấu thì mình viết kiểu:

SELECT * FROM table_name WHERE col_name REGEXP BINARY "^từ_tìm_kiếm$"

Lưu ý trong biểu thức chính quy phải có ^ và $ thì mới là tìm kiếm khớp.
Lỗi Prepare Statement khi dùng REGEXP BINARY của mình cũng tương tự như khi dùng LIKE vậy. Phải làm như sau mới được:

SELECT * FROM table_name WHERE col_name REGEXP BINARY ?

Dấu ? sẽ được lấp bởi chuỗi “^từ_tìm_kiếm$”.
Nói thêm là cái lỗi ngớ ngấn này đã ngốn luôn cả tối hôm qua và sáng ngày hôm nay của mình.
4) JSON
Lỗi này là khi gửi hoặc nhận dữ liệu dạng JSON thì mình quên không sử dụng các hàm json_decode(), json_encode(), hoặc các phương thức JSON.parse(), JSON.stringify()


Nội dung của post này gói gọn trong kiến thức hạn hẹp của mình nhưng mình tin là chúng vẫn có ích.

Phan Bá Hải viết 13:21 ngày 01/10/2018

Mình là dân Java Web
Khi làm việc với SQL, ngày xưa chỉ dính 1 lỗi duy nhất, lỗi gõ sai truy vấn SQL. Còn syntax error có IDE lo
Ngày xưa ghét cay ghét đắng các ORM Framework, chỉ điểm luôn là Hibernate, vì nó config thì rối, lỗi thì từa lưa khi update lại db, Netbeans gen model như củ cải, trong SQL Server khai báo NVARCHAR nhưng Hibernate lại gen ra Serializable , làm phải sửa cả file java + xml
Ngày xưa kiểu thế này
User.java

package entities;
// Generated Feb 3, 2017 9:17:18 PM by Hibernate Tools 4.3.1

/**
 * User generated by hbm2java
 */
public class Users  implements java.io.Serializable {
    
     private int id;
     private String username; // Serializable -> String
     private String password;
     private String fullname; // Serializable -> String

    public User() {
    }

    public User(String username, String password, String fullname) {
       this.username = username;
       this.password = password;
       this.fullname = fullname;
    }

    public int getId() {
        return this.id;
    }
   
    public void setId(int id) {
        this.id = id;
    }
    public String getUsername() {
        return this.username;
    }
    
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return this.password;
    }
    
    public void setPassword(String password) {
        this.password = password;
    }
    public String getFullname() {
        return this.fullname;
    }
    
    public void setFullname(String fullname) {
        this.fullname = fullname;
    }
}

User.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- Generated Feb 3, 2017 9:17:21 PM by Hibernate Tools 4.3.1 -->
<hibernate-mapping>
    <class name="entities.Users" table="USERS" schema="dbo" catalog="SOF302_Assignment" optimistic-lock="version">
        <id name="id" type="int">
            <column name="ID"/>
            <generator class="assigned" />
        </id>
        <property name="username" type="string"> <!-- Serializable -> String -->
            <column name="USERNAME" length="50" not-null="true" />
        </property>
        <property name="password" type="string">
            <column name="PASSWORD" length="50" not-null="true" />
        </property>
        <property name="fullname" type="string"> <!-- Serializable -> String -->
            <column name="FULLNAME" not-null="true" />
        </property>
    </class>
</hibernate-mapping>

Còn bây giờ xài JPA Annotation để khai báo các thuộc tính của SQL
User.java

package entities;

import javax.persistence.*;

@Entity 
@Table(name = "USER")
public class User  implements java.io.Serializable {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     @Column(name = "ID", nullable = false) 
     private int id;

     @Column(name = "USERNAME", length = 50, nullable = false)
     private String username;

     @Column(name = "PASSWORD", length = 50, nullable = false)
     private String password;

     @Column(name = "FULLNAME", length = 50, nullable = false)
     private String fullname;

    public User() {
    }

    public User(String username, String password, String fullname) {
       this.username = username;
       this.password = password;
       this.fullname = fullname;
    }

    public int getId() {
        return this.id;
    }
   
    public void setId(int id) {
        this.id = id;
    }
    public String getUsername() {
        return this.username;
    }
    
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return this.password;
    }
    
    public void setPassword(String password) {
        this.password = password;
    }
    public String getFullname() {
        return this.fullname;
    }
    
    public void setFullname(String fullname) {
        this.fullname = fullname;
    }
}

Còn nếu SQL thông thường (select 1 phần tử qua ID)
Xài Hibernate như thế này

    public List<Users> getUserById(int id) {
        User user = new User();
        try {
            session.getTransaction().begin();
            user = session.get(User.class, id);
            session.getTransaction().commit();
        } catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }
        return user
    }

Lấy user qua username

    public Users getUserLogin(String username) {
        List<User> users = new ArrayList<>();
        User user = new User();
        try {
            session.getTransaction().begin();
            // Get list users ra, sau đó kiểm tra lại xem có user hay không
            // Mục đích là tránh NullPointerException nếu trả về thẳng user dùng uniqueResult()
            users = session.createQuery("SELECT u FROM User AS u WHERE u.username = :username")
                            .setParameter("username", username);
                            .list();
            if(users.size() > 0) {
                 user = users.get(0);
            }
            session.getTransaction().commit();
        } catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }
        return user;
    }

Một số bạn hay có thói quen viết hàm connect liên tục vào các method này, nhìn nó còn dài hơn
Một số khác thì viết một class connect riêng, sau đó chỉ gọi nó ra để connect vào db, code nhìn nó gọn hơn cả Hibernate nhưng dễ bị lỗi SQL Syntax khi query

Uchiha Sasuke viết 13:33 ngày 01/10/2018

trong SQL Server khai báo NVARCHAR nhưng Hibernate lại gen ra Serializable , làm phải sửa cả file java + xml

giống mh lúc trước, lúc đấy chẳng hiểu sao cứ tưởng cái tool nó bị lỗi, làm mh xóa đi cài lại :v , cuối cùng đành phải tạo bằng tay

Phan Bá Hải viết 13:28 ngày 01/10/2018

Nhiều khi để thằng Netbeans gen code nó hại thân lắm
Giờ chủ trương “né” config xml ra nếu có Annotations thay thế

Uchiha Sasuke viết 13:20 ngày 01/10/2018

mh vẫn thấy như cấu hình định nghĩa các bean được inject vào, ở trong file xml thì vẫn hay hơn dễ đọc, dễ tìm tuy file này hơi lớn còn hơn là mở mấy trăm file ra để cãu hình bằng annotation

Bài liên quan
0