2010-06-06 3 views
13

Ich möchte eine Datenbank-Side-String-Verkettung in einer Rails-Abfrage machen, und tun Sie es datenbankunabhängig.Datenbankunabhängige SQL-String-Verkettung in Rails

SQL-92 spezifiziert Double-Bar (||) als Verkettungsoperator. Leider sieht es so aus, als ob MS SQL Server es nicht unterstützt; es verwendet stattdessen +.

Ich vermute, dass Rails SQL-Grammatik-Abstraktion das db-spezifische Operatorproblem bereits gelöst hat. Wenn es existiert, wie benutze ich es?

Antwort

10

Ich hatte das gleiche Problem und kam nie auf etwas, das in Rails eingebaut wurde. Also habe ich diese kleine Methode geschrieben.

# 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 

Ich denke, jemand sollte wirklich eine Art von SQL-Helfer-Plugin schreiben, die automatisch einfache SQL-Ausdrücke formatieren könnte die richtigen Funktionen oder Operatoren für den aktuellen Adapter basiert. Vielleicht werde ich selbst einen schreiben.

+0

Ich denke, wenn es existiert, wirst du derjenige sein, der es schreibt :-P –

+1

FYI, ich habe es geschrieben http://github.com/adamcrown/port-a-query. Es gibt noch nicht viel zu tun, aber es funktioniert mit der Verkettung in MySQL und SQLite – aNoble

0

Wenn Sie etwas Rails neutral wollen, müssen Sie die Werte, die Sie verkettet haben, zurückgeben und das tun, sobald die Daten an Schienen geliefert wurden (oder in Schienen tun, bevor Sie es der Datenbank geben).

Es sieht wie Mysql verwendet CONCAT(), Postgres ||, Oracle CONCAT() oder ||, T-SQL +.

Jegliche Rails-Abstraktion derselben müsste an einem Punkt stattfinden, an dem Sie gerade eine Verkettung mit regulärem Ruby machen könnten, oder ich habe die Frage völlig falsch verstanden.

+0

Was ich suche ist eine Möglichkeit, auf die verschiedenen Verkettungsmethoden (die Sie aufgelistet) auf eine db-unabhängige Weise zugreifen. Ich stelle mir etwas in Rails vor, das zwei Strings (Ausdrücke) benötigt, sie mit dem db-spezifischen Verkettungsoperator/-funktion verknüpft und dann eine einzelne Zeichenfolge zurückgibt. Ich nehme diese Zeichenfolge und verwende sie dann in meiner SQL-Anweisung. –

6

Es hat noch nicht viel Verwendung erhalten, aber ich schrieb den folgenden Code, der das Problem zu lösen scheint. Dieser Affe-Patches der Adapter, ein Verfahren zu haben, um sie zu unterstützen:

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 

Damit Sie sollen in der Lage sein, die folgend in Ihrem Code zu tun:

class User < ActiveRecord::Base 

    def self.find_by_name(name) 
    where("#{connection.concat('first_name', 'last_name')} = ?", name) 
    end 

end 

Das gibt die folgende SQL-Abfrage auf einem SQL-92-Datenbank (Oracle, SQLite, PostgreSQL):

SELECT * FROM users WHERE first_name || last_name = ? 

Für MySQL gibt sie:

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

Für SQL Server gibt es

SELECT * FROM users WHERE first_name + last_name = ? 

Offensichtlich Sie dieses Konzept auf andere Datenbank-Adapter erweitern könnte.