如何在带有 LINQ to SQL 的存储过程中使用临时表

Posted

技术标签:

【中文标题】如何在带有 LINQ to SQL 的存储过程中使用临时表【英文标题】:How to use temp table in Stored Procedure with LINQ to SQL 【发布时间】:2011-07-26 05:08:51 【问题描述】:

我在带有 LINQ to SQL 的存储过程中使用临时表。我将存储过程添加到 Linq to SQL dbml 文件然后项目出现错误消息

“未知返回类型 - 无法检测到以下存储过程的返回类型。”

当我在存储过程中删除临时表时,返回值很好。

如何在 Linq to SQL 的存储过程中使用临时表

我像这样替换 temptable

  CREATE TABLE tempTable(
PartsReceivingID INT, 
SoPartID INT,
RecvQty INT,
ReturnQty INT
)

如下替换

  SELECT @RowCount = count(*)           
  FROM Parts.studentTempTable          
   IF @RowCount > 0         
           BEGIN             
             TRUNCATE TABLE Parts.studentTempTable;
           END  

工作版存储过程

ALTER  PROCEDURE [dbo].[stp_student_Select_New] 
                @pSchID as int, 
                @pCompanyID as int,
                @pAgingDate as int,
                @pTicketNo as VARCHAR(50),
                @pInvoiceNo as VARCHAR(50),
                @pDeliveryNo as VARCHAR(50),
                @pPartNo as VARCHAR(50)
As
SET NOCOUNT ON
BEGIN
          SELECT @RowCount = count(*)
          FROM Parts.studentTempTable

        IF @RowCount > 0
        BEGIN
            TRUNCATE TABLE Parts.studentTempTable;
        END


    ===============================================
    do something with studentTempTable
    ===============================================

        SELECT 
               r.Ticketid AS TicketID,
               r.SoPartNo  AS PartNo ,
               p.Description,
               r.InvoiceNo as InvoiceNo,
               r.InvoiceDate AS InvoiceDate,
               DATEDIFF(DY,r.InvoiceDate,GETDATE())as Aging,
               r.Qty AS CurrentInventory,              
               t.ReturnQty AS ReturnQty
        FROM Parts.studentTempTable AS t,
             Parts.PartsReceiving AS r,
             Parts.PartsInfo as p
        WHERE t.PartsReceivingID = r.PartsReceivingID
          --and i.TicketID = r.TicketID 
          and p.PartID = r.SoPartID  
          and t.ReturnQty >0 
          and DATEDIFF(DY,r.InvoiceDate,GETDATE()) > @pAgingDate
          and r.SchID = @pSchID 
          and r.CompanyID = @pCompanyID
          and r.SoPartNo like  '%%' + @pTicketNo + '%' 
          and r.InvoiceNo like  '%%' + @pInvoiceNo + '%' 
          and r.SoPartNo like  '%%' + @pPartNo + '%' 
          --and i.TicketNo like  '%%' + @pTicketNo + '%' 
          --and r.DeliverNo like  '%%' + @pDeliveryNo + '%' 
Return
END

无工作版本存储过程

ALTER PROCEDURE [dbo].[stp_student_Select] 
                @pVendorID as int, 
                @pCompanyID as int,
                @pAgingDate as int,
                @pTicketNo as VARCHAR(50),
                @pInvoiceNo as VARCHAR(50),
                @pDeliveryNo as VARCHAR(50),
                @pPartNo as VARCHAR(50)

As
SET NOCOUNT ON
BEGIN
    BEGIN TRY


            CREATE TABLE tempTable(
                    PartsReceivingID INT, 
                    SoPartID INT,
                    RecvQty INT,
                    ReturnQty INT
                    )
    ===============================================
    do something with tempTable
    ===============================================
        SELECT 
               isnull(r.Ticketid,0) AS TicketID,
               --i.TicketNo,
               r.SoPartNo  AS PartNo ,
               p.Description,
               r.InvoiceNo as InvoiceNo,
               --r.DeliveryNo,
               r.InvoiceDate AS InvoiceDate,
               DATEDIFF(DY,r.InvoiceDate,GETDATE())as Aging,
               r.Qty AS CurrentInventory,              
               t.ReturnQty AS ReturnQty

        FROM tempTable AS t,
             Parts.PartsReceiving AS r,
             --Ticket.TicketInfo as i,
             Parts.PartsInfo as p

        WHERE t.PartsReceivingID = r.PartsReceivingID
          --and i.TicketID = r.TicketID 
          and p.PartID = r.SoPartID  
          and t.ReturnQty >0 
          and DATEDIFF(DY,r.InvoiceDate,GETDATE()) > @pAgingDate
          and r.VendorID = @pVendorID 
          and r.CompanyID = @pCompanyID
          and r.SoPartNo like  '%%' + @pTicketNo + '%' 
          and r.InvoiceNo like  '%%' + @pInvoiceNo + '%' 
          and r.SoPartNo like  '%%' + @pPartNo + '%' 
          --and i.TicketNo like  '%%' + @pTicketNo + '%' 
          --and r.DeliverNo like  '%%' + @pDeliveryNo + '%' 


        DROP TABLE temptable
    END TRY

    BEGIN CATCH
        SELECT ERROR_MESSAGE() as ErrorMessge,
               ERROR_NUMBER() AS ErrorNumber
    END CATCH
Return
END

【问题讨论】:

你能发布你的存储过程的工作和非工作版本吗? 感谢您的评论我附上了存储的过程 【参考方案1】:

在您的 SP 开头添加以下脚本。

IF 1=0 BEGIN
    SET FMTONLY OFF
END

FMTONLY 仅将元数据返回到数据集。可用于测试响应的格式,而无需实际运行查询。

上面的查询将给出没有数据的列输出。

来源:https://www.youtube.com/watch?v=zaL7fbUou7E

【讨论】:

【参考方案2】:

虽然声明一个表变量将满足 LINQ to SQL 的要求,但由于缺少统计信息,我曾有过一些表变量的性能非常糟糕的情况。

在那些时候,我不得不重新创建一个简单的包装器或填充存储过程来调用真正的存储过程。包装存储过程使其工作的一个要求是声明一个与实际存储过程的输出匹配的表变量并执行 INSERT...EXEC

Declare @Temp table (ColumnA int, ColumnB varchar(256))
Insert Into @Temp(ColumnA, ColumnB)
Exec dbo.OtherStoredProcedure /* pass needed parameters, if any */

Select ColumnA, ColumnB From @Temp

当然,临时表的定义必须与存储过程的输出完全匹配。你甚至不能删除列。

LINQ to SQL 此时不会评估“子”存储过程,您可以通过 LINQ to SQL 调用包装存储过程。

【讨论】:

【参考方案3】:

将sp中使用的临时表作为表创建到原始db中,然后在dbml文件中删除sp之后在sp中使用这些表,它将返回sp的返回类型。将 sp 在 dbml 中删除后,将原始 sp 更改为原来的 sp 并删除临时表形式的原始 db

【讨论】:

【参考方案4】:

如果 tempTable 真的是#tempTable,那么要为存储过程的结果集自动生成类,你必须在存储过程定义的开头写下这个

IF(1=2)
BEGIN
SELECT
  CAST(NULL AS BIGINT)  AS TicketID --assuming TicketId is of bigint type 
  CAST(NULL AS NVARCHAR(16) AS PartNo --assuming PartNo is of Nvarchar(16)
  .......

  END

【讨论】:

【参考方案5】:

如果您自己运行该过程(在 SSMS 或 Visual Studio 中)它会返回结果吗?无论答案如何,我都建议您使用表变量-您当前使用的不是临时表-它只是一个表。使用表变量将排除实际创建/删除表的任何问题。谷歌搜索会找到大量信息,但这似乎信息量很大:http://odetocode.com/code/365.aspx

【讨论】:

当然可以。顺便说一句,真正的临时表是在 tempdb 数据库中创建的,并以 #mytemptable 之类的前一两个井号符号命名。它们通常是通过从一个或多个现有表中选择来创建的,例如:select * into #mytemptable from mytable 谢谢。我在我的 LINQ 实现中使用了临时表,这很令人费解,为什么无法确定 sproc 的 heck 数据类型。表变量工作正常。 所以我不会否决这个答案,我已经开始看到很多人建议在搜索另一个答案时使用表变量而不是临时表,但这会对性能产生一些非常实际的影响,不应该被认为是所有场景的解决方案,特别是如果表变量将包含大量数据。 @Matt 你能在这里扩展你的担忧吗?表变量的建议主要是由 OP 在创建表时的错误引起的(他似乎想要一些临时的东西)。我的研究表明,在看到任何真正的性能差异之前,您需要一个相当大的数据集(大到足以从索引中受益)。基于这个问题,似乎这种担忧可能......超出了范围。如果您认为应该给出有关性能的警告,请随时在答案中添加详细信息。 @jlnorsworthy 这是一个很好的讨论。 ***.com/questions/27894/… 对于小数据,您可能不会注意到太多,但是 SQL 优化器(检查您的执行计划)将在许多情况下通过使用临时表而不是表变量而受益,并且表变量没有基于它们构建的统计信息所以即使在小数据中如果加入一个复杂的查询,你可以看到使用临时表的好处。

以上是关于如何在带有 LINQ to SQL 的存储过程中使用临时表的主要内容,如果未能解决你的问题,请参考以下文章

如何使用存储过程在 LINQ to SQL 中使用事务?

LINQ to SQL语句(20)之存储过程

Linq to sql-存储过程

Linq to SQL - 视图与存储过程

通过 LINQ-to-SQl 加载数据集

Linq to Sql vs Nhibernate vs SubSonic vs 存储过程(帮助)