2010-01-17 62 views
3

如何将此查询转换为内联sql或存储过程?如何将此linq代码转换为内联sql

var a = from arow in context.post 
where arow.post_id == id && arow.post_isdeleted == false 
select new 
{ 
    arow.post_id, 
    PostComments = from c in context.comment 
        where c.CommentPostID == arow.post_id 
        select new 
        { 
         c.id, 
         c.title  
        } 
} 


List<PostType> pt; 
foreach (var s in a) 
{ 
    pt = new PostType(); 
    pt.PostID = s.post_id; 

    //how would I use ADO.NET to put this in a custom class? 
    foreach(var ctl in s.PostComments) 
    { 
     ctl.Title = ctl.title; 
     pt.CommentT.Add(ctl); 
    } 
    ptl.Add(pt); 
} 

一旦执行了内联查询,我会如何将信息放入自定义类中? PostComments是一个子查询 - 那么如何使用ADO.NET将其放入自定义类中?

+0

你正在使用什么SQL?如果你使用t-sql,答案将不会像你使用plsql一样。 – ALOToverflow 2010-01-20 17:31:07

+0

我正在使用sql server – Luke101 2010-01-20 17:48:17

回答

1

简短说明

加载它在列表看来部分你的问题可能看起来比较棘手,那就是如何以与LINQ to SQL(从此处出来的“L2S”)查询为匿名类一样的方式填充自定义类。

根据您foreach循环我猜你的自定义类是类似于这些:

public class PostType 
{ 
    public int PostId { get; set; } 
    public List<PostComment> PostComments { get; set; } 
} 
public class PostComment 
{ 
    public int CommentId { get; set; } 
    public string Title { get; set; } 
} 

LINQ查询应相当于该T-SQL语句:

SELECT P.post_id, C.id, C.title 
FROM post As P, comment As C 
WHERE 
    P.post_id = @PostId 
    AND P.post_isdeleted = 0 -- 0 is false 
    AND C.CommentPostID = P.post_id 

不像L2S版本(请参阅下面的详细解释部分以获取更多信息),则此语句将返回一个展平结果,每行包含一个P.post_id,C.idC.title。如果你的类以相同的方式代表一个入口,这将很容易解决(我不主张这样的设计;我只是在评论设计如何改变它如何填充)。类中的层次关系改变了事物。

此外,您的代码显示List<PostType>,但不需要列表,因为总是会有一个PostType,因为您在post_id上进行筛选。如果该条件被删除,则,然后您可能会在满足其他条件的情况下获得与具有不同PostIds的多个匹配项。如果是这种情况,下面的代码将需要更改。

也就是说,让我们跳入一些ADO.NET并使用SqlDataReader填充类。

int postIdInput = 42; // desired post_id to search for 

// PostType delcared prior to getting the results 
PostType postType = new PostType() 
{ 
    PostId = postIdInput, 
    PostComments = new List<PostComment>() 
}; 

// Database interaction starts here... 
// updated SQL statement to use column name aliases for clarity when used by the SqlDataReader 
string sqlStatement = @"SELECT P.post_id As PostId, C.id As CommentId, C.title As Title 
         FROM post As P, comment As C 
         WHERE 
          P.post_id = @PostId 
          AND P.post_isdeleted = 0 -- 0 is false 
          AND C.CommentPostID = P.post_id"; 

string sqlConnectionString = "..."; // whatever your connection is... probably identical to your L2S context.Connection.ConnectionString 
using (SqlConnection conn = new SqlConnection(sqlConnectionString)) 
{ 
    conn.Open(); 
    SqlCommand command = new SqlCommand(sqlStatement, conn); 
    command.Parameters.AddWithValue("@PostId", postIdInput); // use Parameters.Add() for greater specificity 

    SqlDataReader reader = command.ExecuteReader(); 
    while (reader.Read()) 
    { 
     // postId was set based on input, but could be set here as well although it would occur repeatedly 
     // if desired, uncomment the 2 lines below and there's no need to initialize it earlier (it'll be overwritten anyway) 
     //int postId = Int32.Parse(reader["PostId"].ToString()); 
     //postType.PostId = postId; 
     int commentId = Int32.Parse(reader["CommentId"].ToString()); 
     string title = reader["Title"].ToString(); 

     // add new PostComment to the list 
     PostComment postComment = new PostComment 
     { 
      CommentId = commentId, 
      Title = title 
     }; 
     postType.PostComments.Add(postComment); 
    } 

    // done! postType is populated... 
} 

// use postType... 

这应该涵盖您的方案。但是,要获得更详细的答案,请继续阅读!


详解(又名“授人以渔。”

比方说,你无法弄清楚如何获得等价的SQL语句。尽管有不同的方式可以这样做,但我会专注于您正在使用L2S并探索一些相关选项的事实。

第1步:转换LINQ查询到SQL (通过 “欺骗”)

你很幸运,因为有一个快捷方式。将现有的LINQ表达式转换为SQL比向后翻译SQL和LINQ更方便。

您可以通过使用这些DataContext的选项从您的代码转换的T-SQL语句:

注:我没有说这是一条捷径。 SQL的知识很好理解,并且要清楚我并不是建议盲目使用生成的输出。当然,SQL可能与您期望的有所不同,但它提供了一个体面的起点。如果需要,你可以调整它。

使用这些方法中的任何一种并复制结果 - 您将需要它的步骤2

实施例DataContext.GetCommand()的用法:

var query = /* your L2S query here */; 
string sqlStatement = context.GetCommand(query).CommandText; // voila! 

要获得的结果要么设置断点和复制其值,检查出来在立即窗口,或某处显示它(Console.WriteLine等等。)。

例DataContext.Log用法:在这种情况下执行将转储到控制台窗口的SQL语句

context.Log = Console.Out; 

查询。你可以从那里复制它。在其他地方把它们扔掉,比如到调试输出窗口,请查看以下链接:

第2步:在手的SQL语句,在ADO.NET中使用它

现在您已经拥有了SQL语句,我们可以在ADO.NET中使用它。当然你也可以使用存储过程,并且不应该很难替代它。

在使用它之前,您可能需要清理声明。我用了一个类似的查询本地得到这个和你生成的语句可能类似于此:

SELECT [t0].[post_id], [t1].[id], [t1].[title], (
SELECT COUNT(*) 
FROM [comment] AS [t2] 
WHERE [t2].[id] = [t0].[post_id] 
) As [value] 
FROM [post] As [t0] 
LEFT OUTER JOIN [comment] As [t1] ON [t1].[CommentPostID] = [t0].[post_id] 
WHERE ([t0].[post_id] = @p0) AND ([t0].[post_isdeleted] = 0) 
ORDER BY [t0].[post_id], [t1].[id] 

通知嵌入式SELECT COUNT(*)? L2S查询从未请求计数,但结果请求连接上使用的相同ID的计数。另请注意,这些列没有别名。你会根据它们的实际名称来参考这些列(即post_idPostId)。另外,SQL参数被命名为@ p0 ... @ pn,并且应用默认排序顺序。您可以将其复制/粘贴到之前使用的SqlDataReader中,但您需要重命名要匹配的列和参数。

一个清理的上述版本与注释掉重命名的参数和不必要的部分转载如下(如果这种方法是采取测试,以确保它相当于预计的):

SELECT [P].[post_id] As PostId, [C].[id] As CommentId, [C].[title] As Title--, (
-- SELECT COUNT(*) 
-- FROM [comment] AS [t2] 
-- WHERE [t2].[id] = [t0].[post_id] 
--) As [value] 
FROM [post] As [P] 
LEFT OUTER JOIN [comment] As [C] ON [C].[CommentPostID] = [P].[post_id] 
WHERE ([P].[post_id] = @PostId) AND ([P].[post_isdeleted] = 0) 
--ORDER BY [t0].[post_id], [t1].[id] 

以上现在可以与之前的SqlDataReader一起使用。

如果L2S查询是在一个SelectMany的格式,如可能已经产生更直接的查询:

var query = from arow in context.post 
      from c in context.comment 
      where arow.post_id == id && arow.post_isdeleted == false 
        && c.CommentPostID == arow.post_id 
      select new 
      { 
       arow.post_id, 
       c.id, 
       c.title  
      }; 

的L2S的SelectMany查询生成类似下面的SQL语句:

SELECT [t0].[post_id], [t1].[id], [t1].[title] 
FROM [post] As [t0], [comment] As [t1] 
WHERE ([t0].[post_id] = @p0) AND ([t0].[post_isdeleted] = 0) 
     AND ([t1].[CommentPostID] = [t0].[post_id]) 

LINQPad

虽然这个详细的解释看起来就非常困难,有一个轻松掌握这些信息。如果你还没有给LINQPad一个尝试,那么我强烈推荐它 - 它也是免费的! LINQPad将向您显示您的L2S查询结果,具有查看生成的SQL的SQL选项卡,并显示使用的lambda表达式(上述查询语法显示为等效的lambda/extension)。最重要的是,它是用于C#/ VB.NET(包括LINQ to Objects/XML)的通用工具,以及具有数据库支持的SQL编码等等。

这里是LINQPad的一个微小的屏幕截图显示了一些前面讨论的主题:

LINQPad

我不想占用更多的页面空间比我已经有这么click here to see the image in its original size


如果您到此为止,恭喜!:)

+0

这只是真实的..不知道多少要谢谢你。我希望我可以多次投票..谢谢你在这样详细的解释..我已经下载linqpad,它帮助巨大。我希望我能聘请你为我的项目。再次感谢 – Luke101 2010-01-21 08:12:07

+0

@ Luke101很高兴帮助和感谢:) – 2010-01-21 14:42:20

1

如果你的意思是有邮电PostComments表之间的关系,并有两个表中重复列和一个注释可能与一个以上的职位,以便 您可以轻松创建两个命令:

-Select * from Posts where post_Id = id AND IsDeleted = 0; 
-Select * from Postcomments where id = cid; 

,然后在两个数据表上使用Sql命令适配器执行它们。然后:

foreach(DataRow dr in PostsTable.Rows) 
{ 
//Fill the Post Custom class 
SecondTable.DefaultView.RowFilter = string.Format("PostID = {0}",dr["postID"]); 
foreach(DataRow r in SecondTable.Rows) 
{ 
    //Fill the Comments Custom class 
} 
} 

如果这不是你的情况,所以你可以尝试澄清你的数据库结构?

+0

对不起。我编辑了查询。是的,PostComments与每篇文章(Post)相关。类似于对SO的评论或相关答案。 – Luke101 2010-01-17 07:46:17

+0

如果你只获得一篇文章及其所有评论,所以你只能创建一个命令并且同时执行两条sql语句,而使用sql数据读取器而不是方法ReadNextResult()来填充注释。 – 2010-01-17 09:43:44

0

我不能沿着线测试这一点,但东西:

SELECT 
    p.post_id 
    c.id, 
    c.title 
FROM 
    post p 
WHERE 
    p.id == 'id' and 
    isdeleted = false 
INNER JOIN comment c ON c.commentpostid = p.post_id 

我利用了可读性关键字,但对于DBS你使用,你可能需要改变这种状况。

1

使用SQL事件探查器来捕获生成的查询。复制到新的存储过程并修复输入参数。创建(保存),并用它:)

0
select post_id, id, title from postcomments pc 
where post_id = @id and exists(
    select post_id form post p where p.post_id = pc.post_id and isdeleted = false 
) 

使用DataReader获取数据&只是你的自定义类