Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
515 views
in Technique[技术] by (71.8m points)

sql - MySQL 5.7, sort table by column name using a variable in Stored procedure

I have a simple table with some data:

DROP TABLE IF EXISTS `MY_TABLE`;
CREATE TABLE IF NOT EXISTS `MY_TABLE` (
  `id` CHAR(40) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL,
  PRIMARY KEY (`id`)
);

INSERT INTO `MY_TABLE` (`id`) VALUES (1);
INSERT INTO `MY_TABLE` (`id`) VALUES (2);
INSERT INTO `MY_TABLE` (`id`) VALUES (3);

Now I have a procedure to retrieve this data ordering from a column. This works as expected:

DROP PROCEDURE IF EXISTS test_procedure;
DELIMITER $$
CREATE PROCEDURE test_procedure()
BEGIN

  SELECT id AS columnAlias FROM `MY_TABLE`
  ORDER BY columnAlias;

END $$
DELIMITER ;

CALL test_procedure();  /* Returns 1, 2, 3, 4, 5, 6, 7, 8, 9 */

As well as this:

DROP PROCEDURE IF EXISTS test_procedure;
DELIMITER $$
CREATE PROCEDURE test_procedure()
BEGIN

  SELECT id AS columnAlias FROM `MY_TABLE`
  ORDER BY -columnAlias;

END $$
DELIMITER ;

CALL test_procedure(); /* Should return 9, 8, 7, 6, 5, 4, 3, 2, 1 */

Now, my case: the column name has to be passed from the procedure, so I do:

DROP PROCEDURE IF EXISTS test_procedure;
DELIMITER $$
CREATE PROCEDURE test_procedure(IN data JSON)

BEGIN

  SET @sortBy  = JSON_UNQUOTE(JSON_EXTRACT(DATA, '$.sortBy'));
  
  SELECT id AS columnAlias FROM `MY_TABLE`
  ORDER BY @sortBy;

END $$
DELIMITER ;

CALL test_procedure('{"sortBy": "columnAlias"}');  /* Should return 1, 2, 3, 4, 5, 6, 7, 8, 9 */
CALL test_procedure('{"sortBy": "-columnAlias"}'); /* Should return 9, 8, 7, 6, 5, 4, 3, 2, 1 */

But can't make it work. Any help will be welcome.


EDIT:

Given the solution by @bill_karwin I tried to use a CASE, testing agains my variable:

DROP PROCEDURE IF EXISTS test_procedure;
DELIMITER $$
CREATE PROCEDURE test_procedure(IN data JSON)

BEGIN

  SET @sortBy  = JSON_UNQUOTE(JSON_EXTRACT(DATA, '$.sortBy'));
  
  SELECT id AS columnAlias FROM `MY_TABLE`
  ORDER BY 
    CASE @sortBy 
      WHEN "columnAlias" THEN columnAlias
      WHEN "-columnAlias" THEN -columnAlias
    END
 ;

END $$
DELIMITER ;

CALL test_procedure('{"sortBy": "-columnAlias"}'); 

Doesnt work though, returns 1, 2, 3, while it should be reversed as I'm passing the string with a minus prepended: -columnAlias.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You could use a CASE expression:

SELECT id AS columnAlias FROM `MY_TABLE`
ORDER BY CASE @sortBy WHEN 'updated_at' THEN updated_at 
                      WHEN 'user_id' THEN user_id
                      ELSE id END;

Some people use dynamic SQL, but the CASE expression solution is good because it automatically checks the input against a fixed set of columns, so you don't accidentally cause an SQL injection vulnerability.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...