Skip to content


Subversion checkout URL

You can clone with
Download ZIP


Sql server #44

wants to merge 2 commits into from

5 participants



This branch adds basic support for SQL Server.

Some tests are failing but it's due to the fact that SQL Server doesn't accepts null insertion into primary keys.
INSERT INTO [group] ([id],[order],[created_at],[updated_at]) VALUES (NULL,''superx'',''2012-02-03T17:32:29.796'',''2012-02-03T17:32:29.796'')
should be:
INSERT INTO [group] ([order],[created_at],[updated_at]) VALUES (''superx'',''2012-02-03T17:32:29.796'',''2012-02-03T17:32:29.796'')

Tested with SQL Server 2008 and ruby 1.9.2


+1 for this. Tested importing 158k csv lines into a table without problem.

Tested in SQLServer 2008. Doesn't work with SQLServer 2005.


Hey @ouranos - thanks for contributing this!

Did anyone (@davidsf maybe?) added support for MERGE command so we get something similar to ON DUPLICATE KEY UPDATE with MySQL?

That's something I'm currently discussing with @zdennis by email - let me know if you did experiment with this!


Hi @thbar,
I haven't looked at the MERGE command but I'll be happy to have a look at it. Have you started anything with it?


@ouranos only browsing the docs so far, but I'll definitely work on that. Maybe we can work together on this?

I've been reaching out to Zach by email and he would like to see SQLServer supported.

Here's my current use case by the way (but then I'll open a dedicated issue later so we can discuss all this):

  • create an empty copy of the target table (ie: tmp_entries) without any data
  • use your existing work to batch insert the records
  • do some verification work (like: select distinct where values) on the temp table
  • then once it's done, trigger a merge by a specific business key to merge tmp_entries into entries, updating when it makes sense

So in this case this would require to allow two steps, rather than one step like in the current mysql "on duplicate key update" API:

Book.import [book], :on_duplicate_key_update => [:isbn]

=> what would be your use case, if you had to use MERGE?


@thbar : sorry for the late reply, was quite busy with work lately.

I'd like to avoid creating a temp table if possible, I'm investing the possibilities but nothing came up so far.
Worse case, your scenario would work quite well.

Also since the MERGE statement is a lot more verbose than the SQL equivalent, the code might be quite different.

Edit: It may actually be possible using Table-Valued Parameters, I'll look into this when I get access to my SQL Server.


@ouranos no worries - same here :)

On the temp table: my guess is that most people would have the use for it, but in some cases it's handy to do some mass-verifications, without relying on activerecord validations. Things like: no record should have an empty string for this column etc.

But without it, the feature would still be useful.

I'll research a bit more on my side but just know that right now as I'm fairly busy shipping an ETL. I'm currently doing "update or insert" manually myself and it already works decently well, but this feature would give an interesting extra boost in speed.

Let's keep each other posted!


EDIT: I meant "would not have the use for it"!


See my edit, it may be possible with Table-Valued Parameters.

My main concern about temp tables would be the performance overhead, but that can be benchmarked once we get an implementation working :)


Here's a T-SQL statement replicating the MySQL example

Without temp table ! I'll try to start a new branch tomorrow and work on the implementation.

SET IDENTITY_INSERT books ON -- Allow ID insertion
-- Populate the table
INSERT INTO books (id, title, author_name) VALUES (10, 'Test Book', 'Foo'),(11, 'Other Test Book', 'Foo')
-- Book Template
    [id]             INT,
    [author_name]         NVARCHAR(255),
    [title]          NVARCHAR(255)
DECLARE @Books BookTemplate
-- Insert new records in the template
INSERT INTO @Books (id, title, author_name) VALUES (11, 'Updated Test Book', 'Bar'),(12, 'Another Test Book', 'Foo')
-- Do the merge
MERGE INTO books AS [Target] 
  USING @Books as Source
  ON [Target].[id] = [Source].[id]
    UPDATE SET [title] = [Source].[title]
    INSERT ( [id], [title], [author_name] )
    VALUES ( [Source].[id], [Source].[title], [Source].[author_name] ); 

Great start! Thanks for sharing. I'll have a closer look tonight as I'm working for a client today.

By the way, maybe we could use which supports SQLServer 2008 R2 to share what we have?


Great, I'll start another branch and publish it when I get a bit more done, since it's not directly related to this issue.
Maybe we can start another issue or page on the wiki to continue our discussion.

I think the only thing left on this one would be to find how to get rid of the DEFAULT or NULL are not allowed as explicit identity values. errors so all tests pass and we can then, hopefully, get it merged.


Another issue seems good, sure! I'll follow your repo and see if I can run some tests here too.


Has there been any movement on this since earlier this year? SQL Server support here would be immensely useful to our team, and we'd love to see it merged into the gem. Is there anything we can do to help it along?


+1 REALLY need this functionality - sqlserver import functionality would really be a lifesaver. Please pull!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 26, 2012
  1. @ouranos

    Add SQL Server adapter

    ouranos committed
Commits on Feb 3, 2012
  1. @ouranos

    Update tests for sqlserver

    ouranos committed
This page is out of date. Refresh to see the latest.
2  Gemfile
@@ -14,6 +14,8 @@ group :test do
gem "mysql2", "~> 0.2.4"
gem "pg", "~> 0.9"
gem "sqlite3-ruby", "~> 1.3.1"
+ gem "activerecord-sqlserver-adapter", "~>3.2.0"
+ gem "tiny_tds"
platforms :jruby do
5 Gemfile.lock
@@ -13,6 +13,8 @@ GEM
activerecord-jdbcmysql-adapter (1.1.1)
activerecord-jdbc-adapter (= 1.1.1)
jdbc-mysql (~> 5.1.0)
+ activerecord-sqlserver-adapter (3.2.0)
+ activerecord (~> 3.2.0)
activesupport (3.2.0)
i18n (~> 0.6)
multi_json (~> 1.0)
@@ -58,6 +60,7 @@ GEM
sqlite3 (1.3.5)
sqlite3-ruby (1.3.3)
sqlite3 (>= 1.3.3)
+ tiny_tds (0.5.1)
tzinfo (0.3.31)
@@ -67,6 +70,7 @@ PLATFORMS
activerecord (~> 3.0)
+ activerecord-sqlserver-adapter (~> 3.2.0)
delorean (~> 0.2.0)
factory_girl (~> 1.3.3)
@@ -79,3 +83,4 @@ DEPENDENCIES
ruby-debug-base (= 0.10.4)
sqlite3-ruby (~> 1.3.1)
+ tiny_tds
2  Rakefile
@@ -36,7 +36,7 @@ namespace :display do
task :default => ["display:notice"]
-ADAPTERS = %w(mysql mysql2 jdbcmysql postgresql sqlite3)
+ADAPTERS = %w(mysql mysql2 jdbcmysql postgresql sqlite3 sqlserver)
ADAPTERS.each do |adapter|
namespace :test do
desc "Runs #{adapter} database tests."
6 lib/activerecord-import/active_record/adapters/sqlserver_adapter.rb
@@ -0,0 +1,6 @@
+require "active_record/connection_adapters/sqlserver_adapter"
+require "activerecord-import/adapters/sqlserver_adapter"
+class ActiveRecord::ConnectionAdapters::SQLServerAdapter
+ include ActiveRecord::Import::SQLServerAdapter
15 lib/activerecord-import/adapters/sqlserver_adapter.rb
@@ -0,0 +1,15 @@
+module ActiveRecord::Import::SQLServerAdapter
+ include ActiveRecord::Import::ImportSupport
+ # There is a limit of 1000 rows on the insert method
+ # We need to process it in batches
+ def insert_many( sql, values, *args ) # :nodoc:
+ number_of_inserts = 0
+ while !(batch = values.shift(1000)).blank? do
+ # cloning sql here since the super method is modifying it
+ number_of_inserts += super( sql.clone, batch, args )
+ end
+ number_of_inserts
+ end
1  test/adapters/sqlserver.rb
@@ -0,0 +1 @@
+ENV["ARE_DB"] = "sqlserver"
Something went wrong with that request. Please try again.