-
Notifications
You must be signed in to change notification settings - Fork 562
Description
Basically, the issue I'm having is that calling Person.includes(:emails).limit(10).offset(10) is causing a LEFT OUTER JOIN rather than using Rails' usual method of a second IN query to grab related results.
I've created a small project (https://github.com/sphogan/offsettest) to demonstrate the issue (in case you want a concrete example).
Person.includes(:emails)
Person Load (17.8ms) EXEC sp_executesql N'SELECT [people].* FROM [people]'
Email Load (3.9ms) EXEC sp_executesql N'SELECT [emails].* FROM [emails] WHERE [emails].[person_id] IN (N''1'', N''2'', N''3'', N''4'', N''5'', N''6'', N''7'', N''8'', N''9'', N''10'', N''11'', N''12'', N''13'', N''14'', N''15'', N''16'', N''17'', N''18'', N''19'', N''20'', N''21'')'Perfect! It executes two queries as expected.
Person.limit(10).offset(10)
Person Load (2.7ms) EXEC sp_executesql N'SELECT TOP (10) [__rnt].* FROM ( SELECT ROW_NUMBER() OVER (ORDER BY [people].[id] ASC) AS [__rn], [people].* FROM [people] ) AS [__rnt] WHERE [__rnt].[__rn] > (10) ORDER BY [__rnt].[__rn] ASC'Awesome!
Person.limit(10).offset(10).includes(:emails)
Person Load (3.1ms) EXEC sp_executesql N'SELECT TOP (10) [__rnt].id FROM ( SELECT ROW_NUMBER() OVER (ORDER BY [people].[id] ASC) AS [__rn], [people].id FROM [people] LEFT OUTER JOIN [emails] ON [emails].[person_id] = [people].[id] GROUP BY [people].id ) AS [__rnt] WHERE [__rnt].[__rn] > (10) ORDER BY [__rnt].[__rn] ASC'
SQL (5.6ms) EXEC sp_executesql N'SELECT [people].[id] AS t0_r0, [people].[name] AS t0_r1, [people].[created_at] AS t0_r2, [people].[updated_at] AS t0_r3, [emails].[id] AS t1_r0, [emails].[person_id] AS t1_r1, [emails].[address] AS t1_r2, [emails].[created_at] AS t1_r3, [emails].[updated_at] AS t1_r4 FROM [people] LEFT OUTER JOIN [emails] ON [emails].[person_id] = [people].[id] WHERE [people].[id] IN (11, 12, 13, 14, 15, 16, 17, 18, 19, 20)'This is where the problem is. The adapter works as I assume it would work for Person.includes(:emails) as well as for Person.limit(10).offset(10). However, when the two are combined, it decides to do a LEFT OUTER JOIN to get the emails and I'm not sure why. It isn't using fewer statements since it's still using two SQL statements to load the people and their email addresses, they're just more complex SQL statements. The interesting thing is that calling .to_sql shows me the behavior I expect:
Person.limit(10).offset(10).includes(:emails).to_sql
=> "SELECT TOP (10) [__rnt].* FROM ( SELECT ROW_NUMBER() OVER (ORDER BY [people].[id] ASC) AS [__rn], [people].* FROM [people] ) AS [__rnt] WHERE [__rnt].[__rn] > (10) ORDER BY [__rnt].[__rn] ASC"The statement provided by .to_sql runs perfectly against my database.
Just as a comparison (to make sure I wasn't off-base with my expected behavior), I tested it on a MySQL database:
Person.limit(10).offset(10).includes(:emails)
Person Load (0.2ms) SELECT `people`.* FROM `people` LIMIT 10 OFFSET 10
Email Load (26.8ms) SELECT `emails`.* FROM `emails` WHERE `emails`.`person_id` IN (11, 12, 13, 14, 15, 16, 17, 18, 19, 20)Looking at visit_Arel_Nodes_SelectStatementWithOutOffset, the statement it returns is:
SELECT TOP (10) [__rnt].* FROM ( SELECT ROW_NUMBER() OVER (ORDER BY [people].[id] ASC) AS [__rn], [people].* FROM [people] ) AS [__rnt] WHERE [__rnt].[__rn] > (10) ORDER BY [__rnt].[__rn] ASCSo, at that point, it's the same SQL that .to_sql is showing and that I'm expecting. I'm not sure if this is getting altered by something in Arel::TreeManager or I'm just missing something.