Dynamic SQL result sorting

Asked

Viewed 229 times

2

First good afternoon, I work specifically with Java in the Back-end using Webservices Rest and Postgresql, after some professional enhancements I was instructed to pass ordering and pagination of my application to SQL, that would burden the application server less and the results would return in an agile way. So far paging was not the problem, I could use FETCH or LIMIT to be able to control it, but I reached an impasse when I went ahead to sort. Using CASE in ORDER BY in SQL could not use it dynamically to sort different typing columns. Follow the example:

SELECT
    a.idade,
    a.nome,
    a.cpf
FROM
    cadastro a
ORDER BY
(
    CASE 1 WHEN 1 THEN
        a.idade
    WHEN 2 THEN
        a.nome
    WHEN 3 THEN
        a.cpf
    END
)

This example quoted above would not successfully execute columns that are not of the same type. And doing several CASE would not be cool and productive to set so many parameters in Java Prepared. I asked in the SQL community and they guided me to do the scripts in functions and pass sort as parameter, but I did not find this form very attractive. So I thought about String formatting before moving to Prepared, but it is susceptible to SQL Injection and I certainly do not want to risk this option.

So I ask for tips, I thank you already!

Obs.: About having mentioned XY problem. I tried to do CASE in SQL as mentioned above and thought about the hypothesis of mounting the script in Function and view, but it is not a solution that pleased my boss, conversion of types would not work for all cases of scripts system, so I am looking for alternative solutions. String concatenation before sending to Preparedstatement also thought, however how to avoid an SQL Injection?

  • I’m not sure I understand what you’re trying to do, but it seems to me to be a case of a XY problem. It would not be better if you just assemble 3 different Sqls queries, one ordering by age, one ordering by name and one ordering by CPF and let the application choose which of these queries will be sent to the database, as the case may be?

  • try SELECT a.age, a.name, a.Cpf FROM register a ORDER BY ( CASE 1 WHEN 1 THEN lpad(a. age, 3, '0') WHEN 2 THEN a.name WHEN 3 THEN a.Cpf END )

  • Does sorting come according to the sequence of fields per parameter? Wouldn’t it be simpler for you to pass the field?

  • @Victorstafusa in my view could be a better solution than this. If every system script I go to do server side ordering I do this, it will be nonproductive.

  • @Motta is not a suitable solution, do conversion.

  • @Karanalvespereira Preparedstatment does not accept column name pass.

Show 1 more comment

2 answers

1

You can "mount" your query using a StringBuilder:

public String montarQueryConsulta( int ordem )
{
    StringBuilder sb = new StringBuilder();

    sb.append("SELECT c.idade, c.nome, c.cpf FROM tb_cadastro AS c ORDER BY ");

    switch( ordem )
    {
        case 1:
            sb.append("c.idade");
            break;

        case 2:
            sb.append("c.nome");
            break;          

        case 3:
            sb.append("c.cpf");
            break;

        default:
            sb.append("");
    }

    return sb.toString();
}

And then build a PreparedStatement from the mounted string:

PreparedStatement ps = conn.prepareStatement( montarQueryConsulta(2) );
ResultSet rs = ps.executeQuery();
  • This would not be a loophole for an SQL Injection?

  • 1

    @Eyelash: Not in the above example. Mounting the query by means of a StringBuilder should always be done with great care to avoid such a breach. Do not use the PreparedStatement implies individual filtering of each input parameter. Note that in the response the input parameter ordem is treated as a whole and in a very restricted way.

0

If you really want to go this way you can just cast (to sweep), so you would get around the problem of columns being of different types.

But note that using a clause CASE within a clause ORDER BY is not very performatic.

Consider changing this to three queries (or maybe even "reuse code" to create a view) and calling each query according to the condition.

  • create a view and Function, we have reached almost the same impasse. My boss did not like this solution much. And in the case as we are making paging and ordering server side, I have this problem on my hands.

  • @Eyelash Not a Function, just do the chaining of IF. In Postgres I don’t know how he deals with it. In other Dbms each query within one IF has its own query plan and improves performance. Using a view would be optional, a good option if you don’t want to rewrite the same query n times. Note that this is a good solution when you have few options. In case if you had 15 columns the best would be parametized p/dynamic queries.I see no problem in paging.

  • You can write a generic paging query as an SP and pass your query as a parameter (I’ve done this a few times and usually the result is good). Do not call P/q your boss says unless he is a good developer/engineer/dba. Who will give maintenance on this is vc.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.