Database-independent SQL String Concatenation in Rails

后端 未结 3 1758
难免孤独
难免孤独 2020-12-10 04:03

I want to do a database-side string concatenation in a Rails query, and do it in database-independent way.

SQL-92 specifies double-bar (||) as the conca

相关标签:
3条回答
  • 2020-12-10 04:25

    If you want something Rails neutral, you're going to need to return the values you want concatenated and do that once the data has been delivered to rails (or do it in rails before you give it to the database).

    It looks like Mysql uses CONCAT(), Postgres ||, Oracle CONCAT() or ||, T-SQL +.

    Any rails abstraction of the same would have to take place at a point where you could just be doing concatenation using regular Ruby, or I've completely misunderstood the question.

    0 讨论(0)
  • 2020-12-10 04:36

    It hasn't received much usage yet but I wrote the following code which seems to solve the problem. This monkey-patches the adapters to have a method to support it:

    module ActiveRecord
      module ConnectionAdapters
        class AbstractAdapter
    
          # Will return the given strings as a SQL concationation. By default
          # uses the SQL-92 syntax:
          #
          #   concat('foo', 'bar') -> "foo || bar"
          def concat(*args)
            args * " || "
          end
    
        end
    
        class AbstractMysqlAdapter < AbstractAdapter
    
          # Will return the given strings as a SQL concationation.
          # Uses MySQL format:
          #
          #   concat('foo', 'bar')  -> "CONCAT(foo, bar)"
          def concat(*args)
            "CONCAT(#{args * ', '})"
          end
    
        end
    
        class SQLServerAdapter < AbstractAdapter
    
          # Will return the given strings as a SQL concationation.
          # Uses MS-SQL format:
          #
          #   concat('foo', 'bar')  -> foo + bar
          def concat(*args)
            args * ' + '
          end
    
        end
      end
    end
    

    With this you should be able to do the following in your code:

    class User < ActiveRecord::Base
    
      def self.find_by_name(name)
        where("#{connection.concat('first_name', 'last_name')} = ?", name)
      end
    
    end
    

    This outputs the following SQL query on a SQL-92 database (Oracle, SQLite, PostgreSQL):

    SELECT * FROM users WHERE first_name || last_name = ?
    

    For MySQL it outputs:

    SELECT * FROM users WHERE CONCAT(first_name, last_name) = ?
    

    For SQL Server it outputs

    SELECT * FROM users WHERE first_name + last_name = ?
    

    Obviously you could extend this concept to other database adapters.

    0 讨论(0)
  • 2020-12-10 04:46

    I had the same problem and never came up with anything that was built into Rails. So I wrote this little method.

    # Symbols should be used for field names, everything else will be quoted as a string
    def db_concat(*args)
    
      adapter = configurations[RAILS_ENV]['adapter'].to_sym
      args.map!{ |arg| arg.class==Symbol ? arg.to_s : "'#{arg}'" }
    
      case adapter
        when :mysql
          "CONCAT(#{args.join(',')})"
        when :sqlserver
          args.join('+')
        else
          args.join('||')
      end
    
    end
    

    I'm thinking somebody should really write some sort of SQL helper plugin that could automatically format simple SQL expressions based using the correct functions or operators for the current adapter. Maybe I'll write one myself.

    0 讨论(0)
提交回复
热议问题