Codeigniter : Error in ORDER BY CASE query

2020-03-02 05:02发布

问题:

Here is my query in Codeigniter

$this->db->select('p.*,u.firstname, u.lastname,s.title AS industry, pt.type_name , al.length_value',FALSE);
$this->db->from($this->_tbl_projects . ' as p');
$this->db->join($this->_tbl_client_details . ' as c', 'c.id = p.client_id', 'left');
$this->db->join($this->_tbl_users . ' as u', 'u.id = c.user_id', 'left');
$this->db->join($this->_tbl_project_types . ' as pt', 'pt.project_type_id = p.project_type_id', 'left');
$this->db->join($this->_tbl_specializations . ' as s', 's.specialization_id = p.specialization_id', 'left');
$this->db->join($this->_tbl_article_length . ' as al', 'al.article_length_id = p.article_length_id', 'left');
$this->db->order_by("CASE p.submit_to
                                    WHEN '' THEN 0
                                    WHEN 'writer' THEN 1
                                    ELSE 2
                                END, p.request_end_date asc",FALSE);

It Print out

SELECT p.*, u.firstname, u.lastname, s.title AS industry, pt.type_name, al.length_value 
FROM (`projects` as p) 
LEFT JOIN `client_details` as c ON `c`.`id` = `p`.`client_id` 
LEFT JOIN `users` as u ON `u`.`id` = `c`.`user_id` 
LEFT JOIN `project_types` as pt ON `pt`.`project_type_id` = `p`.`project_type_id` 
LEFT JOIN `specializations` as s ON `s`.`specialization_id` = `p`.`specialization_id` 
LEFT JOIN `article_length` as al ON `al`.`article_length_id` = `p`.`article_length_id` WHERE `p`.`client_id` = '26' AND `p`.`status` IN (2, 3) 
ORDER BY 
        `CASE` p.submit_to 
            WHEN '' THEN 0 
            WHEN 'writer' THEN 1 
            ELSE 2 
         END, `p`.`request_end_date` asc

Here in printed query, CASE is printed by `CASE`, so sql throws error.

How can I solve it?

Structure of submit_to field is

  submit_to enum('','writer','students') NOT NULL

回答1:

The CodeIgniter documentation indicates case statements within the order by clause isn't supported within the Active Record class. I'd recommend refactoring the SQL call so that the case statement is part of the select clause. Something like the following should do the trick.

$this->db->select("p.*,u.firstname, u.lastname,s.title AS industry, pt.type_name, al.length_value, CASE p.submit_to WHEN 'writer' THEN 2 WHEN 'students' THEN 1 ELSE 0 END AS ordered_submit_to",FALSE);
$this->db->from($this->_tbl_projects . ' as p');
$this->db->join($this->_tbl_client_details . ' as c', 'c.id = p.client_id', 'left');
$this->db->join($this->_tbl_users . ' as u', 'u.id = c.user_id', 'left');
$this->db->join($this->_tbl_project_types . ' as pt', 'pt.project_type_id = p.project_type_id', 'left');
$this->db->join($this->_tbl_specializations . ' as s', 's.specialization_id = p.specialization_id', 'left');
$this->db->join($this->_tbl_article_length . ' as al', 'al.article_length_id = p.article_length_id', 'left');
$this->db->order_by('ordered_submit_to', 'ASC');
$this->db->order_by('p.request_end_date', 'ASC');


回答2:

I found a good solution in another answer on SO so in case you've landed on this page and haven't found that one, I'll re-post it here.


You are able to use a case statement if you wrap the case statement in parentheses.

$this->db->order_by("
    (CASE p.submit_to
        WHEN '' THEN 0
        WHEN 'writer' THEN 1
        ELSE 2
    END), 
    p.request_end_date asc"
);


回答3:

I figured it out how to avoid escaping CASE word.

You can modify $_reserved_identifiers variable inside CI_DB_driver class. If you don't want to modify whole file, you can simply change it inside your Model class just before that query.

$this->db->_reserved_identifiers = array('*','CASE');

$this->db->select('p.*,u.firstname, u.lastname,s.title AS industry, pt.type_name , al.length_value',FALSE);
$this->db->from($this->_tbl_projects . ' as p');
$this->db->join($this->_tbl_client_details . ' as c', 'c.id = p.client_id', 'left');
$this->db->join($this->_tbl_users . ' as u', 'u.id = c.user_id', 'left');
$this->db->join($this->_tbl_project_types . ' as pt', 'pt.project_type_id = p.project_type_id', 'left');
$this->db->join($this->_tbl_specializations . ' as s', 's.specialization_id = p.specialization_id', 'left');
$this->db->join($this->_tbl_article_length . ' as al', 'al.article_length_id = p.article_length_id', 'left');
$this->db->order_by("CASE p.submit_to
                                WHEN '' THEN 0
                                WHEN 'writer' THEN 1
                                ELSE 2
                            END, p.request_end_date asc",FALSE);

(I don't know if it's working on 3.x branch. It's working good on 2.x branch)



回答4:

I tested today and it's working.

You can use it like you said whenever you use the second parameter FALSE to disable escaping.

$this->db->order_by("CASE p.submit_to
                                    WHEN '' THEN 0
                                    WHEN 'writer' THEN 1
                                    ELSE 2
                                END, p.request_end_date asc",FALSE);

Greetings.



回答5:

Set your "CASE WHEN" inside your "SELECT" and not inside the "ORDER_BY":

$this->db->select('p.*,u.firstname, u.lastname,s.title AS industry, pt.type_name , al.length_value',FALSE);
$this->db->select("CASE p.submit_to WHEN '' THEN 0 WHEN 'writer' THEN 1 ELSE 2 END order_column_number",FALSE);
$this->db->from($this->_tbl_projects . ' as p');
$this->db->join($this->_tbl_client_details . ' as c', 'c.id = p.client_id', 'left');
$this->db->join($this->_tbl_users . ' as u', 'u.id = c.user_id', 'left');
$this->db->join($this->_tbl_project_types . ' as pt', 'pt.project_type_id = p.project_type_id', 'left');
$this->db->join($this->_tbl_specializations . ' as s', 's.specialization_id = p.specialization_id', 'left');
$this->db->join($this->_tbl_article_length . ' as al', 'al.article_length_id = p.article_length_id', 'left');
$this->db->order_by("order_column_number ASC, p.request_end_date ASC");