Независимая от базы данных конкатенация строк SQL в Rails
Я хочу выполнить конкатенацию строки на стороне базы данных в запросе Rails и сделать это независимым от базы данных способом.
SQL-92 задает двойной бар (||
) как оператор конкатенации. К сожалению, похоже, что MS SQL Server не поддерживает его; вместо этого он использует +
.
Я предполагаю, что аббревиатура грамматики SQL Rails уже решила проблему оператора, специфичную для db. Если он существует, как его использовать?
Ответы
Ответ 1
У меня была такая же проблема, и я никогда не придумывал ничего, что было встроено в Rails. Поэтому я написал этот маленький метод.
# 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
Я думаю, что кто-то должен действительно написать какой-то SQL-помощник, который мог бы автоматически форматировать простые выражения SQL, основанные на использовании правильных функций или операторов для текущего адаптера. Возможно, я сам напишу.
Ответ 2
Он еще не получил много пользы, но я написал следующий код, который, кажется, решает проблему. Эта обезьяна исправляет адаптеры, чтобы иметь способ ее поддержки:
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
С этим вы сможете сделать следующее в своем коде:
class User < ActiveRecord::Base
def self.find_by_name(name)
where("#{connection.concat('first_name', 'last_name')} = ?", name)
end
end
Это выводит следующий SQL-запрос в базе данных SQL-92 (Oracle, SQLite, PostgreSQL):
SELECT * FROM users WHERE first_name || last_name = ?
Для MySQL он выводит:
SELECT * FROM users WHERE CONCAT(first_name, last_name) = ?
Для SQL Server он выводит
SELECT * FROM users WHERE first_name + last_name = ?
Очевидно, вы можете расширить эту концепцию до других адаптеров баз данных.
Ответ 3
Если вы хотите, чтобы Rails был нейтральным, вам нужно будет вернуть значения, которые вы хотите объединить, и сделать это, как только данные будут доставлены на рельсы (или сделайте это в рельсах, прежде чем вы передадите их в базу данных).
Похоже, что Mysql использует CONCAT(), Postgres ||, Oracle CONCAT() или ||, T-SQL +.
Любая реляционная абстракция того же самого должна была бы иметь место в точке, где вы могли бы просто выполнять конкатенацию с использованием обычного Ruby, или я полностью не понял этот вопрос.