Select * from View takes 4 minutesHow do I force SQL Server to use my spatial index through a view?Improving performance of a viewI've created a view for a report, it now takes 30 minutes to run - Any ideas?Optimizing query. FOR XML PATH or some alternatives?Row estimates always too lowBad performance using “NOT IN”Query Locks Table And Takes Long Time to RunWhy is this faster and is it safe to use? (WHERE first letter is in the alphabet)SQL Server statement takes forever while running instantly in OracleSQL Server - Identical query on near identical database takes 20 seconds on sql2012 but does not complete after 50+ hours on sql2014

Is there a way to auto-resolve a fight and account for resource consumption?

Why is double encryption that's equivalent to single encryption no better than single encryption?

Antonym for “boilerplate” or “cookie-cutter”

Where to search for PhD level jobs in OR?

What examples are there of unrelated physical quantities measured in the same units?

Is Bcrypt a hashing algorithm or is my study material wrong?

Does animal blood, esp. human, really have similar salinity as ocean water, and does that prove anything about evolution?

How to raise an event in Sitecore 9+

Did the Allies reverse the threads on secret microfilm-hiding buttons to thwart the Germans?

responsibility for arrangement of elements - Frontend or Backend?

How can I sell my shares in a privately-owned company I used to be employed by?

Why is more music written in sharp keys than flat keys?

Is there a high level reason why the inverse square law of gravitation yields periodic orbits without precession?

What do I get by paying more for a bicycle?

What is the difference between democracy and ochlocracy?

Letters associated with prime numbers

Shrinkage priors

How can an immortal member of the nobility be prevented from taking the throne?

Do European politicians typically put their pronouns on their social media pages?

What do you call this when cats hunch their backs and their fur stands on end?

Use of expression "statistically significantly positive"

Why do the new Star Trek series have so few episodes in each season?

What does exhaust smell on oil and transmission dipstick mean?

How much caffeine would there be if I reuse tea leaves in a second brewing?



Select * from View takes 4 minutes


How do I force SQL Server to use my spatial index through a view?Improving performance of a viewI've created a view for a report, it now takes 30 minutes to run - Any ideas?Optimizing query. FOR XML PATH or some alternatives?Row estimates always too lowBad performance using “NOT IN”Query Locks Table And Takes Long Time to RunWhy is this faster and is it safe to use? (WHERE first letter is in the alphabet)SQL Server statement takes forever while running instantly in OracleSQL Server - Identical query on near identical database takes 20 seconds on sql2012 but does not complete after 50+ hours on sql2014






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty
margin-bottom:0;









11

















I am running in to an issue where when I run a query against a view it takes 4+ minutes. However, when I run the guts of the query it finishes in like 1 second.



The only thing I am not sure about is the tables being joined are both temporal tables.



Ad hoc query plan:
https://www.brentozar.com/pastetheplan/?id=BykohB2p4



View query plan:
https://www.brentozar.com/pastetheplan/?id=SkIfTHh6E



Any suggestions on where to try and figure this out?



View code:



ALTER VIEW [dbo].[vwDealHistoryPITA]
AS
SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID, cm.LastUpdateDate) AS Deal_HistoryID,
cm.CodeMasterID,
cm.ProjectName,
cm.[Status],
d.CompanyID,
d.DealTypeMasterID,
cm.[Description],
d.PassiveInd,
d.ApproxTPGOwnership,
d.NumberBoardSeats,
d.FollowonInvestmentInd,
d.SocialImpactInd,
d.EquityInd,
d.DebtInd,
d.RealEstateInd,
d.TargetPctgReturn,
d.ApproxTotalDealSize,
cm.CurrencyCode,
d.ConflictCheck,
cm.CreatedDate,
cm.CreatedBy,
cm.LastUpdateDate,
cm.LastUpdateBy,
d.ExpensesExceedThresholdDate,
d.CurrentTPGCheckSize,
d.PreferredEquityInd,
d.ConvertibleDebtInd,
d.OtherRealAssetsInd,
d.InitialTPGCheckSize,
d.DirectLendingInd,
cm.NameApproved,
cm.FolderID,
cm.CodaProcessedDateTime,
cm.DeadDate,
d.SectorMasterID,
d.DTODataCompleteDate,
cm.ValidFrom AS CodeMasterValidFrom,
cm.ValidTo AS CodeMasterValidTo,
d.validFrom AS DealValidFrom,
d.validTo AS DealValidTo
FROM dbo.CodeMaster FOR SYSTEM_TIME ALL cm
INNER JOIN dbo.Deal FOR SYSTEM_TIME ALL d ON cm.CodeMasterID = d.CodeMasterID;
GO


Added the Partition by and get similar results to the ad hoc query.










share|improve this question


































    11

















    I am running in to an issue where when I run a query against a view it takes 4+ minutes. However, when I run the guts of the query it finishes in like 1 second.



    The only thing I am not sure about is the tables being joined are both temporal tables.



    Ad hoc query plan:
    https://www.brentozar.com/pastetheplan/?id=BykohB2p4



    View query plan:
    https://www.brentozar.com/pastetheplan/?id=SkIfTHh6E



    Any suggestions on where to try and figure this out?



    View code:



    ALTER VIEW [dbo].[vwDealHistoryPITA]
    AS
    SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID, cm.LastUpdateDate) AS Deal_HistoryID,
    cm.CodeMasterID,
    cm.ProjectName,
    cm.[Status],
    d.CompanyID,
    d.DealTypeMasterID,
    cm.[Description],
    d.PassiveInd,
    d.ApproxTPGOwnership,
    d.NumberBoardSeats,
    d.FollowonInvestmentInd,
    d.SocialImpactInd,
    d.EquityInd,
    d.DebtInd,
    d.RealEstateInd,
    d.TargetPctgReturn,
    d.ApproxTotalDealSize,
    cm.CurrencyCode,
    d.ConflictCheck,
    cm.CreatedDate,
    cm.CreatedBy,
    cm.LastUpdateDate,
    cm.LastUpdateBy,
    d.ExpensesExceedThresholdDate,
    d.CurrentTPGCheckSize,
    d.PreferredEquityInd,
    d.ConvertibleDebtInd,
    d.OtherRealAssetsInd,
    d.InitialTPGCheckSize,
    d.DirectLendingInd,
    cm.NameApproved,
    cm.FolderID,
    cm.CodaProcessedDateTime,
    cm.DeadDate,
    d.SectorMasterID,
    d.DTODataCompleteDate,
    cm.ValidFrom AS CodeMasterValidFrom,
    cm.ValidTo AS CodeMasterValidTo,
    d.validFrom AS DealValidFrom,
    d.validTo AS DealValidTo
    FROM dbo.CodeMaster FOR SYSTEM_TIME ALL cm
    INNER JOIN dbo.Deal FOR SYSTEM_TIME ALL d ON cm.CodeMasterID = d.CodeMasterID;
    GO


    Added the Partition by and get similar results to the ad hoc query.










    share|improve this question






























      11












      11








      11


      4






      I am running in to an issue where when I run a query against a view it takes 4+ minutes. However, when I run the guts of the query it finishes in like 1 second.



      The only thing I am not sure about is the tables being joined are both temporal tables.



      Ad hoc query plan:
      https://www.brentozar.com/pastetheplan/?id=BykohB2p4



      View query plan:
      https://www.brentozar.com/pastetheplan/?id=SkIfTHh6E



      Any suggestions on where to try and figure this out?



      View code:



      ALTER VIEW [dbo].[vwDealHistoryPITA]
      AS
      SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID, cm.LastUpdateDate) AS Deal_HistoryID,
      cm.CodeMasterID,
      cm.ProjectName,
      cm.[Status],
      d.CompanyID,
      d.DealTypeMasterID,
      cm.[Description],
      d.PassiveInd,
      d.ApproxTPGOwnership,
      d.NumberBoardSeats,
      d.FollowonInvestmentInd,
      d.SocialImpactInd,
      d.EquityInd,
      d.DebtInd,
      d.RealEstateInd,
      d.TargetPctgReturn,
      d.ApproxTotalDealSize,
      cm.CurrencyCode,
      d.ConflictCheck,
      cm.CreatedDate,
      cm.CreatedBy,
      cm.LastUpdateDate,
      cm.LastUpdateBy,
      d.ExpensesExceedThresholdDate,
      d.CurrentTPGCheckSize,
      d.PreferredEquityInd,
      d.ConvertibleDebtInd,
      d.OtherRealAssetsInd,
      d.InitialTPGCheckSize,
      d.DirectLendingInd,
      cm.NameApproved,
      cm.FolderID,
      cm.CodaProcessedDateTime,
      cm.DeadDate,
      d.SectorMasterID,
      d.DTODataCompleteDate,
      cm.ValidFrom AS CodeMasterValidFrom,
      cm.ValidTo AS CodeMasterValidTo,
      d.validFrom AS DealValidFrom,
      d.validTo AS DealValidTo
      FROM dbo.CodeMaster FOR SYSTEM_TIME ALL cm
      INNER JOIN dbo.Deal FOR SYSTEM_TIME ALL d ON cm.CodeMasterID = d.CodeMasterID;
      GO


      Added the Partition by and get similar results to the ad hoc query.










      share|improve this question

















      I am running in to an issue where when I run a query against a view it takes 4+ minutes. However, when I run the guts of the query it finishes in like 1 second.



      The only thing I am not sure about is the tables being joined are both temporal tables.



      Ad hoc query plan:
      https://www.brentozar.com/pastetheplan/?id=BykohB2p4



      View query plan:
      https://www.brentozar.com/pastetheplan/?id=SkIfTHh6E



      Any suggestions on where to try and figure this out?



      View code:



      ALTER VIEW [dbo].[vwDealHistoryPITA]
      AS
      SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID, cm.LastUpdateDate) AS Deal_HistoryID,
      cm.CodeMasterID,
      cm.ProjectName,
      cm.[Status],
      d.CompanyID,
      d.DealTypeMasterID,
      cm.[Description],
      d.PassiveInd,
      d.ApproxTPGOwnership,
      d.NumberBoardSeats,
      d.FollowonInvestmentInd,
      d.SocialImpactInd,
      d.EquityInd,
      d.DebtInd,
      d.RealEstateInd,
      d.TargetPctgReturn,
      d.ApproxTotalDealSize,
      cm.CurrencyCode,
      d.ConflictCheck,
      cm.CreatedDate,
      cm.CreatedBy,
      cm.LastUpdateDate,
      cm.LastUpdateBy,
      d.ExpensesExceedThresholdDate,
      d.CurrentTPGCheckSize,
      d.PreferredEquityInd,
      d.ConvertibleDebtInd,
      d.OtherRealAssetsInd,
      d.InitialTPGCheckSize,
      d.DirectLendingInd,
      cm.NameApproved,
      cm.FolderID,
      cm.CodaProcessedDateTime,
      cm.DeadDate,
      d.SectorMasterID,
      d.DTODataCompleteDate,
      cm.ValidFrom AS CodeMasterValidFrom,
      cm.ValidTo AS CodeMasterValidTo,
      d.validFrom AS DealValidFrom,
      d.validTo AS DealValidTo
      FROM dbo.CodeMaster FOR SYSTEM_TIME ALL cm
      INNER JOIN dbo.Deal FOR SYSTEM_TIME ALL d ON cm.CodeMasterID = d.CodeMasterID;
      GO


      Added the Partition by and get similar results to the ad hoc query.







      sql-server






      share|improve this question
















      share|improve this question













      share|improve this question




      share|improve this question








      edited May 29 at 20:09







      user761786

















      asked May 29 at 18:19









      user761786user761786

      3381 gold badge3 silver badges11 bronze badges




      3381 gold badge3 silver badges11 bronze badges























          1 Answer
          1






          active

          oldest

          votes


















          18


















          The main performance differences



          The main differences here are that the better performing query is pushing down the seek predicate on CodeMasterID on all 4 tables (2 temporal tables (actual & history)) where the select on the view appears to not do that until the end (filter operator).



          TL DR;



          The issue is due to to parameters not pushing down to window functions in certain cases such as views. The easiest solution is adding OPTION(RECOMPILE) to the view call as to make the optimizer 'see' the params at runtime if that is a possibility. If it is too expensive to recompile the execution plan for each query call, using an inline table valued function that expects a parameter could be a solution. There is an excellent Blogpost by Paul White on this. For a more detailed way on finding and resolving your particular issue, keep on reading.




          The better performing query



          Codemaster table



          enter image description here



          enter image description here



          Deal table



          enter image description here



          enter image description here



          I love the smell of seek predicates in the morning




          The big bad query



          Codemaster table



          enter image description here



          enter image description here



          This is a predicate only zone



          The Deal table



          enter image description here



          But the optimizer did not read 'The art of the deal™"



          enter image description here



          ...and does not learn from the past



          Until all that data reaches the filter operator



          enter image description here




          So, what gives?



          The main problem here is the optimizer not 'seeing' the parameters at runtime due to the window functions in the view and not being able to use the SelOnSeqPrj (select on sequence project, further down in this post for reference).



          I was able to replicate the same results with a test sample and using SP_EXECUTESQL to parameterize the call to the view. See addendum for the DDL / DML



          executing a query against a test view with a window function and an INNER JOIN



          SET STATISTICS IO, TIME ON;
          EXEC SP_EXECUTESQL
          N'SELECT * FROM dbo.Bad
          Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155;


          Resulting in about 4.5s of cpu time and 3.2s elapsed time



           SQL Server Execution Times:
          CPU time = 4595 ms, elapsed time = 3209 ms.


          When we add the sweet embrace of OPTION(RECOMPILE)



          SET STATISTICS IO, TIME ON;
          EXEC SP_EXECUTESQL
          N'SELECT * FROM dbo.Bad
          Where CodeMasterID = @P1 OPTION(RECOMPILE)',N'@P1 INT',@P1 = 37155;


          It is all good.



           SQL Server Execution Times:
          CPU time = 16 ms, elapsed time = 98 ms.


          Why



          This all again supports the point of not being able to apply the @P1 predicate to the tables because of the window function & parameterization resulting in the filter operator



          enter image description hereenter image description here



          Not only an issue for temporal tables



          See addendum 2



          Even when not using temporal tables, this happens:
          enter image description here



          The same result is seen when writing the query like this:



          DECLARE @P1 int = 37155
          SELECT * FROM dbo.Bad2
          Where CodeMasterID = @P1;


          Again, the optimizer is not pushing down the predicate before applying the window function.



          When omitting the ROW_NUMBER()



          CREATE VIEW dbo.Bad3
          as
          SELECT
          cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
          FROM dbo.CodeMaster2 cm
          INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID;


          All is well



          SET STATISTICS IO, TIME ON;
          EXEC SP_EXECUTESQL
          N'SELECT * FROM dbo.Bad3
          Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155


          SQL Server Execution Times:
          CPU time = 0 ms, elapsed time = 33 ms.


          so where does all that leave us?



          The ROW_NUMBER() is calculated before the filter is applied on the bad queries.



          And all this leads us to this blogpost from 2013 by Paul White
          on window functions and views.



          One of the important parts for our example is this statement:




          Unfortunately, the SelOnSeqPrj simplification rule only works when the
          predicate performs a comparison with a constant. For that reason, the
          following query produces the sub-optimal plan on SQL Server 2008 and
          later:




          DECLARE @ProductID INT = 878;

          SELECT
          mrt.ProductID,
          mrt.TransactionID,
          mrt.ReferenceOrderID,
          mrt.TransactionDate,
          mrt.Quantity
          FROM dbo.MostRecentTransactionsPerProduct AS mrt
          WHERE
          mrt.ProductID = @ProductID;


          enter image description here



          This part corresponds to what we have seen when declaring the parameter ourselves / using SP_EXECUTESQL on the view.




          The actual solutions



          1: OPTION(RECOMPILE)



          We know that OPTION(RECOMPILE) to 'see' the value at runtime is a possibility. When recompiling the execution plan for each query call is too expensive, there are other solutions.



          2: Inline table valued function with a parameter



          CREATE FUNCTION dbo.BlaBla
          (
          @P1 INT
          )
          RETURNS TABLE
          WITH SCHEMABINDING AS
          RETURN
          (
          SELECT
          ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID) AS Deal_HistoryID,
          cm.CodeMasterID,CM.ManagerID,
          cm.ParentDeptID,d.DealID,
          d.CodeMasterID as dealcodemaster,
          d.EvenMoreBlaID
          FROM dbo.CodeMaster2 cm
          INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID
          Where cm.CodeMasterID = @P1
          )
          EXEC SP_EXECUTESQL
          N'SELECT * FROM dbo.BlaBLa(@P1)',N'@P1 INT',@P1 = 37155


          Resulting in the expected seek predicates



           SQL Server Execution Times:
          CPU time = 0 ms, elapsed time = 0 ms.


          With about 9 logical reads on my test



          3: Writing the query without the use of a view.



          The other 'solution' could be writing the query entirely without the use of a view.



          4: Not keeping the ROW_NUMBER() function in the view, instead specifying it in the call to the view.



          An example of this would be:



          CREATE VIEW dbo.Bad2
          as
          SELECT
          cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
          FROM dbo.CodeMaster2 cm
          INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID;

          GO
          SET STATISTICS IO, TIME ON;
          EXEC SP_EXECUTESQL
          N'SELECT ROW_NUMBER() OVER (PARTITION BY CodeMasterID ORDER BY CodeMasterID) AS Deal_HistoryID,* FROM dbo.Bad2
          Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155;


          There should be other creative ways around this issue, the important part is knowing what causes it.




          Addendum #1



          CREATE TABLE dbo.Codemaster 
          (
          CodeMasterID int NOT NULL PRIMARY KEY CLUSTERED
          , ManagerID INT NULL
          , ParentDeptID int NULL
          , SysStartTime datetime2 GENERATED ALWAYS AS ROW START NOT NULL
          , SysEndTime datetime2 GENERATED ALWAYS AS ROW END NOT NULL
          , PERIOD FOR SYSTEM_TIME (SysStartTime,SysEndTime)
          )
          WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Codemaster_History))
          ;

          CREATE TABLE dbo.Deal
          (
          DealID int NOT NULL PRIMARY KEY CLUSTERED
          , CodeMasterID INT NULL
          , EvenMoreBlaID int NULL
          , SysStartTime datetime2 GENERATED ALWAYS AS ROW START NOT NULL
          , SysEndTime datetime2 GENERATED ALWAYS AS ROW END NOT NULL
          , PERIOD FOR SYSTEM_TIME (SysStartTime,SysEndTime)
          )
          WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Deal_History))
          ;

          INSERT INTO dbo.Codemaster(CodeMasterID,ManagerID,ParentDeptID)
          SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
          ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
          ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
          FROM MASTER..spt_values as spt1
          CROSS JOIN MASTER..spt_values as spt2;


          INSERT INTO dbo.Deal(DealID,CodeMasterID,EvenMoreBlaID)
          SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
          ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
          ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
          FROM MASTER..spt_values as spt1
          CROSS JOIN MASTER..spt_values as spt2;

          CREATE INDEX IX_CodeMasterID
          ON dbo.Deal(CodeMasterId);
          CREATE INDEX IX_CodeMasterID
          ON dbo.Deal_History(CodeMasterId);

          CREATE INDEX IX_CodeMasterID
          ON dbo.Codemaster(CodeMasterId);
          CREATE INDEX IX_CodeMasterID
          ON dbo.Codemaster_History(CodeMasterId);


          SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID, cm.SysStartTime) AS Deal_HistoryID,
          cm.*, d.*
          FROM dbo.CodeMaster FOR SYSTEM_TIME ALL cm
          INNER JOIN dbo.Deal FOR SYSTEM_TIME ALL d ON cm.CodeMasterID = d.CodeMasterID
          Where cm.CodeMasterID = 37155;

          -- Guud
          GO
          CREATE VIEW dbo.Bad
          as
          SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID, cm.SysStartTime) AS Deal_HistoryID,
          cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
          FROM dbo.CodeMaster FOR SYSTEM_TIME ALL cm
          INNER JOIN dbo.Deal FOR SYSTEM_TIME ALL d ON cm.CodeMasterID = d.CodeMasterID

          GO
          EXEC SP_EXECUTESQL
          N'SELECT * FROM dbo.Bad
          Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155

          -- Very bad shame on you



          Addendum #2



          CREATE TABLE dbo.Codemaster2
          (
          CodeMasterID int NOT NULL PRIMARY KEY CLUSTERED
          , ManagerID INT NULL
          , ParentDeptID int NULL

          );

          CREATE TABLE dbo.Deal2
          (
          DealID int NOT NULL PRIMARY KEY CLUSTERED
          , CodeMasterID INT NULL
          , EvenMoreBlaID int NULL
          );

          INSERT INTO dbo.Codemaster2(CodeMasterID,ManagerID,ParentDeptID)
          SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
          ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
          ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
          FROM MASTER..spt_values as spt1
          CROSS JOIN MASTER..spt_values as spt2;


          INSERT INTO dbo.Deal2(DealID,CodeMasterID,EvenMoreBlaID)
          SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
          ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
          ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
          FROM MASTER..spt_values as spt1
          CROSS JOIN MASTER..spt_values as spt2;

          CREATE INDEX IX_CodeMasterID
          ON dbo.Deal2(CodeMasterId);
          CREATE INDEX IX_CodeMasterID
          ON dbo.Codemaster2(CodeMasterId);


          SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterId) AS Deal_HistoryID,
          cm.*, d.*
          FROM dbo.CodeMaster2 cm
          INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID
          Where cm.CodeMasterID = 37155;

          -- Guud
          GO
          CREATE VIEW dbo.Bad2
          as
          SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID) AS Deal_HistoryID,
          cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
          FROM dbo.CodeMaster2 cm
          INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID

          GO
          SET STATISTICS IO, TIME ON;
          EXEC SP_EXECUTESQL
          N'SELECT * FROM dbo.Bad2
          Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155





          share|improve this answer





























            Your Answer








            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "182"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader:
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/4.0/"u003ecc by-sa 4.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            ,
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );














            draft saved

            draft discarded
















            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fdba.stackexchange.com%2fquestions%2f239370%2fselect-from-view-takes-4-minutes%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown


























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            18


















            The main performance differences



            The main differences here are that the better performing query is pushing down the seek predicate on CodeMasterID on all 4 tables (2 temporal tables (actual & history)) where the select on the view appears to not do that until the end (filter operator).



            TL DR;



            The issue is due to to parameters not pushing down to window functions in certain cases such as views. The easiest solution is adding OPTION(RECOMPILE) to the view call as to make the optimizer 'see' the params at runtime if that is a possibility. If it is too expensive to recompile the execution plan for each query call, using an inline table valued function that expects a parameter could be a solution. There is an excellent Blogpost by Paul White on this. For a more detailed way on finding and resolving your particular issue, keep on reading.




            The better performing query



            Codemaster table



            enter image description here



            enter image description here



            Deal table



            enter image description here



            enter image description here



            I love the smell of seek predicates in the morning




            The big bad query



            Codemaster table



            enter image description here



            enter image description here



            This is a predicate only zone



            The Deal table



            enter image description here



            But the optimizer did not read 'The art of the deal™"



            enter image description here



            ...and does not learn from the past



            Until all that data reaches the filter operator



            enter image description here




            So, what gives?



            The main problem here is the optimizer not 'seeing' the parameters at runtime due to the window functions in the view and not being able to use the SelOnSeqPrj (select on sequence project, further down in this post for reference).



            I was able to replicate the same results with a test sample and using SP_EXECUTESQL to parameterize the call to the view. See addendum for the DDL / DML



            executing a query against a test view with a window function and an INNER JOIN



            SET STATISTICS IO, TIME ON;
            EXEC SP_EXECUTESQL
            N'SELECT * FROM dbo.Bad
            Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155;


            Resulting in about 4.5s of cpu time and 3.2s elapsed time



             SQL Server Execution Times:
            CPU time = 4595 ms, elapsed time = 3209 ms.


            When we add the sweet embrace of OPTION(RECOMPILE)



            SET STATISTICS IO, TIME ON;
            EXEC SP_EXECUTESQL
            N'SELECT * FROM dbo.Bad
            Where CodeMasterID = @P1 OPTION(RECOMPILE)',N'@P1 INT',@P1 = 37155;


            It is all good.



             SQL Server Execution Times:
            CPU time = 16 ms, elapsed time = 98 ms.


            Why



            This all again supports the point of not being able to apply the @P1 predicate to the tables because of the window function & parameterization resulting in the filter operator



            enter image description hereenter image description here



            Not only an issue for temporal tables



            See addendum 2



            Even when not using temporal tables, this happens:
            enter image description here



            The same result is seen when writing the query like this:



            DECLARE @P1 int = 37155
            SELECT * FROM dbo.Bad2
            Where CodeMasterID = @P1;


            Again, the optimizer is not pushing down the predicate before applying the window function.



            When omitting the ROW_NUMBER()



            CREATE VIEW dbo.Bad3
            as
            SELECT
            cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
            FROM dbo.CodeMaster2 cm
            INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID;


            All is well



            SET STATISTICS IO, TIME ON;
            EXEC SP_EXECUTESQL
            N'SELECT * FROM dbo.Bad3
            Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155


            SQL Server Execution Times:
            CPU time = 0 ms, elapsed time = 33 ms.


            so where does all that leave us?



            The ROW_NUMBER() is calculated before the filter is applied on the bad queries.



            And all this leads us to this blogpost from 2013 by Paul White
            on window functions and views.



            One of the important parts for our example is this statement:




            Unfortunately, the SelOnSeqPrj simplification rule only works when the
            predicate performs a comparison with a constant. For that reason, the
            following query produces the sub-optimal plan on SQL Server 2008 and
            later:




            DECLARE @ProductID INT = 878;

            SELECT
            mrt.ProductID,
            mrt.TransactionID,
            mrt.ReferenceOrderID,
            mrt.TransactionDate,
            mrt.Quantity
            FROM dbo.MostRecentTransactionsPerProduct AS mrt
            WHERE
            mrt.ProductID = @ProductID;


            enter image description here



            This part corresponds to what we have seen when declaring the parameter ourselves / using SP_EXECUTESQL on the view.




            The actual solutions



            1: OPTION(RECOMPILE)



            We know that OPTION(RECOMPILE) to 'see' the value at runtime is a possibility. When recompiling the execution plan for each query call is too expensive, there are other solutions.



            2: Inline table valued function with a parameter



            CREATE FUNCTION dbo.BlaBla
            (
            @P1 INT
            )
            RETURNS TABLE
            WITH SCHEMABINDING AS
            RETURN
            (
            SELECT
            ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID) AS Deal_HistoryID,
            cm.CodeMasterID,CM.ManagerID,
            cm.ParentDeptID,d.DealID,
            d.CodeMasterID as dealcodemaster,
            d.EvenMoreBlaID
            FROM dbo.CodeMaster2 cm
            INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID
            Where cm.CodeMasterID = @P1
            )
            EXEC SP_EXECUTESQL
            N'SELECT * FROM dbo.BlaBLa(@P1)',N'@P1 INT',@P1 = 37155


            Resulting in the expected seek predicates



             SQL Server Execution Times:
            CPU time = 0 ms, elapsed time = 0 ms.


            With about 9 logical reads on my test



            3: Writing the query without the use of a view.



            The other 'solution' could be writing the query entirely without the use of a view.



            4: Not keeping the ROW_NUMBER() function in the view, instead specifying it in the call to the view.



            An example of this would be:



            CREATE VIEW dbo.Bad2
            as
            SELECT
            cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
            FROM dbo.CodeMaster2 cm
            INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID;

            GO
            SET STATISTICS IO, TIME ON;
            EXEC SP_EXECUTESQL
            N'SELECT ROW_NUMBER() OVER (PARTITION BY CodeMasterID ORDER BY CodeMasterID) AS Deal_HistoryID,* FROM dbo.Bad2
            Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155;


            There should be other creative ways around this issue, the important part is knowing what causes it.




            Addendum #1



            CREATE TABLE dbo.Codemaster 
            (
            CodeMasterID int NOT NULL PRIMARY KEY CLUSTERED
            , ManagerID INT NULL
            , ParentDeptID int NULL
            , SysStartTime datetime2 GENERATED ALWAYS AS ROW START NOT NULL
            , SysEndTime datetime2 GENERATED ALWAYS AS ROW END NOT NULL
            , PERIOD FOR SYSTEM_TIME (SysStartTime,SysEndTime)
            )
            WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Codemaster_History))
            ;

            CREATE TABLE dbo.Deal
            (
            DealID int NOT NULL PRIMARY KEY CLUSTERED
            , CodeMasterID INT NULL
            , EvenMoreBlaID int NULL
            , SysStartTime datetime2 GENERATED ALWAYS AS ROW START NOT NULL
            , SysEndTime datetime2 GENERATED ALWAYS AS ROW END NOT NULL
            , PERIOD FOR SYSTEM_TIME (SysStartTime,SysEndTime)
            )
            WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Deal_History))
            ;

            INSERT INTO dbo.Codemaster(CodeMasterID,ManagerID,ParentDeptID)
            SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
            ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
            ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
            FROM MASTER..spt_values as spt1
            CROSS JOIN MASTER..spt_values as spt2;


            INSERT INTO dbo.Deal(DealID,CodeMasterID,EvenMoreBlaID)
            SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
            ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
            ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
            FROM MASTER..spt_values as spt1
            CROSS JOIN MASTER..spt_values as spt2;

            CREATE INDEX IX_CodeMasterID
            ON dbo.Deal(CodeMasterId);
            CREATE INDEX IX_CodeMasterID
            ON dbo.Deal_History(CodeMasterId);

            CREATE INDEX IX_CodeMasterID
            ON dbo.Codemaster(CodeMasterId);
            CREATE INDEX IX_CodeMasterID
            ON dbo.Codemaster_History(CodeMasterId);


            SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID, cm.SysStartTime) AS Deal_HistoryID,
            cm.*, d.*
            FROM dbo.CodeMaster FOR SYSTEM_TIME ALL cm
            INNER JOIN dbo.Deal FOR SYSTEM_TIME ALL d ON cm.CodeMasterID = d.CodeMasterID
            Where cm.CodeMasterID = 37155;

            -- Guud
            GO
            CREATE VIEW dbo.Bad
            as
            SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID, cm.SysStartTime) AS Deal_HistoryID,
            cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
            FROM dbo.CodeMaster FOR SYSTEM_TIME ALL cm
            INNER JOIN dbo.Deal FOR SYSTEM_TIME ALL d ON cm.CodeMasterID = d.CodeMasterID

            GO
            EXEC SP_EXECUTESQL
            N'SELECT * FROM dbo.Bad
            Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155

            -- Very bad shame on you



            Addendum #2



            CREATE TABLE dbo.Codemaster2
            (
            CodeMasterID int NOT NULL PRIMARY KEY CLUSTERED
            , ManagerID INT NULL
            , ParentDeptID int NULL

            );

            CREATE TABLE dbo.Deal2
            (
            DealID int NOT NULL PRIMARY KEY CLUSTERED
            , CodeMasterID INT NULL
            , EvenMoreBlaID int NULL
            );

            INSERT INTO dbo.Codemaster2(CodeMasterID,ManagerID,ParentDeptID)
            SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
            ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
            ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
            FROM MASTER..spt_values as spt1
            CROSS JOIN MASTER..spt_values as spt2;


            INSERT INTO dbo.Deal2(DealID,CodeMasterID,EvenMoreBlaID)
            SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
            ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
            ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
            FROM MASTER..spt_values as spt1
            CROSS JOIN MASTER..spt_values as spt2;

            CREATE INDEX IX_CodeMasterID
            ON dbo.Deal2(CodeMasterId);
            CREATE INDEX IX_CodeMasterID
            ON dbo.Codemaster2(CodeMasterId);


            SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterId) AS Deal_HistoryID,
            cm.*, d.*
            FROM dbo.CodeMaster2 cm
            INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID
            Where cm.CodeMasterID = 37155;

            -- Guud
            GO
            CREATE VIEW dbo.Bad2
            as
            SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID) AS Deal_HistoryID,
            cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
            FROM dbo.CodeMaster2 cm
            INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID

            GO
            SET STATISTICS IO, TIME ON;
            EXEC SP_EXECUTESQL
            N'SELECT * FROM dbo.Bad2
            Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155





            share|improve this answer
































              18


















              The main performance differences



              The main differences here are that the better performing query is pushing down the seek predicate on CodeMasterID on all 4 tables (2 temporal tables (actual & history)) where the select on the view appears to not do that until the end (filter operator).



              TL DR;



              The issue is due to to parameters not pushing down to window functions in certain cases such as views. The easiest solution is adding OPTION(RECOMPILE) to the view call as to make the optimizer 'see' the params at runtime if that is a possibility. If it is too expensive to recompile the execution plan for each query call, using an inline table valued function that expects a parameter could be a solution. There is an excellent Blogpost by Paul White on this. For a more detailed way on finding and resolving your particular issue, keep on reading.




              The better performing query



              Codemaster table



              enter image description here



              enter image description here



              Deal table



              enter image description here



              enter image description here



              I love the smell of seek predicates in the morning




              The big bad query



              Codemaster table



              enter image description here



              enter image description here



              This is a predicate only zone



              The Deal table



              enter image description here



              But the optimizer did not read 'The art of the deal™"



              enter image description here



              ...and does not learn from the past



              Until all that data reaches the filter operator



              enter image description here




              So, what gives?



              The main problem here is the optimizer not 'seeing' the parameters at runtime due to the window functions in the view and not being able to use the SelOnSeqPrj (select on sequence project, further down in this post for reference).



              I was able to replicate the same results with a test sample and using SP_EXECUTESQL to parameterize the call to the view. See addendum for the DDL / DML



              executing a query against a test view with a window function and an INNER JOIN



              SET STATISTICS IO, TIME ON;
              EXEC SP_EXECUTESQL
              N'SELECT * FROM dbo.Bad
              Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155;


              Resulting in about 4.5s of cpu time and 3.2s elapsed time



               SQL Server Execution Times:
              CPU time = 4595 ms, elapsed time = 3209 ms.


              When we add the sweet embrace of OPTION(RECOMPILE)



              SET STATISTICS IO, TIME ON;
              EXEC SP_EXECUTESQL
              N'SELECT * FROM dbo.Bad
              Where CodeMasterID = @P1 OPTION(RECOMPILE)',N'@P1 INT',@P1 = 37155;


              It is all good.



               SQL Server Execution Times:
              CPU time = 16 ms, elapsed time = 98 ms.


              Why



              This all again supports the point of not being able to apply the @P1 predicate to the tables because of the window function & parameterization resulting in the filter operator



              enter image description hereenter image description here



              Not only an issue for temporal tables



              See addendum 2



              Even when not using temporal tables, this happens:
              enter image description here



              The same result is seen when writing the query like this:



              DECLARE @P1 int = 37155
              SELECT * FROM dbo.Bad2
              Where CodeMasterID = @P1;


              Again, the optimizer is not pushing down the predicate before applying the window function.



              When omitting the ROW_NUMBER()



              CREATE VIEW dbo.Bad3
              as
              SELECT
              cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
              FROM dbo.CodeMaster2 cm
              INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID;


              All is well



              SET STATISTICS IO, TIME ON;
              EXEC SP_EXECUTESQL
              N'SELECT * FROM dbo.Bad3
              Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155


              SQL Server Execution Times:
              CPU time = 0 ms, elapsed time = 33 ms.


              so where does all that leave us?



              The ROW_NUMBER() is calculated before the filter is applied on the bad queries.



              And all this leads us to this blogpost from 2013 by Paul White
              on window functions and views.



              One of the important parts for our example is this statement:




              Unfortunately, the SelOnSeqPrj simplification rule only works when the
              predicate performs a comparison with a constant. For that reason, the
              following query produces the sub-optimal plan on SQL Server 2008 and
              later:




              DECLARE @ProductID INT = 878;

              SELECT
              mrt.ProductID,
              mrt.TransactionID,
              mrt.ReferenceOrderID,
              mrt.TransactionDate,
              mrt.Quantity
              FROM dbo.MostRecentTransactionsPerProduct AS mrt
              WHERE
              mrt.ProductID = @ProductID;


              enter image description here



              This part corresponds to what we have seen when declaring the parameter ourselves / using SP_EXECUTESQL on the view.




              The actual solutions



              1: OPTION(RECOMPILE)



              We know that OPTION(RECOMPILE) to 'see' the value at runtime is a possibility. When recompiling the execution plan for each query call is too expensive, there are other solutions.



              2: Inline table valued function with a parameter



              CREATE FUNCTION dbo.BlaBla
              (
              @P1 INT
              )
              RETURNS TABLE
              WITH SCHEMABINDING AS
              RETURN
              (
              SELECT
              ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID) AS Deal_HistoryID,
              cm.CodeMasterID,CM.ManagerID,
              cm.ParentDeptID,d.DealID,
              d.CodeMasterID as dealcodemaster,
              d.EvenMoreBlaID
              FROM dbo.CodeMaster2 cm
              INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID
              Where cm.CodeMasterID = @P1
              )
              EXEC SP_EXECUTESQL
              N'SELECT * FROM dbo.BlaBLa(@P1)',N'@P1 INT',@P1 = 37155


              Resulting in the expected seek predicates



               SQL Server Execution Times:
              CPU time = 0 ms, elapsed time = 0 ms.


              With about 9 logical reads on my test



              3: Writing the query without the use of a view.



              The other 'solution' could be writing the query entirely without the use of a view.



              4: Not keeping the ROW_NUMBER() function in the view, instead specifying it in the call to the view.



              An example of this would be:



              CREATE VIEW dbo.Bad2
              as
              SELECT
              cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
              FROM dbo.CodeMaster2 cm
              INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID;

              GO
              SET STATISTICS IO, TIME ON;
              EXEC SP_EXECUTESQL
              N'SELECT ROW_NUMBER() OVER (PARTITION BY CodeMasterID ORDER BY CodeMasterID) AS Deal_HistoryID,* FROM dbo.Bad2
              Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155;


              There should be other creative ways around this issue, the important part is knowing what causes it.




              Addendum #1



              CREATE TABLE dbo.Codemaster 
              (
              CodeMasterID int NOT NULL PRIMARY KEY CLUSTERED
              , ManagerID INT NULL
              , ParentDeptID int NULL
              , SysStartTime datetime2 GENERATED ALWAYS AS ROW START NOT NULL
              , SysEndTime datetime2 GENERATED ALWAYS AS ROW END NOT NULL
              , PERIOD FOR SYSTEM_TIME (SysStartTime,SysEndTime)
              )
              WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Codemaster_History))
              ;

              CREATE TABLE dbo.Deal
              (
              DealID int NOT NULL PRIMARY KEY CLUSTERED
              , CodeMasterID INT NULL
              , EvenMoreBlaID int NULL
              , SysStartTime datetime2 GENERATED ALWAYS AS ROW START NOT NULL
              , SysEndTime datetime2 GENERATED ALWAYS AS ROW END NOT NULL
              , PERIOD FOR SYSTEM_TIME (SysStartTime,SysEndTime)
              )
              WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Deal_History))
              ;

              INSERT INTO dbo.Codemaster(CodeMasterID,ManagerID,ParentDeptID)
              SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
              ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
              ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
              FROM MASTER..spt_values as spt1
              CROSS JOIN MASTER..spt_values as spt2;


              INSERT INTO dbo.Deal(DealID,CodeMasterID,EvenMoreBlaID)
              SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
              ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
              ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
              FROM MASTER..spt_values as spt1
              CROSS JOIN MASTER..spt_values as spt2;

              CREATE INDEX IX_CodeMasterID
              ON dbo.Deal(CodeMasterId);
              CREATE INDEX IX_CodeMasterID
              ON dbo.Deal_History(CodeMasterId);

              CREATE INDEX IX_CodeMasterID
              ON dbo.Codemaster(CodeMasterId);
              CREATE INDEX IX_CodeMasterID
              ON dbo.Codemaster_History(CodeMasterId);


              SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID, cm.SysStartTime) AS Deal_HistoryID,
              cm.*, d.*
              FROM dbo.CodeMaster FOR SYSTEM_TIME ALL cm
              INNER JOIN dbo.Deal FOR SYSTEM_TIME ALL d ON cm.CodeMasterID = d.CodeMasterID
              Where cm.CodeMasterID = 37155;

              -- Guud
              GO
              CREATE VIEW dbo.Bad
              as
              SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID, cm.SysStartTime) AS Deal_HistoryID,
              cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
              FROM dbo.CodeMaster FOR SYSTEM_TIME ALL cm
              INNER JOIN dbo.Deal FOR SYSTEM_TIME ALL d ON cm.CodeMasterID = d.CodeMasterID

              GO
              EXEC SP_EXECUTESQL
              N'SELECT * FROM dbo.Bad
              Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155

              -- Very bad shame on you



              Addendum #2



              CREATE TABLE dbo.Codemaster2
              (
              CodeMasterID int NOT NULL PRIMARY KEY CLUSTERED
              , ManagerID INT NULL
              , ParentDeptID int NULL

              );

              CREATE TABLE dbo.Deal2
              (
              DealID int NOT NULL PRIMARY KEY CLUSTERED
              , CodeMasterID INT NULL
              , EvenMoreBlaID int NULL
              );

              INSERT INTO dbo.Codemaster2(CodeMasterID,ManagerID,ParentDeptID)
              SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
              ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
              ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
              FROM MASTER..spt_values as spt1
              CROSS JOIN MASTER..spt_values as spt2;


              INSERT INTO dbo.Deal2(DealID,CodeMasterID,EvenMoreBlaID)
              SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
              ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
              ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
              FROM MASTER..spt_values as spt1
              CROSS JOIN MASTER..spt_values as spt2;

              CREATE INDEX IX_CodeMasterID
              ON dbo.Deal2(CodeMasterId);
              CREATE INDEX IX_CodeMasterID
              ON dbo.Codemaster2(CodeMasterId);


              SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterId) AS Deal_HistoryID,
              cm.*, d.*
              FROM dbo.CodeMaster2 cm
              INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID
              Where cm.CodeMasterID = 37155;

              -- Guud
              GO
              CREATE VIEW dbo.Bad2
              as
              SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID) AS Deal_HistoryID,
              cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
              FROM dbo.CodeMaster2 cm
              INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID

              GO
              SET STATISTICS IO, TIME ON;
              EXEC SP_EXECUTESQL
              N'SELECT * FROM dbo.Bad2
              Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155





              share|improve this answer






























                18














                18










                18









                The main performance differences



                The main differences here are that the better performing query is pushing down the seek predicate on CodeMasterID on all 4 tables (2 temporal tables (actual & history)) where the select on the view appears to not do that until the end (filter operator).



                TL DR;



                The issue is due to to parameters not pushing down to window functions in certain cases such as views. The easiest solution is adding OPTION(RECOMPILE) to the view call as to make the optimizer 'see' the params at runtime if that is a possibility. If it is too expensive to recompile the execution plan for each query call, using an inline table valued function that expects a parameter could be a solution. There is an excellent Blogpost by Paul White on this. For a more detailed way on finding and resolving your particular issue, keep on reading.




                The better performing query



                Codemaster table



                enter image description here



                enter image description here



                Deal table



                enter image description here



                enter image description here



                I love the smell of seek predicates in the morning




                The big bad query



                Codemaster table



                enter image description here



                enter image description here



                This is a predicate only zone



                The Deal table



                enter image description here



                But the optimizer did not read 'The art of the deal™"



                enter image description here



                ...and does not learn from the past



                Until all that data reaches the filter operator



                enter image description here




                So, what gives?



                The main problem here is the optimizer not 'seeing' the parameters at runtime due to the window functions in the view and not being able to use the SelOnSeqPrj (select on sequence project, further down in this post for reference).



                I was able to replicate the same results with a test sample and using SP_EXECUTESQL to parameterize the call to the view. See addendum for the DDL / DML



                executing a query against a test view with a window function and an INNER JOIN



                SET STATISTICS IO, TIME ON;
                EXEC SP_EXECUTESQL
                N'SELECT * FROM dbo.Bad
                Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155;


                Resulting in about 4.5s of cpu time and 3.2s elapsed time



                 SQL Server Execution Times:
                CPU time = 4595 ms, elapsed time = 3209 ms.


                When we add the sweet embrace of OPTION(RECOMPILE)



                SET STATISTICS IO, TIME ON;
                EXEC SP_EXECUTESQL
                N'SELECT * FROM dbo.Bad
                Where CodeMasterID = @P1 OPTION(RECOMPILE)',N'@P1 INT',@P1 = 37155;


                It is all good.



                 SQL Server Execution Times:
                CPU time = 16 ms, elapsed time = 98 ms.


                Why



                This all again supports the point of not being able to apply the @P1 predicate to the tables because of the window function & parameterization resulting in the filter operator



                enter image description hereenter image description here



                Not only an issue for temporal tables



                See addendum 2



                Even when not using temporal tables, this happens:
                enter image description here



                The same result is seen when writing the query like this:



                DECLARE @P1 int = 37155
                SELECT * FROM dbo.Bad2
                Where CodeMasterID = @P1;


                Again, the optimizer is not pushing down the predicate before applying the window function.



                When omitting the ROW_NUMBER()



                CREATE VIEW dbo.Bad3
                as
                SELECT
                cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
                FROM dbo.CodeMaster2 cm
                INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID;


                All is well



                SET STATISTICS IO, TIME ON;
                EXEC SP_EXECUTESQL
                N'SELECT * FROM dbo.Bad3
                Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155


                SQL Server Execution Times:
                CPU time = 0 ms, elapsed time = 33 ms.


                so where does all that leave us?



                The ROW_NUMBER() is calculated before the filter is applied on the bad queries.



                And all this leads us to this blogpost from 2013 by Paul White
                on window functions and views.



                One of the important parts for our example is this statement:




                Unfortunately, the SelOnSeqPrj simplification rule only works when the
                predicate performs a comparison with a constant. For that reason, the
                following query produces the sub-optimal plan on SQL Server 2008 and
                later:




                DECLARE @ProductID INT = 878;

                SELECT
                mrt.ProductID,
                mrt.TransactionID,
                mrt.ReferenceOrderID,
                mrt.TransactionDate,
                mrt.Quantity
                FROM dbo.MostRecentTransactionsPerProduct AS mrt
                WHERE
                mrt.ProductID = @ProductID;


                enter image description here



                This part corresponds to what we have seen when declaring the parameter ourselves / using SP_EXECUTESQL on the view.




                The actual solutions



                1: OPTION(RECOMPILE)



                We know that OPTION(RECOMPILE) to 'see' the value at runtime is a possibility. When recompiling the execution plan for each query call is too expensive, there are other solutions.



                2: Inline table valued function with a parameter



                CREATE FUNCTION dbo.BlaBla
                (
                @P1 INT
                )
                RETURNS TABLE
                WITH SCHEMABINDING AS
                RETURN
                (
                SELECT
                ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID) AS Deal_HistoryID,
                cm.CodeMasterID,CM.ManagerID,
                cm.ParentDeptID,d.DealID,
                d.CodeMasterID as dealcodemaster,
                d.EvenMoreBlaID
                FROM dbo.CodeMaster2 cm
                INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID
                Where cm.CodeMasterID = @P1
                )
                EXEC SP_EXECUTESQL
                N'SELECT * FROM dbo.BlaBLa(@P1)',N'@P1 INT',@P1 = 37155


                Resulting in the expected seek predicates



                 SQL Server Execution Times:
                CPU time = 0 ms, elapsed time = 0 ms.


                With about 9 logical reads on my test



                3: Writing the query without the use of a view.



                The other 'solution' could be writing the query entirely without the use of a view.



                4: Not keeping the ROW_NUMBER() function in the view, instead specifying it in the call to the view.



                An example of this would be:



                CREATE VIEW dbo.Bad2
                as
                SELECT
                cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
                FROM dbo.CodeMaster2 cm
                INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID;

                GO
                SET STATISTICS IO, TIME ON;
                EXEC SP_EXECUTESQL
                N'SELECT ROW_NUMBER() OVER (PARTITION BY CodeMasterID ORDER BY CodeMasterID) AS Deal_HistoryID,* FROM dbo.Bad2
                Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155;


                There should be other creative ways around this issue, the important part is knowing what causes it.




                Addendum #1



                CREATE TABLE dbo.Codemaster 
                (
                CodeMasterID int NOT NULL PRIMARY KEY CLUSTERED
                , ManagerID INT NULL
                , ParentDeptID int NULL
                , SysStartTime datetime2 GENERATED ALWAYS AS ROW START NOT NULL
                , SysEndTime datetime2 GENERATED ALWAYS AS ROW END NOT NULL
                , PERIOD FOR SYSTEM_TIME (SysStartTime,SysEndTime)
                )
                WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Codemaster_History))
                ;

                CREATE TABLE dbo.Deal
                (
                DealID int NOT NULL PRIMARY KEY CLUSTERED
                , CodeMasterID INT NULL
                , EvenMoreBlaID int NULL
                , SysStartTime datetime2 GENERATED ALWAYS AS ROW START NOT NULL
                , SysEndTime datetime2 GENERATED ALWAYS AS ROW END NOT NULL
                , PERIOD FOR SYSTEM_TIME (SysStartTime,SysEndTime)
                )
                WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Deal_History))
                ;

                INSERT INTO dbo.Codemaster(CodeMasterID,ManagerID,ParentDeptID)
                SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
                ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
                ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
                FROM MASTER..spt_values as spt1
                CROSS JOIN MASTER..spt_values as spt2;


                INSERT INTO dbo.Deal(DealID,CodeMasterID,EvenMoreBlaID)
                SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
                ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
                ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
                FROM MASTER..spt_values as spt1
                CROSS JOIN MASTER..spt_values as spt2;

                CREATE INDEX IX_CodeMasterID
                ON dbo.Deal(CodeMasterId);
                CREATE INDEX IX_CodeMasterID
                ON dbo.Deal_History(CodeMasterId);

                CREATE INDEX IX_CodeMasterID
                ON dbo.Codemaster(CodeMasterId);
                CREATE INDEX IX_CodeMasterID
                ON dbo.Codemaster_History(CodeMasterId);


                SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID, cm.SysStartTime) AS Deal_HistoryID,
                cm.*, d.*
                FROM dbo.CodeMaster FOR SYSTEM_TIME ALL cm
                INNER JOIN dbo.Deal FOR SYSTEM_TIME ALL d ON cm.CodeMasterID = d.CodeMasterID
                Where cm.CodeMasterID = 37155;

                -- Guud
                GO
                CREATE VIEW dbo.Bad
                as
                SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID, cm.SysStartTime) AS Deal_HistoryID,
                cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
                FROM dbo.CodeMaster FOR SYSTEM_TIME ALL cm
                INNER JOIN dbo.Deal FOR SYSTEM_TIME ALL d ON cm.CodeMasterID = d.CodeMasterID

                GO
                EXEC SP_EXECUTESQL
                N'SELECT * FROM dbo.Bad
                Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155

                -- Very bad shame on you



                Addendum #2



                CREATE TABLE dbo.Codemaster2
                (
                CodeMasterID int NOT NULL PRIMARY KEY CLUSTERED
                , ManagerID INT NULL
                , ParentDeptID int NULL

                );

                CREATE TABLE dbo.Deal2
                (
                DealID int NOT NULL PRIMARY KEY CLUSTERED
                , CodeMasterID INT NULL
                , EvenMoreBlaID int NULL
                );

                INSERT INTO dbo.Codemaster2(CodeMasterID,ManagerID,ParentDeptID)
                SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
                ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
                ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
                FROM MASTER..spt_values as spt1
                CROSS JOIN MASTER..spt_values as spt2;


                INSERT INTO dbo.Deal2(DealID,CodeMasterID,EvenMoreBlaID)
                SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
                ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
                ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
                FROM MASTER..spt_values as spt1
                CROSS JOIN MASTER..spt_values as spt2;

                CREATE INDEX IX_CodeMasterID
                ON dbo.Deal2(CodeMasterId);
                CREATE INDEX IX_CodeMasterID
                ON dbo.Codemaster2(CodeMasterId);


                SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterId) AS Deal_HistoryID,
                cm.*, d.*
                FROM dbo.CodeMaster2 cm
                INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID
                Where cm.CodeMasterID = 37155;

                -- Guud
                GO
                CREATE VIEW dbo.Bad2
                as
                SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID) AS Deal_HistoryID,
                cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
                FROM dbo.CodeMaster2 cm
                INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID

                GO
                SET STATISTICS IO, TIME ON;
                EXEC SP_EXECUTESQL
                N'SELECT * FROM dbo.Bad2
                Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155





                share|improve this answer
















                The main performance differences



                The main differences here are that the better performing query is pushing down the seek predicate on CodeMasterID on all 4 tables (2 temporal tables (actual & history)) where the select on the view appears to not do that until the end (filter operator).



                TL DR;



                The issue is due to to parameters not pushing down to window functions in certain cases such as views. The easiest solution is adding OPTION(RECOMPILE) to the view call as to make the optimizer 'see' the params at runtime if that is a possibility. If it is too expensive to recompile the execution plan for each query call, using an inline table valued function that expects a parameter could be a solution. There is an excellent Blogpost by Paul White on this. For a more detailed way on finding and resolving your particular issue, keep on reading.




                The better performing query



                Codemaster table



                enter image description here



                enter image description here



                Deal table



                enter image description here



                enter image description here



                I love the smell of seek predicates in the morning




                The big bad query



                Codemaster table



                enter image description here



                enter image description here



                This is a predicate only zone



                The Deal table



                enter image description here



                But the optimizer did not read 'The art of the deal™"



                enter image description here



                ...and does not learn from the past



                Until all that data reaches the filter operator



                enter image description here




                So, what gives?



                The main problem here is the optimizer not 'seeing' the parameters at runtime due to the window functions in the view and not being able to use the SelOnSeqPrj (select on sequence project, further down in this post for reference).



                I was able to replicate the same results with a test sample and using SP_EXECUTESQL to parameterize the call to the view. See addendum for the DDL / DML



                executing a query against a test view with a window function and an INNER JOIN



                SET STATISTICS IO, TIME ON;
                EXEC SP_EXECUTESQL
                N'SELECT * FROM dbo.Bad
                Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155;


                Resulting in about 4.5s of cpu time and 3.2s elapsed time



                 SQL Server Execution Times:
                CPU time = 4595 ms, elapsed time = 3209 ms.


                When we add the sweet embrace of OPTION(RECOMPILE)



                SET STATISTICS IO, TIME ON;
                EXEC SP_EXECUTESQL
                N'SELECT * FROM dbo.Bad
                Where CodeMasterID = @P1 OPTION(RECOMPILE)',N'@P1 INT',@P1 = 37155;


                It is all good.



                 SQL Server Execution Times:
                CPU time = 16 ms, elapsed time = 98 ms.


                Why



                This all again supports the point of not being able to apply the @P1 predicate to the tables because of the window function & parameterization resulting in the filter operator



                enter image description hereenter image description here



                Not only an issue for temporal tables



                See addendum 2



                Even when not using temporal tables, this happens:
                enter image description here



                The same result is seen when writing the query like this:



                DECLARE @P1 int = 37155
                SELECT * FROM dbo.Bad2
                Where CodeMasterID = @P1;


                Again, the optimizer is not pushing down the predicate before applying the window function.



                When omitting the ROW_NUMBER()



                CREATE VIEW dbo.Bad3
                as
                SELECT
                cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
                FROM dbo.CodeMaster2 cm
                INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID;


                All is well



                SET STATISTICS IO, TIME ON;
                EXEC SP_EXECUTESQL
                N'SELECT * FROM dbo.Bad3
                Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155


                SQL Server Execution Times:
                CPU time = 0 ms, elapsed time = 33 ms.


                so where does all that leave us?



                The ROW_NUMBER() is calculated before the filter is applied on the bad queries.



                And all this leads us to this blogpost from 2013 by Paul White
                on window functions and views.



                One of the important parts for our example is this statement:




                Unfortunately, the SelOnSeqPrj simplification rule only works when the
                predicate performs a comparison with a constant. For that reason, the
                following query produces the sub-optimal plan on SQL Server 2008 and
                later:




                DECLARE @ProductID INT = 878;

                SELECT
                mrt.ProductID,
                mrt.TransactionID,
                mrt.ReferenceOrderID,
                mrt.TransactionDate,
                mrt.Quantity
                FROM dbo.MostRecentTransactionsPerProduct AS mrt
                WHERE
                mrt.ProductID = @ProductID;


                enter image description here



                This part corresponds to what we have seen when declaring the parameter ourselves / using SP_EXECUTESQL on the view.




                The actual solutions



                1: OPTION(RECOMPILE)



                We know that OPTION(RECOMPILE) to 'see' the value at runtime is a possibility. When recompiling the execution plan for each query call is too expensive, there are other solutions.



                2: Inline table valued function with a parameter



                CREATE FUNCTION dbo.BlaBla
                (
                @P1 INT
                )
                RETURNS TABLE
                WITH SCHEMABINDING AS
                RETURN
                (
                SELECT
                ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID) AS Deal_HistoryID,
                cm.CodeMasterID,CM.ManagerID,
                cm.ParentDeptID,d.DealID,
                d.CodeMasterID as dealcodemaster,
                d.EvenMoreBlaID
                FROM dbo.CodeMaster2 cm
                INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID
                Where cm.CodeMasterID = @P1
                )
                EXEC SP_EXECUTESQL
                N'SELECT * FROM dbo.BlaBLa(@P1)',N'@P1 INT',@P1 = 37155


                Resulting in the expected seek predicates



                 SQL Server Execution Times:
                CPU time = 0 ms, elapsed time = 0 ms.


                With about 9 logical reads on my test



                3: Writing the query without the use of a view.



                The other 'solution' could be writing the query entirely without the use of a view.



                4: Not keeping the ROW_NUMBER() function in the view, instead specifying it in the call to the view.



                An example of this would be:



                CREATE VIEW dbo.Bad2
                as
                SELECT
                cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
                FROM dbo.CodeMaster2 cm
                INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID;

                GO
                SET STATISTICS IO, TIME ON;
                EXEC SP_EXECUTESQL
                N'SELECT ROW_NUMBER() OVER (PARTITION BY CodeMasterID ORDER BY CodeMasterID) AS Deal_HistoryID,* FROM dbo.Bad2
                Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155;


                There should be other creative ways around this issue, the important part is knowing what causes it.




                Addendum #1



                CREATE TABLE dbo.Codemaster 
                (
                CodeMasterID int NOT NULL PRIMARY KEY CLUSTERED
                , ManagerID INT NULL
                , ParentDeptID int NULL
                , SysStartTime datetime2 GENERATED ALWAYS AS ROW START NOT NULL
                , SysEndTime datetime2 GENERATED ALWAYS AS ROW END NOT NULL
                , PERIOD FOR SYSTEM_TIME (SysStartTime,SysEndTime)
                )
                WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Codemaster_History))
                ;

                CREATE TABLE dbo.Deal
                (
                DealID int NOT NULL PRIMARY KEY CLUSTERED
                , CodeMasterID INT NULL
                , EvenMoreBlaID int NULL
                , SysStartTime datetime2 GENERATED ALWAYS AS ROW START NOT NULL
                , SysEndTime datetime2 GENERATED ALWAYS AS ROW END NOT NULL
                , PERIOD FOR SYSTEM_TIME (SysStartTime,SysEndTime)
                )
                WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Deal_History))
                ;

                INSERT INTO dbo.Codemaster(CodeMasterID,ManagerID,ParentDeptID)
                SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
                ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
                ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
                FROM MASTER..spt_values as spt1
                CROSS JOIN MASTER..spt_values as spt2;


                INSERT INTO dbo.Deal(DealID,CodeMasterID,EvenMoreBlaID)
                SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
                ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
                ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
                FROM MASTER..spt_values as spt1
                CROSS JOIN MASTER..spt_values as spt2;

                CREATE INDEX IX_CodeMasterID
                ON dbo.Deal(CodeMasterId);
                CREATE INDEX IX_CodeMasterID
                ON dbo.Deal_History(CodeMasterId);

                CREATE INDEX IX_CodeMasterID
                ON dbo.Codemaster(CodeMasterId);
                CREATE INDEX IX_CodeMasterID
                ON dbo.Codemaster_History(CodeMasterId);


                SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID, cm.SysStartTime) AS Deal_HistoryID,
                cm.*, d.*
                FROM dbo.CodeMaster FOR SYSTEM_TIME ALL cm
                INNER JOIN dbo.Deal FOR SYSTEM_TIME ALL d ON cm.CodeMasterID = d.CodeMasterID
                Where cm.CodeMasterID = 37155;

                -- Guud
                GO
                CREATE VIEW dbo.Bad
                as
                SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID, cm.SysStartTime) AS Deal_HistoryID,
                cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
                FROM dbo.CodeMaster FOR SYSTEM_TIME ALL cm
                INNER JOIN dbo.Deal FOR SYSTEM_TIME ALL d ON cm.CodeMasterID = d.CodeMasterID

                GO
                EXEC SP_EXECUTESQL
                N'SELECT * FROM dbo.Bad
                Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155

                -- Very bad shame on you



                Addendum #2



                CREATE TABLE dbo.Codemaster2
                (
                CodeMasterID int NOT NULL PRIMARY KEY CLUSTERED
                , ManagerID INT NULL
                , ParentDeptID int NULL

                );

                CREATE TABLE dbo.Deal2
                (
                DealID int NOT NULL PRIMARY KEY CLUSTERED
                , CodeMasterID INT NULL
                , EvenMoreBlaID int NULL
                );

                INSERT INTO dbo.Codemaster2(CodeMasterID,ManagerID,ParentDeptID)
                SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
                ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
                ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
                FROM MASTER..spt_values as spt1
                CROSS JOIN MASTER..spt_values as spt2;


                INSERT INTO dbo.Deal2(DealID,CodeMasterID,EvenMoreBlaID)
                SELECT TOP(1000000) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum1,
                ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum2,
                ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) as rownum3
                FROM MASTER..spt_values as spt1
                CROSS JOIN MASTER..spt_values as spt2;

                CREATE INDEX IX_CodeMasterID
                ON dbo.Deal2(CodeMasterId);
                CREATE INDEX IX_CodeMasterID
                ON dbo.Codemaster2(CodeMasterId);


                SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterId) AS Deal_HistoryID,
                cm.*, d.*
                FROM dbo.CodeMaster2 cm
                INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID
                Where cm.CodeMasterID = 37155;

                -- Guud
                GO
                CREATE VIEW dbo.Bad2
                as
                SELECT ROW_NUMBER() OVER (PARTITION BY cm.CodeMasterID ORDER BY cm.CodeMasterID) AS Deal_HistoryID,
                cm.CodeMasterID,CM.ManagerID,cm.ParentDeptID,d.DealID, d.CodeMasterID as dealcodemaster,d.EvenMoreBlaID
                FROM dbo.CodeMaster2 cm
                INNER JOIN dbo.Deal2 d ON cm.CodeMasterID = d.CodeMasterID

                GO
                SET STATISTICS IO, TIME ON;
                EXEC SP_EXECUTESQL
                N'SELECT * FROM dbo.Bad2
                Where CodeMasterID = @P1',N'@P1 INT',@P1 = 37155






                share|improve this answer















                share|improve this answer




                share|improve this answer








                edited Jun 4 at 7:27

























                answered May 29 at 21:58









                Randi VertongenRandi Vertongen

                10.4k3 gold badges13 silver badges35 bronze badges




                10.4k3 gold badges13 silver badges35 bronze badges































                    draft saved

                    draft discarded















































                    Thanks for contributing an answer to Database Administrators Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid


                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.

                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function ()
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fdba.stackexchange.com%2fquestions%2f239370%2fselect-from-view-takes-4-minutes%23new-answer', 'question_page');

                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown









                    Popular posts from this blog

                    Tamil (spriik) Luke uk diar | Nawigatjuun

                    Align equal signs while including text over equalitiesAMS align: left aligned text/math plus multicolumn alignmentMultiple alignmentsAligning equations in multiple placesNumbering and aligning an equation with multiple columnsHow to align one equation with another multline equationUsing \ in environments inside the begintabularxNumber equations and preserving alignment of equal signsHow can I align equations to the left and to the right?Double equation alignment problem within align enviromentAligned within align: Why are they right-aligned?

                    Where does the image of a data connector as a sharp metal spike originate from?Where does the concept of infected people turning into zombies only after death originate from?Where does the motif of a reanimated human head originate?Where did the notion that Dragons could speak originate?Where does the archetypal image of the 'Grey' alien come from?Where did the suffix '-Man' originate?Where does the notion of being injured or killed by an illusion originate?Where did the term “sophont” originate?Where does the trope of magic spells being driven by advanced technology originate from?Where did the term “the living impaired” originate?